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