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