Fix building with --disable-x11 --disable-lua --disable-ncurses
[monky] / src / exec.c
1 /* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
2  * vim: ts=4 sw=4 noet ai cindent syntax=c
3  *
4  * Conky, a system monitor, based on torsmo
5  *
6  * Any original torsmo code is licensed under the BSD license
7  *
8  * All code written since the fork of torsmo is licensed under the GPL
9  *
10  * Please see COPYING for details
11  *
12  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13  * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
14  *      (see AUTHORS)
15  * All rights reserved.
16  *
17  * This program is free software: you can redistribute it and/or modify
18  * it under the terms of the GNU General Public License as published by
19  * the Free Software Foundation, either version 3 of the License, or
20  * (at your option) any later version.
21  *
22  * This program is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  * You should have received a copy of the GNU General Public License
27  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
28  *
29  */
30
31 #include "conky.h"
32 #include "core.h"
33 #include "logging.h"
34 #include "specials.h"
35 #include "text_object.h"
36 #include "timed_thread.h"
37 #include <stdio.h>
38 #include <sys/types.h>
39 #include <sys/wait.h>
40 #include <unistd.h>
41
42 struct execi_data {
43         double last_update;
44         float interval;
45         char *cmd;
46         char *buffer;
47         double data;
48         timed_thread *p_timed_thread;
49         float barnum;
50 };
51
52 /* FIXME: this will probably not work, since the variable is being reused
53  * between different text objects. So when a process really hangs, it's PID
54  * will be overwritten at the next iteration. */
55 pid_t childpid = 0;
56
57 //our own implementation of popen, the difference : the value of 'childpid' will be filled with
58 //the pid of the running 'command'. This is useful if want to kill it when it hangs while reading
59 //or writing to it. We have to kill it because pclose will wait until the process dies by itself
60 static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
61         int ends[2];
62         int parentend, childend;
63
64         //by running pipe after the strcmp's we make sure that we don't have to create a pipe
65         //and close the ends if mode is something illegal
66         if(strcmp(mode, "r") == 0) {
67                 if(pipe(ends) != 0) {
68                         return NULL;
69                 }
70                 parentend = ends[0];
71                 childend = ends[1];
72         } else if(strcmp(mode, "w") == 0) {
73                 if(pipe(ends) != 0) {
74                         return NULL;
75                 }
76                 parentend = ends[1];
77                 childend = ends[0];
78         } else {
79                 return NULL;
80         }
81         *child = fork();
82         if(*child == -1) {
83                 close(parentend);
84                 close(childend);
85                 return NULL;
86         } else if(*child > 0) {
87                 close(childend);
88                 waitpid(*child, NULL, 0);
89         } else {
90                 //don't read from both stdin and pipe or write to both stdout and pipe
91                 if(childend == ends[0]) {
92                         close(0);
93                 } else {
94                         close(1);
95                 }
96                 dup(childend);  //by dupping childend, the returned fd will have close-on-exec turned off
97                 execl("/bin/sh", "sh", "-c", command, (char *) NULL);
98                 _exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
99         }
100         return fdopen(parentend, mode);
101 }
102
103 //remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
104 //string has to end with \0 and it's length should fit in a int
105 #define BACKSPACE 8
106 static void remove_deleted_chars(char *string){
107         int i = 0;
108         while(string[i] != 0){
109                 if(string[i] == BACKSPACE){
110                         if(i != 0){
111                                 strcpy( &(string[i-1]), &(string[i+1]) );
112                                 i--;
113                         }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
114                 }else i++;
115         }
116 }
117
118 static inline double get_barnum(char *buf)
119 {
120         char *c = buf;
121         double barnum;
122
123         while (*c) {
124                 if (*c == '\001') {
125                         *c = ' ';
126                 }
127                 c++;
128         }
129
130         if (sscanf(buf, "%lf", &barnum) == 0) {
131                 NORM_ERR("reading exec value failed (perhaps it's not the "
132                                 "correct format?)");
133                 return -1;
134         }
135         if (barnum > 100.0 || barnum < 0.0) {
136                 NORM_ERR("your exec value is not between 0 and 100, "
137                                 "therefore it will be ignored");
138                 return -1;
139         }
140         return barnum;
141 }
142
143 static inline void read_exec(const char *data, char *buf, const int size)
144 {
145         FILE *fp;
146
147         memset(buf, 0, size);
148
149         if (!data)
150                 return;
151
152         alarm(update_interval);
153         fp = pid_popen(data, "r", &childpid);
154         if(fp) {
155                 int length;
156
157                 length = fread(buf, 1, size, fp);
158                 pclose(fp);
159                 buf[length] = '\0';
160                 if (length > 0 && buf[length - 1] == '\n') {
161                         buf[length - 1] = '\0';
162                 }
163         } else {
164                 buf[0] = '\0';
165         }
166         alarm(0);
167 }
168
169 static void *threaded_exec(void *) __attribute__((noreturn));
170
171 static void *threaded_exec(void *arg)
172 {
173         char *buff, *p2;
174         struct text_object *obj = arg;
175         struct execi_data *ed = obj->data.opaque;
176
177         while (1) {
178                 buff = malloc(text_buffer_size);
179                 read_exec(ed->cmd, buff, text_buffer_size);
180                 p2 = buff;
181                 while (*p2) {
182                         if (*p2 == '\001') {
183                                 *p2 = ' ';
184                         }
185                         p2++;
186                 }
187                 timed_thread_lock(ed->p_timed_thread);
188                 if (ed->buffer)
189                         free(ed->buffer);
190                 ed->buffer = buff;
191                 timed_thread_unlock(ed->p_timed_thread);
192                 if (timed_thread_test(ed->p_timed_thread, 0)) {
193                         timed_thread_exit(ed->p_timed_thread);
194                 }
195         }
196         /* never reached */
197 }
198
199 /* check the execi fields and return true if the given interval has passed */
200 static int time_to_update(struct execi_data *ed)
201 {
202         if (!ed->interval)
203                 return 0;
204         if (current_update_time - ed->last_update >= ed->interval)
205                 return 1;
206         return 0;
207 }
208
209 void scan_exec_arg(struct text_object *obj, const char *arg)
210 {
211         obj->data.s = strndup(arg ? arg : "", text_buffer_size);
212 }
213
214 void scan_pre_exec_arg(struct text_object *obj, const char *arg)
215 {
216         char buf[2048];
217
218         obj->type = OBJ_text;
219         read_exec(arg, buf, sizeof(buf));
220         obj->data.s = strndup(buf, text_buffer_size);
221 }
222
223 void scan_execi_arg(struct text_object *obj, const char *arg)
224 {
225         struct execi_data *ed;
226         int n;
227
228         ed = malloc(sizeof(struct execi_data));
229         memset(ed, 0, sizeof(struct execi_data));
230
231         if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
232                 NORM_ERR("${execi* <interval> command}");
233                 free(ed);
234                 return;
235         }
236         ed->cmd = strndup(arg + n, text_buffer_size);
237         obj->data.opaque = ed;
238 }
239
240 #ifdef X11
241 void scan_execgraph_arg(struct text_object *obj, const char *arg)
242 {
243         struct execi_data *ed;
244         char *buf;
245
246         ed = malloc(sizeof(struct execi_data));
247         memset(ed, 0, sizeof(struct execi_data));
248
249         buf = scan_graph(obj, arg, 100);
250         if (!buf) {
251                 NORM_ERR("missing command argument to execgraph object");
252                 return;
253         }
254         ed->cmd = buf;
255         obj->data.opaque = ed;
256 }
257 #endif /* X11 */
258
259 void print_exec(struct text_object *obj, char *p, int p_max_size)
260 {
261         read_exec(obj->data.s, p, p_max_size);
262         remove_deleted_chars(p);
263 }
264
265 void print_execp(struct text_object *obj, char *p, int p_max_size)
266 {
267         struct information *tmp_info;
268         struct text_object subroot;
269
270         read_exec(obj->data.s, p, p_max_size);
271
272         tmp_info = malloc(sizeof(struct information));
273         memcpy(tmp_info, &info, sizeof(struct information));
274         parse_conky_vars(&subroot, p, p, tmp_info);
275
276         free_text_objects(&subroot, 1);
277         free(tmp_info);
278 }
279
280 void print_execi(struct text_object *obj, char *p, int p_max_size)
281 {
282         struct execi_data *ed = obj->data.opaque;
283
284         if (!ed)
285                 return;
286
287         if (time_to_update(ed)) {
288                 if (!ed->buffer)
289                         ed->buffer = malloc(text_buffer_size);
290                 read_exec(ed->cmd, ed->buffer, text_buffer_size);
291                 ed->last_update = current_update_time;
292         }
293         snprintf(p, p_max_size, "%s", ed->buffer);
294 }
295
296 void print_execpi(struct text_object *obj, char *p)
297 {
298         struct execi_data *ed = obj->data.opaque;
299         struct text_object subroot;
300         struct information *tmp_info;
301
302         if (!ed)
303                 return;
304
305         tmp_info = malloc(sizeof(struct information));
306         memcpy(tmp_info, &info, sizeof(struct information));
307
308         if (!time_to_update(ed)) {
309                 parse_conky_vars(&subroot, ed->buffer, p, tmp_info);
310         } else {
311                 char *output;
312                 int length;
313                 FILE *fp = pid_popen(ed->cmd, "r", &childpid);
314
315                 if (!ed->buffer)
316                         ed->buffer = malloc(text_buffer_size);
317
318                 length = fread(ed->buffer, 1, text_buffer_size, fp);
319                 pclose(fp);
320
321                 output = ed->buffer;
322                 output[length] = '\0';
323                 if (length > 0 && output[length - 1] == '\n') {
324                         output[length - 1] = '\0';
325                 }
326
327                 parse_conky_vars(&subroot, ed->buffer, p, tmp_info);
328                 ed->last_update = current_update_time;
329         }
330         free_text_objects(&subroot, 1);
331         free(tmp_info);
332 }
333
334 void print_texeci(struct text_object *obj, char *p, int p_max_size)
335 {
336         struct execi_data *ed = obj->data.opaque;
337
338         if (!ed)
339                 return;
340
341         if (!ed->p_timed_thread) {
342                 ed->p_timed_thread = timed_thread_create(&threaded_exec,
343                                         (void *) obj, ed->interval * 1000000);
344                 if (!ed->p_timed_thread) {
345                         NORM_ERR("Error creating texeci timed thread");
346                 }
347                 /*
348                  * note that we don't register this thread with the
349                  * timed_thread list, because we destroy it manually
350                  */
351                 if (timed_thread_run(ed->p_timed_thread)) {
352                         NORM_ERR("Error running texeci timed thread");
353                 }
354         } else {
355                 timed_thread_lock(ed->p_timed_thread);
356                 snprintf(p, p_max_size, "%s", ed->buffer);
357                 timed_thread_unlock(ed->p_timed_thread);
358         }
359 }
360
361 #ifdef X11
362 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
363 {
364         double barnum;
365
366         read_exec(obj->data.s, p, p_max_size);
367         barnum = get_barnum(p); /*using the same function*/
368
369         if (barnum >= 0.0) {
370                 barnum /= 100;
371                 new_gauge(obj, p, round_to_int(barnum * 255.0));
372         }
373 }
374
375 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
376 {
377         double barnum;
378         struct execi_data *ed = obj->data.opaque;
379
380         if (!ed)
381                 return;
382
383         read_exec(ed->cmd, p, p_max_size);
384         barnum = get_barnum(p);
385
386         if (barnum > 0) {
387                 new_graph(obj, p, round_to_int(barnum));
388         }
389 }
390
391 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
392 {
393         struct execi_data *ed = obj->data.opaque;
394
395         if (!ed)
396                 return;
397
398         if (time_to_update(ed)) {
399                 double barnum;
400
401                 read_exec(ed->cmd, p, p_max_size);
402                 barnum = get_barnum(p);
403
404                 if (barnum >= 0.0) {
405                         ed->barnum = barnum;
406                 }
407                 ed->last_update = current_update_time;
408         }
409         new_graph(obj, p, (int) (ed->barnum));
410 }
411
412 void print_execigauge(struct text_object *obj, char *p, int p_max_size)
413 {
414         struct execi_data *ed = obj->data.opaque;
415
416         if (!ed)
417                 return;
418
419         if (time_to_update(ed)) {
420                 double barnum;
421
422                 read_exec(ed->cmd, p, p_max_size);
423                 barnum = get_barnum(p);
424
425                 if (barnum >= 0.0) {
426                         ed->barnum = 255 * barnum / 100.0;
427                 }
428                 ed->last_update = current_update_time;
429         }
430         new_gauge(obj, p, round_to_int(ed->barnum));
431 }
432 #endif /* X11 */
433
434 void print_execbar(struct text_object *obj, char *p, int p_max_size)
435 {
436         double barnum;
437         read_exec(obj->data.s, p, p_max_size);
438         barnum = get_barnum(p);
439
440         if (barnum >= 0.0) {
441 #ifdef X11
442                 if(output_methods & TO_X) {
443                         barnum /= 100;
444                         new_bar(obj, p, round_to_int(barnum * 255.0));
445                 }else
446 #endif /* X11 */
447                         new_bar_in_shell(obj, p, p_max_size, barnum);
448         }
449 }
450
451 void print_execibar(struct text_object *obj, char *p, int p_max_size)
452 {
453         struct execi_data *ed = obj->data.opaque;
454         double barnum;
455
456         if (!ed)
457                 return;
458
459         if (time_to_update(ed)) {
460                 read_exec(ed->cmd, p, p_max_size);
461                 barnum = get_barnum(p);
462
463                 if (barnum >= 0.0) {
464                         ed->barnum = barnum;
465                 }
466                 ed->last_update = current_update_time;
467         }
468 #ifdef X11
469         if(output_methods & TO_X) {
470                 new_bar(obj, p, round_to_int(ed->barnum * 2.55));
471         } else
472 #endif /* X11 */
473                 new_bar_in_shell(obj, p, p_max_size, round_to_int(ed->barnum));
474 }
475
476 void free_exec(struct text_object *obj)
477 {
478         if (obj->data.s) {
479                 free(obj->data.s);
480                 obj->data.s = NULL;
481         }
482 }
483
484 void free_execi(struct text_object *obj)
485 {
486         struct execi_data *ed = obj->data.opaque;
487
488         if (!ed)
489                 return;
490
491         if (ed->p_timed_thread)
492                 timed_thread_destroy(ed->p_timed_thread, &ed->p_timed_thread);
493         if (ed->cmd)
494                 free(ed->cmd);
495         if (ed->buffer)
496                 free(ed->buffer);
497         free(obj->data.opaque);
498         obj->data.opaque = NULL;
499 }