Bulid fix for nvidia support.
[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, const
144                 char use_alarm)
145 {
146         FILE *fp;
147
148         memset(buf, 0, size);
149
150         if (!data)
151                 return;
152
153         if (use_alarm) alarm(update_interval);
154         fp = pid_popen(data, "r", &childpid);
155         if(fp) {
156                 int length;
157
158                 length = fread(buf, 1, size, fp);
159                 pclose(fp);
160                 buf[length] = '\0';
161                 if (length > 0 && buf[length - 1] == '\n') {
162                         buf[length - 1] = '\0';
163                 }
164         } else {
165                 buf[0] = '\0';
166         }
167         if (use_alarm) alarm(0);
168 }
169
170 static void *threaded_exec(void *) __attribute__((noreturn));
171
172 static void *threaded_exec(void *arg)
173 {
174         char *buff, *p2;
175         struct text_object *obj = arg;
176         struct execi_data *ed = obj->data.opaque;
177
178         while (1) {
179                 buff = malloc(text_buffer_size);
180                 read_exec(ed->cmd, buff, text_buffer_size, 0);
181                 p2 = buff;
182                 while (*p2) {
183                         if (*p2 == '\001') {
184                                 *p2 = ' ';
185                         }
186                         p2++;
187                 }
188                 timed_thread_lock(ed->p_timed_thread);
189                 if (ed->buffer)
190                         free(ed->buffer);
191                 ed->buffer = buff;
192                 timed_thread_unlock(ed->p_timed_thread);
193                 if (timed_thread_test(ed->p_timed_thread, 0)) {
194                         timed_thread_exit(ed->p_timed_thread);
195                 }
196         }
197         /* never reached */
198 }
199
200 /* check the execi fields and return true if the given interval has passed */
201 static int time_to_update(struct execi_data *ed)
202 {
203         if (!ed->interval)
204                 return 0;
205         if (current_update_time - ed->last_update >= ed->interval)
206                 return 1;
207         return 0;
208 }
209
210 void scan_exec_arg(struct text_object *obj, const char *arg)
211 {
212         obj->data.s = strndup(arg ? arg : "", text_buffer_size);
213 }
214
215 void scan_pre_exec_arg(struct text_object *obj, const char *arg)
216 {
217         char buf[2048];
218
219         obj->type = OBJ_text;
220         read_exec(arg, buf, sizeof(buf), 1);
221         obj->data.s = strndup(buf, text_buffer_size);
222 }
223
224 void scan_execi_arg(struct text_object *obj, const char *arg)
225 {
226         struct execi_data *ed;
227         int n;
228
229         ed = malloc(sizeof(struct execi_data));
230         memset(ed, 0, sizeof(struct execi_data));
231
232         if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
233                 NORM_ERR("${execi* <interval> command}");
234                 free(ed);
235                 return;
236         }
237         ed->cmd = strndup(arg + n, text_buffer_size);
238         obj->data.opaque = ed;
239 }
240
241 #ifdef X11
242 void scan_execgraph_arg(struct text_object *obj, const char *arg)
243 {
244         struct execi_data *ed;
245         char *buf;
246
247         ed = malloc(sizeof(struct execi_data));
248         memset(ed, 0, sizeof(struct execi_data));
249
250         buf = scan_graph(obj, arg, 100);
251         if (!buf) {
252                 NORM_ERR("missing command argument to execgraph object");
253                 return;
254         }
255         ed->cmd = buf;
256         obj->data.opaque = ed;
257 }
258 #endif /* X11 */
259
260 void print_exec(struct text_object *obj, char *p, int p_max_size)
261 {
262         read_exec(obj->data.s, p, p_max_size, 1);
263         remove_deleted_chars(p);
264 }
265
266 void print_execp(struct text_object *obj, char *p, int p_max_size)
267 {
268         struct information *tmp_info;
269         struct text_object subroot;
270         char *buf;
271
272         buf = malloc(text_buffer_size);
273         memset(buf, 0, text_buffer_size);
274
275         read_exec(obj->data.s, buf, text_buffer_size, 1);
276
277         tmp_info = malloc(sizeof(struct information));
278         memcpy(tmp_info, &info, sizeof(struct information));
279         parse_conky_vars(&subroot, buf, p, p_max_size, tmp_info);
280
281         free_text_objects(&subroot, 1);
282         free(tmp_info);
283         free(buf);
284 }
285
286 void print_execi(struct text_object *obj, char *p, int p_max_size)
287 {
288         struct execi_data *ed = obj->data.opaque;
289
290         if (!ed)
291                 return;
292
293         if (time_to_update(ed)) {
294                 if (!ed->buffer)
295                         ed->buffer = malloc(text_buffer_size);
296                 read_exec(ed->cmd, ed->buffer, text_buffer_size, 1);
297                 ed->last_update = current_update_time;
298         }
299         snprintf(p, p_max_size, "%s", ed->buffer);
300 }
301
302 void print_execpi(struct text_object *obj, char *p, int p_max_size)
303 {
304         struct execi_data *ed = obj->data.opaque;
305         struct text_object subroot;
306         struct information *tmp_info;
307
308         if (!ed)
309                 return;
310
311         tmp_info = malloc(sizeof(struct information));
312         memcpy(tmp_info, &info, sizeof(struct information));
313
314         if (time_to_update(ed)) {
315                 char *output;
316                 int length;
317                 FILE *fp = pid_popen(ed->cmd, "r", &childpid);
318
319                 if (!ed->buffer)
320                         ed->buffer = malloc(text_buffer_size);
321
322                 length = fread(ed->buffer, 1, text_buffer_size, fp);
323                 pclose(fp);
324
325                 output = ed->buffer;
326                 output[length] = '\0';
327                 if (length > 0 && output[length - 1] == '\n') {
328                         output[length - 1] = '\0';
329                 }
330
331                 ed->last_update = current_update_time;
332         }
333         parse_conky_vars(&subroot, ed->buffer, p, p_max_size, tmp_info);
334         free_text_objects(&subroot, 1);
335         free(tmp_info);
336 }
337
338 void print_texeci(struct text_object *obj, char *p, int p_max_size)
339 {
340         struct execi_data *ed = obj->data.opaque;
341
342         if (!ed)
343                 return;
344
345         if (!ed->p_timed_thread) {
346                 ed->p_timed_thread = timed_thread_create(&threaded_exec,
347                                         (void *) obj, ed->interval * 1000000);
348                 if (!ed->p_timed_thread) {
349                         NORM_ERR("Error creating texeci timed thread");
350                 }
351                 /*
352                  * note that we don't register this thread with the
353                  * timed_thread list, because we destroy it manually
354                  */
355                 if (timed_thread_run(ed->p_timed_thread)) {
356                         NORM_ERR("Error running texeci timed thread");
357                 }
358         } else {
359                 timed_thread_lock(ed->p_timed_thread);
360                 snprintf(p, p_max_size, "%s", ed->buffer);
361                 timed_thread_unlock(ed->p_timed_thread);
362         }
363 }
364
365 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
366 {
367         double barnum;
368
369         read_exec(obj->data.s, p, p_max_size, 1);
370         barnum = get_barnum(p); /*using the same function*/
371
372         if (barnum >= 0.0) {
373                 barnum /= 100;
374                 new_gauge(obj, p, p_max_size, round_to_int(barnum * 255.0));
375         }
376 }
377
378 #ifdef X11
379 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
380 {
381         double barnum;
382         struct execi_data *ed = obj->data.opaque;
383
384         if (!ed)
385                 return;
386
387         read_exec(ed->cmd, p, p_max_size, 1);
388         barnum = get_barnum(p);
389
390         if (barnum > 0) {
391                 new_graph(obj, p, p_max_size, round_to_int(barnum));
392         }
393 }
394
395 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
396 {
397         struct execi_data *ed = obj->data.opaque;
398
399         if (!ed)
400                 return;
401
402         if (time_to_update(ed)) {
403                 double barnum;
404
405                 read_exec(ed->cmd, p, p_max_size, 1);
406                 barnum = get_barnum(p);
407
408                 if (barnum >= 0.0) {
409                         ed->barnum = barnum;
410                 }
411                 ed->last_update = current_update_time;
412         }
413         new_graph(obj, p, p_max_size, (int) (ed->barnum));
414 }
415 #endif /* X11 */
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, 1);
428                 barnum = get_barnum(p);
429
430                 if (barnum >= 0.0) {
431                         ed->barnum = 255 * barnum / 100.0;
432                 }
433                 ed->last_update = current_update_time;
434         }
435         new_gauge(obj, p, p_max_size, round_to_int(ed->barnum));
436 }
437
438 void print_execbar(struct text_object *obj, char *p, int p_max_size)
439 {
440         double barnum;
441         read_exec(obj->data.s, p, p_max_size, 1);
442         barnum = get_barnum(p);
443
444         if (barnum >= 0.0) {
445                 barnum /= 100;
446                 new_bar(obj, p, p_max_size, round_to_int(barnum * 255.0));
447         }
448 }
449
450 void print_execibar(struct text_object *obj, char *p, int p_max_size)
451 {
452         struct execi_data *ed = obj->data.opaque;
453         double barnum;
454
455         if (!ed)
456                 return;
457
458         if (time_to_update(ed)) {
459                 read_exec(ed->cmd, p, p_max_size, 1);
460                 barnum = get_barnum(p);
461
462                 if (barnum >= 0.0) {
463                         ed->barnum = barnum;
464                 }
465                 ed->last_update = current_update_time;
466         }
467         new_bar(obj, p, p_max_size, round_to_int(ed->barnum * 2.55));
468 }
469
470 void free_exec(struct text_object *obj)
471 {
472         if (obj->data.s) {
473                 free(obj->data.s);
474                 obj->data.s = NULL;
475         }
476 }
477
478 void free_execi(struct text_object *obj)
479 {
480         struct execi_data *ed = obj->data.opaque;
481
482         if (!ed)
483                 return;
484
485         if (ed->p_timed_thread)
486                 timed_thread_destroy(ed->p_timed_thread, &ed->p_timed_thread);
487         if (ed->cmd)
488                 free(ed->cmd);
489         if (ed->buffer)
490                 free(ed->buffer);
491         free(obj->data.opaque);
492         obj->data.opaque = NULL;
493 }