fclose(3) instead of pclose(3) as the latter seems to leak file descriptors
[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-2010 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                 close(parentend);
97                 dup(childend);  //by dupping childend, the returned fd will have close-on-exec turned off
98                 execl("/bin/sh", "sh", "-c", command, (char *) NULL);
99                 _exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
100         }
101         return fdopen(parentend, mode);
102 }
103
104 //remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
105 //string has to end with \0 and it's length should fit in a int
106 #define BACKSPACE 8
107 static void remove_deleted_chars(char *string){
108         int i = 0;
109         while(string[i] != 0){
110                 if(string[i] == BACKSPACE){
111                         if(i != 0){
112                                 strcpy( &(string[i-1]), &(string[i+1]) );
113                                 i--;
114                         }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
115                 }else i++;
116         }
117 }
118
119 static inline double get_barnum(char *buf)
120 {
121         char *c = buf;
122         double barnum;
123
124         while (*c) {
125                 if (*c == '\001') {
126                         *c = ' ';
127                 }
128                 c++;
129         }
130
131         if (sscanf(buf, "%lf", &barnum) == 0) {
132                 NORM_ERR("reading exec value failed (perhaps it's not the "
133                                 "correct format?)");
134                 return -1;
135         }
136         if (barnum > 100.0 || barnum < 0.0) {
137                 NORM_ERR("your exec value is not between 0 and 100, "
138                                 "therefore it will be ignored");
139                 return -1;
140         }
141         return barnum;
142 }
143
144 static inline void read_exec(const char *data, char *buf, const int size, const
145                 char use_alarm)
146 {
147         FILE *fp;
148
149         memset(buf, 0, size);
150
151         if (!data)
152                 return;
153
154         if (use_alarm) alarm(update_interval);
155         fp = pid_popen(data, "r", &childpid);
156         if(fp) {
157                 int length;
158
159                 length = fread(buf, 1, size, fp);
160                 fclose(fp);
161                 buf[length] = '\0';
162                 if (length > 0 && buf[length - 1] == '\n') {
163                         buf[length - 1] = '\0';
164                 }
165         } else {
166                 buf[0] = '\0';
167         }
168         if (use_alarm) alarm(0);
169 }
170
171 static void *threaded_exec(void *) __attribute__((noreturn));
172
173 static void *threaded_exec(void *arg)
174 {
175         char *buff, *p2;
176         struct text_object *obj = arg;
177         struct execi_data *ed = obj->data.opaque;
178
179         while (1) {
180                 buff = malloc(text_buffer_size);
181                 read_exec(ed->cmd, buff, text_buffer_size, 0);
182                 p2 = buff;
183                 while (*p2) {
184                         if (*p2 == '\001') {
185                                 *p2 = ' ';
186                         }
187                         p2++;
188                 }
189                 timed_thread_lock(ed->p_timed_thread);
190                 if (ed->buffer)
191                         free(ed->buffer);
192                 ed->buffer = buff;
193                 timed_thread_unlock(ed->p_timed_thread);
194                 if (timed_thread_test(ed->p_timed_thread, 0)) {
195                         timed_thread_exit(ed->p_timed_thread);
196                 }
197         }
198         /* never reached */
199 }
200
201 /* check the execi fields and return true if the given interval has passed */
202 static int time_to_update(struct execi_data *ed)
203 {
204         if (!ed->interval)
205                 return 0;
206         if (current_update_time - ed->last_update >= ed->interval)
207                 return 1;
208         return 0;
209 }
210
211 void scan_exec_arg(struct text_object *obj, const char *arg)
212 {
213         obj->data.s = strndup(arg ? arg : "", text_buffer_size);
214 }
215
216 void scan_pre_exec_arg(struct text_object *obj, const char *arg)
217 {
218         char buf[2048];
219
220         obj->type = OBJ_text;
221         read_exec(arg, buf, sizeof(buf), 1);
222         obj->data.s = strndup(buf, text_buffer_size);
223 }
224
225 void scan_execi_arg(struct text_object *obj, const char *arg)
226 {
227         struct execi_data *ed;
228         int n;
229
230         ed = malloc(sizeof(struct execi_data));
231         memset(ed, 0, sizeof(struct execi_data));
232
233         if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
234                 NORM_ERR("${execi* <interval> command}");
235                 free(ed);
236                 return;
237         }
238         ed->cmd = strndup(arg + n, text_buffer_size);
239         obj->data.opaque = ed;
240 }
241
242 #ifdef X11
243 void scan_execgraph_arg(struct text_object *obj, const char *arg)
244 {
245         struct execi_data *ed;
246         char *buf;
247
248         ed = malloc(sizeof(struct execi_data));
249         memset(ed, 0, sizeof(struct execi_data));
250
251         buf = scan_graph(obj, arg, 100);
252         if (!buf) {
253                 NORM_ERR("missing command argument to execgraph object");
254                 return;
255         }
256         ed->cmd = buf;
257         obj->data.opaque = ed;
258 }
259 #endif /* X11 */
260
261 void print_exec(struct text_object *obj, char *p, int p_max_size)
262 {
263         read_exec(obj->data.s, p, p_max_size, 1);
264         remove_deleted_chars(p);
265 }
266
267 void print_execp(struct text_object *obj, char *p, int p_max_size)
268 {
269         struct information *tmp_info;
270         struct text_object subroot;
271         char *buf;
272
273         buf = malloc(text_buffer_size);
274         memset(buf, 0, text_buffer_size);
275
276         read_exec(obj->data.s, buf, text_buffer_size, 1);
277
278         tmp_info = malloc(sizeof(struct information));
279         memcpy(tmp_info, &info, sizeof(struct information));
280         parse_conky_vars(&subroot, buf, p, p_max_size, tmp_info);
281
282         free_text_objects(&subroot, 1);
283         free(tmp_info);
284         free(buf);
285 }
286
287 void print_execi(struct text_object *obj, char *p, int p_max_size)
288 {
289         struct execi_data *ed = obj->data.opaque;
290
291         if (!ed)
292                 return;
293
294         if (time_to_update(ed)) {
295                 if (!ed->buffer)
296                         ed->buffer = malloc(text_buffer_size);
297                 read_exec(ed->cmd, ed->buffer, text_buffer_size, 1);
298                 ed->last_update = current_update_time;
299         }
300         snprintf(p, p_max_size, "%s", ed->buffer);
301 }
302
303 void print_execpi(struct text_object *obj, char *p, int p_max_size)
304 {
305         struct execi_data *ed = obj->data.opaque;
306         struct text_object subroot;
307         struct information *tmp_info;
308
309         if (!ed)
310                 return;
311
312         tmp_info = malloc(sizeof(struct information));
313         memcpy(tmp_info, &info, sizeof(struct information));
314
315         if (time_to_update(ed)) {
316                 char *output;
317                 int length;
318                 FILE *fp = pid_popen(ed->cmd, "r", &childpid);
319
320                 if (!ed->buffer)
321                         ed->buffer = malloc(text_buffer_size);
322
323                 length = fread(ed->buffer, 1, text_buffer_size, fp);
324                 fclose(fp);
325
326                 output = ed->buffer;
327                 output[length] = '\0';
328                 if (length > 0 && output[length - 1] == '\n') {
329                         output[length - 1] = '\0';
330                 }
331
332                 ed->last_update = current_update_time;
333         }
334         parse_conky_vars(&subroot, ed->buffer, p, p_max_size, tmp_info);
335         free_text_objects(&subroot, 1);
336         free(tmp_info);
337 }
338
339 void print_texeci(struct text_object *obj, char *p, int p_max_size)
340 {
341         struct execi_data *ed = obj->data.opaque;
342
343         if (!ed)
344                 return;
345
346         if (!ed->p_timed_thread) {
347                 ed->p_timed_thread = timed_thread_create(&threaded_exec,
348                                         (void *) obj, ed->interval * 1000000);
349                 if (!ed->p_timed_thread) {
350                         NORM_ERR("Error creating texeci timed thread");
351                 }
352                 /*
353                  * note that we don't register this thread with the
354                  * timed_thread list, because we destroy it manually
355                  */
356                 if (timed_thread_run(ed->p_timed_thread)) {
357                         NORM_ERR("Error running texeci timed thread");
358                 }
359         } else {
360                 timed_thread_lock(ed->p_timed_thread);
361                 snprintf(p, p_max_size, "%s", ed->buffer);
362                 timed_thread_unlock(ed->p_timed_thread);
363         }
364 }
365
366 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
367 {
368         double barnum;
369
370         read_exec(obj->data.s, p, p_max_size, 1);
371         barnum = get_barnum(p); /*using the same function*/
372
373         if (barnum >= 0.0) {
374                 barnum /= 100;
375                 new_gauge(obj, p, p_max_size, round_to_int(barnum * 255.0));
376         }
377 }
378
379 #ifdef X11
380 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
381 {
382         double barnum;
383         struct execi_data *ed = obj->data.opaque;
384
385         if (!ed)
386                 return;
387
388         read_exec(ed->cmd, p, p_max_size, 1);
389         barnum = get_barnum(p);
390
391         if (barnum > 0) {
392                 new_graph(obj, p, p_max_size, round_to_int(barnum));
393         }
394 }
395
396 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
397 {
398         struct execi_data *ed = obj->data.opaque;
399
400         if (!ed)
401                 return;
402
403         if (time_to_update(ed)) {
404                 double barnum;
405
406                 read_exec(ed->cmd, p, p_max_size, 1);
407                 barnum = get_barnum(p);
408
409                 if (barnum >= 0.0) {
410                         ed->barnum = barnum;
411                 }
412                 ed->last_update = current_update_time;
413         }
414         new_graph(obj, p, p_max_size, (int) (ed->barnum));
415 }
416 #endif /* X11 */
417
418 void print_execigauge(struct text_object *obj, char *p, int p_max_size)
419 {
420         struct execi_data *ed = obj->data.opaque;
421
422         if (!ed)
423                 return;
424
425         if (time_to_update(ed)) {
426                 double barnum;
427
428                 read_exec(ed->cmd, p, p_max_size, 1);
429                 barnum = get_barnum(p);
430
431                 if (barnum >= 0.0) {
432                         ed->barnum = 255 * barnum / 100.0;
433                 }
434                 ed->last_update = current_update_time;
435         }
436         new_gauge(obj, p, p_max_size, round_to_int(ed->barnum));
437 }
438
439 void print_execbar(struct text_object *obj, char *p, int p_max_size)
440 {
441         double barnum;
442         read_exec(obj->data.s, p, p_max_size, 1);
443         barnum = get_barnum(p);
444
445         if (barnum >= 0.0) {
446                 barnum /= 100;
447                 new_bar(obj, p, p_max_size, round_to_int(barnum * 255.0));
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, 1);
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         new_bar(obj, p, p_max_size, round_to_int(ed->barnum * 2.55));
469 }
470
471 void free_exec(struct text_object *obj)
472 {
473         if (obj->data.s) {
474                 free(obj->data.s);
475                 obj->data.s = NULL;
476         }
477 }
478
479 void free_execi(struct text_object *obj)
480 {
481         struct execi_data *ed = obj->data.opaque;
482
483         if (!ed)
484                 return;
485
486         if (ed->p_timed_thread)
487                 timed_thread_destroy(ed->p_timed_thread, &ed->p_timed_thread);
488         if (ed->cmd)
489                 free(ed->cmd);
490         if (ed->buffer)
491                 free(ed->buffer);
492         free(obj->data.opaque);
493         obj->data.opaque = NULL;
494 }