286079a87917faac8d8f22adacaa29e5e3cdb01b
[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                 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         char *buf;
270
271         buf = malloc(text_buffer_size);
272         memset(buf, 0, text_buffer_size);
273
274         read_exec(obj->data.s, buf, text_buffer_size);
275
276         tmp_info = malloc(sizeof(struct information));
277         memcpy(tmp_info, &info, sizeof(struct information));
278         parse_conky_vars(&subroot, buf, p, p_max_size, tmp_info);
279
280         free_text_objects(&subroot, 1);
281         free(tmp_info);
282         free(buf);
283 }
284
285 void print_execi(struct text_object *obj, char *p, int p_max_size)
286 {
287         struct execi_data *ed = obj->data.opaque;
288
289         if (!ed)
290                 return;
291
292         if (time_to_update(ed)) {
293                 if (!ed->buffer)
294                         ed->buffer = malloc(text_buffer_size);
295                 read_exec(ed->cmd, ed->buffer, text_buffer_size);
296                 ed->last_update = current_update_time;
297         }
298         snprintf(p, p_max_size, "%s", ed->buffer);
299 }
300
301 void print_execpi(struct text_object *obj, char *p, int p_max_size)
302 {
303         struct execi_data *ed = obj->data.opaque;
304         struct text_object subroot;
305         struct information *tmp_info;
306
307         if (!ed)
308                 return;
309
310         tmp_info = malloc(sizeof(struct information));
311         memcpy(tmp_info, &info, sizeof(struct information));
312
313         if (time_to_update(ed)) {
314                 char *output;
315                 int length;
316                 FILE *fp = pid_popen(ed->cmd, "r", &childpid);
317
318                 if (!ed->buffer)
319                         ed->buffer = malloc(text_buffer_size);
320
321                 length = fread(ed->buffer, 1, text_buffer_size, fp);
322                 pclose(fp);
323
324                 output = ed->buffer;
325                 output[length] = '\0';
326                 if (length > 0 && output[length - 1] == '\n') {
327                         output[length - 1] = '\0';
328                 }
329
330                 ed->last_update = current_update_time;
331         }
332         parse_conky_vars(&subroot, ed->buffer, p, p_max_size, tmp_info);
333         free_text_objects(&subroot, 1);
334         free(tmp_info);
335 }
336
337 void print_texeci(struct text_object *obj, char *p, int p_max_size)
338 {
339         struct execi_data *ed = obj->data.opaque;
340
341         if (!ed)
342                 return;
343
344         if (!ed->p_timed_thread) {
345                 ed->p_timed_thread = timed_thread_create(&threaded_exec,
346                                         (void *) obj, ed->interval * 1000000);
347                 if (!ed->p_timed_thread) {
348                         NORM_ERR("Error creating texeci timed thread");
349                 }
350                 /*
351                  * note that we don't register this thread with the
352                  * timed_thread list, because we destroy it manually
353                  */
354                 if (timed_thread_run(ed->p_timed_thread)) {
355                         NORM_ERR("Error running texeci timed thread");
356                 }
357         } else {
358                 timed_thread_lock(ed->p_timed_thread);
359                 snprintf(p, p_max_size, "%s", ed->buffer);
360                 timed_thread_unlock(ed->p_timed_thread);
361         }
362 }
363
364 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
365 {
366         double barnum;
367
368         read_exec(obj->data.s, p, p_max_size);
369         barnum = get_barnum(p); /*using the same function*/
370
371         if (barnum >= 0.0) {
372                 barnum /= 100;
373                 new_gauge(obj, p, p_max_size, round_to_int(barnum * 255.0));
374         }
375 }
376
377 #ifdef X11
378 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
379 {
380         double barnum;
381         struct execi_data *ed = obj->data.opaque;
382
383         if (!ed)
384                 return;
385
386         read_exec(ed->cmd, p, p_max_size);
387         barnum = get_barnum(p);
388
389         if (barnum > 0) {
390                 new_graph(obj, p, p_max_size, round_to_int(barnum));
391         }
392 }
393
394 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
395 {
396         struct execi_data *ed = obj->data.opaque;
397
398         if (!ed)
399                 return;
400
401         if (time_to_update(ed)) {
402                 double barnum;
403
404                 read_exec(ed->cmd, p, p_max_size);
405                 barnum = get_barnum(p);
406
407                 if (barnum >= 0.0) {
408                         ed->barnum = barnum;
409                 }
410                 ed->last_update = current_update_time;
411         }
412         new_graph(obj, p, p_max_size, (int) (ed->barnum));
413 }
414 #endif /* X11 */
415
416 void print_execigauge(struct text_object *obj, char *p, int p_max_size)
417 {
418         struct execi_data *ed = obj->data.opaque;
419
420         if (!ed)
421                 return;
422
423         if (time_to_update(ed)) {
424                 double barnum;
425
426                 read_exec(ed->cmd, p, p_max_size);
427                 barnum = get_barnum(p);
428
429                 if (barnum >= 0.0) {
430                         ed->barnum = 255 * barnum / 100.0;
431                 }
432                 ed->last_update = current_update_time;
433         }
434         new_gauge(obj, p, p_max_size, round_to_int(ed->barnum));
435 }
436
437 void print_execbar(struct text_object *obj, char *p, int p_max_size)
438 {
439         double barnum;
440         read_exec(obj->data.s, p, p_max_size);
441         barnum = get_barnum(p);
442
443         if (barnum >= 0.0) {
444                 barnum /= 100;
445                 new_bar(obj, p, p_max_size, round_to_int(barnum * 255.0));
446         }
447 }
448
449 void print_execibar(struct text_object *obj, char *p, int p_max_size)
450 {
451         struct execi_data *ed = obj->data.opaque;
452         double barnum;
453
454         if (!ed)
455                 return;
456
457         if (time_to_update(ed)) {
458                 read_exec(ed->cmd, p, p_max_size);
459                 barnum = get_barnum(p);
460
461                 if (barnum >= 0.0) {
462                         ed->barnum = barnum;
463                 }
464                 ed->last_update = current_update_time;
465         }
466         new_bar(obj, p, p_max_size, round_to_int(ed->barnum * 2.55));
467 }
468
469 void free_exec(struct text_object *obj)
470 {
471         if (obj->data.s) {
472                 free(obj->data.s);
473                 obj->data.s = NULL;
474         }
475 }
476
477 void free_execi(struct text_object *obj)
478 {
479         struct execi_data *ed = obj->data.opaque;
480
481         if (!ed)
482                 return;
483
484         if (ed->p_timed_thread)
485                 timed_thread_destroy(ed->p_timed_thread, &ed->p_timed_thread);
486         if (ed->cmd)
487                 free(ed->cmd);
488         if (ed->buffer)
489                 free(ed->buffer);
490         free(obj->data.opaque);
491         obj->data.opaque = NULL;
492 }