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