exec: put all exec-related stuff into it's own file
authorPhil Sutter <phil@nwl.cc>
Thu, 15 Oct 2009 19:51:21 +0000 (21:51 +0200)
committerPhil Sutter <phil@nwl.cc>
Tue, 3 Nov 2009 00:50:28 +0000 (01:50 +0100)
While here, also merge the execi and texeci fields of struct
text_object, so more common code can be shared in between.

src/Makefile.am
src/conky.c
src/conky.h
src/core.c
src/exec.c [new file with mode: 0644]
src/exec.h [new file with mode: 0644]
src/text_object.h

index ac6f1b8..6c415f0 100644 (file)
@@ -50,11 +50,11 @@ endif # BUILD_CONFIG_OUTPUT
 
 # source files always needed for compiling
 mandatory_sources = colours.c colours.h common.c common.h conky.c conky.h \
-               core.c core.h diskio.c diskio.h fs.c fs.h logging.h mail.c mail.h \
-               mixer.c mixer.h template.c template.h timed_thread.c timed_thread.h \
-               mboxscan.c mboxscan.h specials.c specials.h tailhead.c tailhead.h \
-               temphelper.c temphelper.h text_object.c text_object.h timeinfo.c \
-               timeinfo.h algebra.c algebra.h
+               core.c core.h diskio.c diskio.h exec.c exec.h fs.c fs.h logging.h \
+               mail.c mail.h mixer.c mixer.h template.c template.h timed_thread.c \
+               timed_thread.h mboxscan.c mboxscan.h specials.c specials.h tailhead.c \
+               tailhead.h temphelper.c temphelper.h text_object.c text_object.h \
+               timeinfo.c timeinfo.h algebra.c algebra.h
 
 # source files only needed when the apropriate option is enabled
 audacious = audacious.c audacious.h
index 60c6228..bfd0f80 100644 (file)
@@ -76,6 +76,7 @@
 #include "build.h"
 #include "colours.h"
 #include "diskio.h"
+#include "exec.h"
 #ifdef X11
 #include "fonts.h"
 #endif
@@ -163,7 +164,6 @@ double update_interval;
 double update_interval_old;
 double update_interval_bat;
 void *global_cpu = NULL;
-pid_t childpid = 0;
 
 int argc_copy;
 char** argv_copy;
@@ -631,107 +631,6 @@ static void human_readable(long long num, char *buf, int size)
 /* global object list root element */
 static struct text_object global_root_object;
 
-//our own implementation of popen, the difference : the value of 'childpid' will be filled with
-//the pid of the running 'command'. This is useful if want to kill it when it hangs while reading
-//or writing to it. We have to kill it because pclose will wait until the process dies by itself
-FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
-       int ends[2];
-       int parentend, childend;
-
-       //by running pipe after the strcmp's we make sure that we don't have to create a pipe
-       //and close the ends if mode is something illegal
-       if(strcmp(mode, "r") == 0) {
-               if(pipe(ends) != 0) {
-                       return NULL;
-               }
-               parentend = ends[0];
-               childend = ends[1];
-       } else if(strcmp(mode, "w") == 0) {
-               if(pipe(ends) != 0) {
-                       return NULL;
-               }
-               parentend = ends[1];
-               childend = ends[0];
-       } else {
-               return NULL;
-       }
-       *child = fork();
-       if(*child == -1) {
-               close(parentend);
-               close(childend);
-               return NULL;
-       } else if(*child > 0) {
-               close(childend);
-               waitpid(*child, NULL, 0);
-       } else {
-               //don't read from both stdin and pipe or write to both stdout and pipe
-               if(childend == ends[0]) {
-                       close(0);
-               } else {
-                       close(1);
-               }
-               dup(childend);  //by dupping childend, the returned fd will have close-on-exec turned off
-               execl("/bin/sh", "sh", "-c", command, (char *) NULL);
-               _exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail) 
-       }
-       return fdopen(parentend, mode);
-}
-
-static inline void read_exec(const char *data, char *buf, const int size)
-{
-       FILE *fp;
-
-       alarm(update_interval);
-       fp = pid_popen(data, "r", &childpid);
-       if(fp) {
-               int length;
-
-               length = fread(buf, 1, size, fp);
-               pclose(fp);
-               buf[length] = '\0';
-               if (length > 0 && buf[length - 1] == '\n') {
-                       buf[length - 1] = '\0';
-               }
-       } else {
-               buf[0] = '\0';
-       }
-       alarm(0);
-}
-
-void do_read_exec(const char *data, char *buf, const int size)
-{
-       read_exec(data, buf, size);
-}
-
-void *threaded_exec(void *) __attribute__((noreturn));
-
-void *threaded_exec(void *arg)
-{
-       char *buff, *p2;
-       struct text_object *obj = (struct text_object *)arg;
-
-       while (1) {
-               buff = malloc(text_buffer_size);
-               read_exec(obj->data.texeci.cmd, buff,
-                       text_buffer_size);
-               p2 = buff;
-               while (*p2) {
-                       if (*p2 == '\001') {
-                               *p2 = ' ';
-                       }
-                       p2++;
-               }
-               timed_thread_lock(obj->data.texeci.p_timed_thread);
-               strncpy(obj->data.texeci.buffer, buff, text_buffer_size);
-               timed_thread_unlock(obj->data.texeci.p_timed_thread);
-               free(buff);
-               if (timed_thread_test(obj->data.texeci.p_timed_thread, 0)) {
-                       timed_thread_exit(obj->data.texeci.p_timed_thread);
-               }
-       }
-       /* never reached */
-}
-
 static long current_text_color;
 
 void set_current_text_color(long colour)
@@ -893,21 +792,6 @@ char *format_time(unsigned long timeval, const int width)
        return strndup("<inf>", text_buffer_size);
 }
 
-//remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
-//string has to end with \0 and it's length should fit in a int
-#define BACKSPACE 8
-void remove_deleted_chars(char *string){
-       int i = 0;
-       while(string[i] != 0){
-               if(string[i] == BACKSPACE){
-                       if(i != 0){
-                               strcpy( &(string[i-1]), &(string[i+1]) );
-                               i--;
-                       }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
-               }else i++;
-       }
-}
-
 static inline void format_media_player_time(char *buf, const int size,
                int seconds)
 {
@@ -931,31 +815,6 @@ static inline void format_media_player_time(char *buf, const int size,
        }
 }
 
-static inline double get_barnum(char *buf)
-{
-       char *c = buf;
-       double barnum;
-
-       while (*c) {
-               if (*c == '\001') {
-                       *c = ' ';
-               }
-               c++;
-       }
-
-       if (sscanf(buf, "%lf", &barnum) == 0) {
-               NORM_ERR("reading exec value failed (perhaps it's not the "
-                               "correct format?)");
-               return -1;
-       }
-       if (barnum > 100.0 || barnum < 0.0) {
-               NORM_ERR("your exec value is not between 0 and 100, "
-                               "therefore it will be ignored");
-               return -1;
-       }
-       return barnum;
-}
-
 /* substitutes all occurrences of '\n' with SECRIT_MULTILINE_CHAR, which allows
  * multiline objects like $exec work with $align[rc] and friends
  */
@@ -1503,206 +1362,43 @@ static void generate_text_internal(char *p, int p_max_size,
                                evaluate(obj->data.s, p);
                        }
                        OBJ(exec) {
-                               read_exec(obj->data.s, p, text_buffer_size);
-                               remove_deleted_chars(p);
+                               print_exec(obj, p, p_max_size);
                        }
                        OBJ(execp) {
-                               struct information *tmp_info;
-                               struct text_object subroot;
-
-                               read_exec(obj->data.s, p, text_buffer_size);
-
-                               tmp_info = malloc(sizeof(struct information));
-                               memcpy(tmp_info, cur, sizeof(struct information));
-                               parse_conky_vars(&subroot, p, p, tmp_info);
-
-                               free_text_objects(&subroot, 1);
-                               free(tmp_info);
+                               print_execp(obj, p, p_max_size);
                        }
 #ifdef X11
                        OBJ(execgauge) {
-                               double barnum;
-
-                               read_exec(obj->data.s, p, text_buffer_size);
-                               barnum = get_barnum(p); /*using the same function*/
-
-                               if (barnum >= 0.0) {
-                                       barnum /= 100;
-                                       new_gauge(p, obj->a, obj->b, round_to_int(barnum * 255.0));
-                               }
+                               print_execgauge(obj, p, p_max_size);
                        }
 #endif /* X11 */
                        OBJ(execbar) {
-                               double barnum;
-
-                               read_exec(obj->data.s, p, text_buffer_size);
-                               barnum = get_barnum(p);
-
-                               if (barnum >= 0.0) {
-#ifdef X11
-                                       if(output_methods & TO_X) {
-                                               barnum /= 100;
-                                               new_bar(p, obj->a, obj->b, round_to_int(barnum * 255.0));
-                                       }else{
-#endif /* X11 */
-                                               if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
-                                               new_bar_in_shell(p, p_max_size, barnum, obj->a);
-#ifdef X11
-                                       }
-#endif /* X11 */
-                               }
+                               print_execbar(obj, p, p_max_size);
                        }
 #ifdef X11
                        OBJ(execgraph) {
-                               char showaslog = FALSE;
-                               char tempgrad = FALSE;
-                               double barnum;
-                               char *cmd = obj->data.s;
-
-                               if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
-                                       tempgrad = TRUE;
-                                       cmd += strlen(" "TEMPGRAD);
-                               }
-                               if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
-                                       showaslog = TRUE;
-                                       cmd += strlen(" "LOGGRAPH);
-                               }
-                               read_exec(cmd, p, text_buffer_size);
-                               barnum = get_barnum(p);
-
-                               if (barnum > 0) {
-                                       new_graph(p, obj->a, obj->b, obj->c, obj->d, round_to_int(barnum),
-                                                       100, 1, showaslog, tempgrad);
-                               }
+                               print_execgraph(obj, p, p_max_size);
                        }
 #endif /* X11 */
                        OBJ(execibar) {
-                               if (current_update_time - obj->data.execi.last_update
-                                               >= obj->data.execi.interval) {
-                                       double barnum;
-
-                                       read_exec(obj->data.execi.cmd, p, text_buffer_size);
-                                       barnum = get_barnum(p);
-
-                                       if (barnum >= 0.0) {
-                                               obj->f = barnum;
-                                       }
-                                       obj->data.execi.last_update = current_update_time;
-                               }
-#ifdef X11
-                               if(output_methods & TO_X) {
-                                       new_bar(p, obj->a, obj->b, round_to_int(obj->f * 2.55));
-                               } else {
-#endif /* X11 */
-                                       if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
-                                       new_bar_in_shell(p, p_max_size, round_to_int(obj->f), obj->a);
-#ifdef X11
-                               }
-#endif /* X11 */
+                               print_execibar(obj, p, p_max_size);
                        }
 #ifdef X11
                        OBJ(execigraph) {
-                               if (current_update_time - obj->data.execi.last_update
-                                               >= obj->data.execi.interval) {
-                                       double barnum;
-                                       char showaslog = FALSE;
-                                       char tempgrad = FALSE;
-                                       char *cmd = obj->data.execi.cmd;
-
-                                       if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
-                                               tempgrad = TRUE;
-                                               cmd += strlen(" "TEMPGRAD);
-                                       }
-                                       if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
-                                               showaslog = TRUE;
-                                               cmd += strlen(" "LOGGRAPH);
-                                       }
-                                       obj->char_a = showaslog;
-                                       obj->char_b = tempgrad;
-                                       read_exec(cmd, p, text_buffer_size);
-                                       barnum = get_barnum(p);
-
-                                       if (barnum >= 0.0) {
-                                               obj->f = barnum;
-                                       }
-                                       obj->data.execi.last_update = current_update_time;
-                               }
-                               new_graph(p, obj->a, obj->b, obj->c, obj->d, (int) (obj->f), 100, 1, obj->char_a, obj->char_b);
+                               print_execigraph(obj, p, p_max_size);
                        }
                        OBJ(execigauge) {
-                               if (current_update_time - obj->data.execi.last_update
-                                               >= obj->data.execi.interval) {
-                                       double barnum;
-
-                                       read_exec(obj->data.execi.cmd, p, text_buffer_size);
-                                       barnum = get_barnum(p);
-
-                                       if (barnum >= 0.0) {
-                                               obj->f = 255 * barnum / 100.0;
-                                       }
-                                       obj->data.execi.last_update = current_update_time;
-                               }
-                               new_gauge(p, obj->a, obj->b, round_to_int(obj->f));
+                               print_execigauge(obj, p, p_max_size);
                        }
 #endif /* X11 */
                        OBJ(execi) {
-                               if (current_update_time - obj->data.execi.last_update
-                                               >= obj->data.execi.interval
-                                               && obj->data.execi.interval != 0) {
-                                       read_exec(obj->data.execi.cmd, obj->data.execi.buffer,
-                                               text_buffer_size);
-                                       obj->data.execi.last_update = current_update_time;
-                               }
-                               snprintf(p, text_buffer_size, "%s", obj->data.execi.buffer);
+                               print_execi(obj, p, p_max_size);
                        }
                        OBJ(execpi) {
-                               struct text_object subroot;
-                               struct information *tmp_info =
-                                       malloc(sizeof(struct information));
-                               memcpy(tmp_info, cur, sizeof(struct information));
-
-                               if (current_update_time - obj->data.execi.last_update
-                                               < obj->data.execi.interval
-                                               || obj->data.execi.interval == 0) {
-                                       parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
-                               } else {
-                                       char *output = obj->data.execi.buffer;
-                                       FILE *fp = pid_popen(obj->data.execi.cmd, "r", &childpid);
-                                       int length = fread(output, 1, text_buffer_size, fp);
-
-                                       pclose(fp);
-
-                                       output[length] = '\0';
-                                       if (length > 0 && output[length - 1] == '\n') {
-                                               output[length - 1] = '\0';
-                                       }
-
-                                       parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
-                                       obj->data.execi.last_update = current_update_time;
-                               }
-                               free_text_objects(&subroot, 1);
-                               free(tmp_info);
+                               print_execpi(obj, p);
                        }
                        OBJ(texeci) {
-                               if (!obj->data.texeci.p_timed_thread) {
-                                       obj->data.texeci.p_timed_thread =
-                                               timed_thread_create(&threaded_exec,
-                                               (void *) obj, obj->data.texeci.interval * 1000000);
-                                       if (!obj->data.texeci.p_timed_thread) {
-                                               NORM_ERR("Error creating texeci timed thread");
-                                       }
-                                       /*
-                                        * note that we don't register this thread with the
-                                        * timed_thread list, because we destroy it manually
-                                        */
-                                       if (timed_thread_run(obj->data.texeci.p_timed_thread)) {
-                                               NORM_ERR("Error running texeci timed thread");
-                                       }
-                               } else {
-                                       timed_thread_lock(obj->data.texeci.p_timed_thread);
-                                       snprintf(p, text_buffer_size, "%s", obj->data.texeci.buffer);
-                                       timed_thread_unlock(obj->data.texeci.p_timed_thread);
-                               }
+                               print_texeci(obj, p, p_max_size);
                        }
                        OBJ(imap_unseen) {
                                struct mail_s *mail = ensure_mail_thread(obj, imap_thread, "imap");
index f0a850f..fa8c5b2 100644 (file)
@@ -336,9 +336,6 @@ extern unsigned int max_user_text;
 /* path to config file */
 extern char *current_config;
 
-/* just a wrapper for read_exec() defined in conky.c */
-void do_read_exec(const char *data, char *buf, const int size);
-
 #ifdef X11
 #define TO_X 1
 #endif /* X11 */
@@ -367,4 +364,7 @@ void set_update_interval(double interval);
 #define UNUSED(a)  (void)a
 #define UNUSED_ATTR __attribute__ ((unused))
 
+void parse_conky_vars(struct text_object *, const char *,
+                       char *, struct information *);
+
 #endif /* _conky_h_ */
index 42d4a8a..f245be8 100644 (file)
@@ -35,6 +35,7 @@
 #include "build.h"
 #include "colours.h"
 #include "diskio.h"
+#include "exec.h"
 #ifdef X11
 #include "fonts.h"
 #endif
@@ -596,117 +597,39 @@ struct text_object *construct_text_object(const char *s, const char *arg, long
                obj->data.s = strndup(arg ? arg : "", text_buffer_size);
 #endif /* IMLIB2 */
        END OBJ(exec, 0)
-               obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+               scan_exec_arg(obj, arg);
        END OBJ(execp, 0)
-               obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+               scan_exec_arg(obj, arg);
        END OBJ(execbar, 0)
                SIZE_DEFAULTS(bar);
-               obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+               scan_exec_arg(obj, arg);
 #ifdef X11
        END OBJ(execgauge, 0)
                SIZE_DEFAULTS(gauge);
-               obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+               scan_exec_arg(obj, arg);
        END OBJ(execgraph, 0)
                SIZE_DEFAULTS(graph);
-               obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+               scan_exec_arg(obj, arg);
 #endif /* X11 */
-       END OBJ(execibar, 0)
-               int n;
+       END OBJ_ARG(execibar, 0, "execibar needs arguments")
                SIZE_DEFAULTS(bar);
-
-               if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
-                       char buf[256];
-
-                       NORM_ERR("${execibar <interval> command}");
-                       obj->type = OBJ_text;
-                       snprintf(buf, 256, "${%s}", s);
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
-               }
+               scan_execi_arg(obj, arg);
 #ifdef X11
-       END OBJ(execigraph, 0)
-               int n;
+       END OBJ_ARG(execigraph, 0, "execigraph needs arguments")
                SIZE_DEFAULTS(graph);
-
-               if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
-                       char buf[256];
-
-                       NORM_ERR("${execigraph <interval> command}");
-                       obj->type = OBJ_text;
-                       snprintf(buf, 256, "${%s}", s);
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
-               }
-       END OBJ(execigauge, 0)
-               int n;
+               scan_execi_arg(obj, arg);
+       END OBJ_ARG(execigauge, 0, "execigauge needs arguments")
                SIZE_DEFAULTS(gauge);
-
-               if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
-                       char buf[256];
-
-                       NORM_ERR("${execigauge <interval> command}");
-                       obj->type = OBJ_text;
-                       snprintf(buf, 256, "${%s}", s);
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
-               }
+               scan_execi_arg(obj, arg);
 #endif /* X11 */
-       END OBJ(execi, 0)
-               int n;
-
-               if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
-                       char buf[256];
-
-                       NORM_ERR("${execi <interval> command}");
-                       obj->type = OBJ_text;
-                       snprintf(buf, 256, "${%s}", s);
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
-                       obj->data.execi.buffer = malloc(text_buffer_size);
-               }
-       END OBJ(execpi, 0)
-               int n;
-
-               if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
-                       char buf[256];
-
-                       NORM_ERR("${execi <interval> command}");
-                       obj->type = OBJ_text;
-                       snprintf(buf, 256, "${%s}", s);
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
-                       obj->data.execi.buffer = malloc(text_buffer_size);
-               }
-       END OBJ(texeci, 0)
-                       int n;
-
-                       if (!arg || sscanf(arg, "%f %n", &obj->data.texeci.interval, &n) <= 0) {
-                               char buf[256];
-
-                               NORM_ERR("${texeci <interval> command}");
-                               obj->type = OBJ_text;
-                               snprintf(buf, 256, "${%s}", s);
-                               obj->data.s = strndup(buf, text_buffer_size);
-                       } else {
-                               obj->data.texeci.cmd = strndup(arg + n, text_buffer_size);
-                               obj->data.texeci.buffer = malloc(text_buffer_size);
-                       }
-                       obj->data.texeci.p_timed_thread = NULL;
+       END OBJ_ARG(execi, 0, "execi needs arguments")
+               scan_execi_arg(obj, arg);
+       END OBJ_ARG(execpi, 0, "execpi needs arguments")
+               scan_execi_arg(obj, arg);
+       END OBJ_ARG(texeci, 0, "texeci needs arguments")
+               scan_execi_arg(obj, arg);
        END OBJ(pre_exec, 0)
-               obj->type = OBJ_text;
-               if (arg) {
-                       char buf[2048];
-
-                       do_read_exec(arg, buf, sizeof(buf));
-                       obj->data.s = strndup(buf, text_buffer_size);
-               } else {
-                       obj->data.s = strndup("", text_buffer_size);
-               }
+               scan_pre_exec_arg(obj, arg);
        END OBJ(fs_bar, &update_fs_stats)
                init_fs_bar(obj, arg);
        END OBJ(fs_bar_free, &update_fs_stats)
@@ -1956,7 +1879,7 @@ void free_text_objects(struct text_object *root, int internal)
                        case OBJ_execgraph:
 #endif
                        case OBJ_execp:
-                               free(data.s);
+                               free_exec(obj);
                                break;
 #ifdef HAVE_ICONV
                        case OBJ_iconv_start:
@@ -2118,17 +2041,12 @@ void free_text_objects(struct text_object *root, int internal)
                        case OBJ_execpi:
                        case OBJ_execi:
                        case OBJ_execibar:
+                       case OBJ_texeci:
 #ifdef X11
                        case OBJ_execigraph:
                        case OBJ_execigauge:
 #endif /* X11 */
-                               free(data.execi.cmd);
-                               free(data.execi.buffer);
-                               break;
-                       case OBJ_texeci:
-                               if (data.texeci.p_timed_thread) timed_thread_destroy(data.texeci.p_timed_thread, &data.texeci.p_timed_thread);
-                               free(data.texeci.cmd);
-                               free(data.texeci.buffer);
+                               free_execi(obj);
                                break;
                        case OBJ_nameserver:
                                free_dns_data();
diff --git a/src/exec.c b/src/exec.c
new file mode 100644 (file)
index 0000000..1867176
--- /dev/null
@@ -0,0 +1,455 @@
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=c
+ *
+ * Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
+ *     (see AUTHORS)
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "conky.h"
+#include "core.h"
+#include "logging.h"
+#include "specials.h"
+#include "text_object.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/* FIXME: this will probably not work, since the variable is being reused
+ * between different text objects. So when a process really hangs, it's PID
+ * will be overwritten at the next iteration. */
+pid_t childpid = 0;
+
+//our own implementation of popen, the difference : the value of 'childpid' will be filled with
+//the pid of the running 'command'. This is useful if want to kill it when it hangs while reading
+//or writing to it. We have to kill it because pclose will wait until the process dies by itself
+static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
+       int ends[2];
+       int parentend, childend;
+
+       //by running pipe after the strcmp's we make sure that we don't have to create a pipe
+       //and close the ends if mode is something illegal
+       if(strcmp(mode, "r") == 0) {
+               if(pipe(ends) != 0) {
+                       return NULL;
+               }
+               parentend = ends[0];
+               childend = ends[1];
+       } else if(strcmp(mode, "w") == 0) {
+               if(pipe(ends) != 0) {
+                       return NULL;
+               }
+               parentend = ends[1];
+               childend = ends[0];
+       } else {
+               return NULL;
+       }
+       *child = fork();
+       if(*child == -1) {
+               close(parentend);
+               close(childend);
+               return NULL;
+       } else if(*child > 0) {
+               close(childend);
+               waitpid(*child, NULL, 0);
+       } else {
+               //don't read from both stdin and pipe or write to both stdout and pipe
+               if(childend == ends[0]) {
+                       close(0);
+               } else {
+                       close(1);
+               }
+               dup(childend);  //by dupping childend, the returned fd will have close-on-exec turned off
+               execl("/bin/sh", "sh", "-c", command, (char *) NULL);
+               _exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail) 
+       }
+       return fdopen(parentend, mode);
+}
+
+//remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
+//string has to end with \0 and it's length should fit in a int
+#define BACKSPACE 8
+static void remove_deleted_chars(char *string){
+       int i = 0;
+       while(string[i] != 0){
+               if(string[i] == BACKSPACE){
+                       if(i != 0){
+                               strcpy( &(string[i-1]), &(string[i+1]) );
+                               i--;
+                       }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
+               }else i++;
+       }
+}
+
+static inline double get_barnum(char *buf)
+{
+       char *c = buf;
+       double barnum;
+
+       while (*c) {
+               if (*c == '\001') {
+                       *c = ' ';
+               }
+               c++;
+       }
+
+       if (sscanf(buf, "%lf", &barnum) == 0) {
+               NORM_ERR("reading exec value failed (perhaps it's not the "
+                               "correct format?)");
+               return -1;
+       }
+       if (barnum > 100.0 || barnum < 0.0) {
+               NORM_ERR("your exec value is not between 0 and 100, "
+                               "therefore it will be ignored");
+               return -1;
+       }
+       return barnum;
+}
+
+static inline void read_exec(const char *data, char *buf, const int size)
+{
+       FILE *fp;
+
+       memset(buf, 0, size);
+
+       if (!data)
+               return;
+
+       alarm(update_interval);
+       fp = pid_popen(data, "r", &childpid);
+       if(fp) {
+               int length;
+
+               length = fread(buf, 1, size, fp);
+               pclose(fp);
+               buf[length] = '\0';
+               if (length > 0 && buf[length - 1] == '\n') {
+                       buf[length - 1] = '\0';
+               }
+       } else {
+               buf[0] = '\0';
+       }
+       alarm(0);
+}
+
+static void *threaded_exec(void *) __attribute__((noreturn));
+
+static void *threaded_exec(void *arg)
+{
+       char *buff, *p2;
+       struct text_object *obj = arg;
+
+       while (1) {
+               buff = malloc(text_buffer_size);
+               read_exec(obj->data.execi.cmd, buff, text_buffer_size);
+               p2 = buff;
+               while (*p2) {
+                       if (*p2 == '\001') {
+                               *p2 = ' ';
+                       }
+                       p2++;
+               }
+               timed_thread_lock(obj->data.execi.p_timed_thread);
+               if (obj->data.execi.buffer)
+                       free(obj->data.execi.buffer);
+               obj->data.execi.buffer = buff;
+               timed_thread_unlock(obj->data.execi.p_timed_thread);
+               if (timed_thread_test(obj->data.execi.p_timed_thread, 0)) {
+                       timed_thread_exit(obj->data.execi.p_timed_thread);
+               }
+       }
+       /* never reached */
+}
+
+/* check the execi fields and return true if the given interval has passed */
+static int time_to_update(struct text_object *obj)
+{
+       if (!obj->data.execi.interval)
+               return 0;
+       if (current_update_time - obj->data.execi.last_update >= obj->data.execi.interval)
+               return 1;
+       return 0;
+}
+
+void scan_exec_arg(struct text_object *obj, const char *arg)
+{
+       obj->data.s = strndup(arg ? arg : "", text_buffer_size);
+}
+
+void scan_pre_exec_arg(struct text_object *obj, const char *arg)
+{
+       char buf[2048];
+
+       obj->type = OBJ_text;
+       read_exec(arg, buf, sizeof(buf));
+       obj->data.s = strndup(buf, text_buffer_size);
+}
+
+void scan_execi_arg(struct text_object *obj, const char *arg)
+{
+       int n;
+
+       if (sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
+               NORM_ERR("${execi* <interval> command}");
+               return;
+       }
+       obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
+}
+
+void print_exec(struct text_object *obj, char *p, int p_max_size)
+{
+       read_exec(obj->data.s, p, p_max_size);
+       remove_deleted_chars(p);
+}
+
+void print_execp(struct text_object *obj, char *p, int p_max_size)
+{
+       struct information *tmp_info;
+       struct text_object subroot;
+
+       read_exec(obj->data.s, p, p_max_size);
+
+       tmp_info = malloc(sizeof(struct information));
+       memcpy(tmp_info, &info, sizeof(struct information));
+       parse_conky_vars(&subroot, p, p, tmp_info);
+
+       free_text_objects(&subroot, 1);
+       free(tmp_info);
+}
+
+void print_execi(struct text_object *obj, char *p, int p_max_size)
+{
+       if (time_to_update(obj)) {
+               if (!obj->data.execi.buffer)
+                       obj->data.execi.buffer = malloc(text_buffer_size);
+               read_exec(obj->data.execi.cmd, obj->data.execi.buffer, text_buffer_size);
+               obj->data.execi.last_update = current_update_time;
+       }
+       snprintf(p, p_max_size, "%s", obj->data.execi.buffer);
+}
+
+void print_execpi(struct text_object *obj, char *p)
+{
+       struct text_object subroot;
+       struct information *tmp_info;
+
+       tmp_info = malloc(sizeof(struct information));
+       memcpy(tmp_info, &info, sizeof(struct information));
+
+       if (!time_to_update(obj)) {
+               parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
+       } else {
+               char *output;
+               int length;
+               FILE *fp = pid_popen(obj->data.execi.cmd, "r", &childpid);
+
+               if (!obj->data.execi.buffer)
+                       obj->data.execi.buffer = malloc(text_buffer_size);
+
+               length = fread(obj->data.execi.buffer, 1, text_buffer_size, fp);
+               pclose(fp);
+
+               output = obj->data.execi.buffer;
+               output[length] = '\0';
+               if (length > 0 && output[length - 1] == '\n') {
+                       output[length - 1] = '\0';
+               }
+
+               parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
+               obj->data.execi.last_update = current_update_time;
+       }
+       free_text_objects(&subroot, 1);
+       free(tmp_info);
+}
+
+void print_texeci(struct text_object *obj, char *p, int p_max_size)
+{
+       if (!obj->data.execi.p_timed_thread) {
+               obj->data.execi.p_timed_thread =
+                       timed_thread_create(&threaded_exec,
+                                       (void *) obj, obj->data.execi.interval * 1000000);
+               if (!obj->data.execi.p_timed_thread) {
+                       NORM_ERR("Error creating texeci timed thread");
+               }
+               /*
+                * note that we don't register this thread with the
+                * timed_thread list, because we destroy it manually
+                */
+               if (timed_thread_run(obj->data.execi.p_timed_thread)) {
+                       NORM_ERR("Error running texeci timed thread");
+               }
+       } else {
+               timed_thread_lock(obj->data.execi.p_timed_thread);
+               snprintf(p, p_max_size, "%s", obj->data.execi.buffer);
+               timed_thread_unlock(obj->data.execi.p_timed_thread);
+       }
+}
+
+#ifdef X11
+void print_execgauge(struct text_object *obj, char *p, int p_max_size)
+{
+       double barnum;
+
+       read_exec(obj->data.s, p, p_max_size);
+       barnum = get_barnum(p); /*using the same function*/
+
+       if (barnum >= 0.0) {
+               barnum /= 100;
+               new_gauge(p, obj->a, obj->b, round_to_int(barnum * 255.0));
+       }
+}
+
+void print_execgraph(struct text_object *obj, char *p, int p_max_size)
+{
+       char showaslog = FALSE;
+       char tempgrad = FALSE;
+       double barnum;
+       char *cmd = obj->data.execi.cmd;
+
+       if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
+               tempgrad = TRUE;
+               cmd += strlen(" "TEMPGRAD);
+       }
+       if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
+               showaslog = TRUE;
+               cmd += strlen(" "LOGGRAPH);
+       }
+       read_exec(cmd, p, p_max_size);
+       barnum = get_barnum(p);
+
+       if (barnum > 0) {
+               new_graph(p, obj->a, obj->b, obj->c, obj->d, round_to_int(barnum),
+                               100, 1, showaslog, tempgrad);
+       }
+}
+
+void print_execigraph(struct text_object *obj, char *p, int p_max_size)
+{
+       if (time_to_update(obj)) {
+               double barnum;
+               char showaslog = FALSE;
+               char tempgrad = FALSE;
+               char *cmd = obj->data.execi.cmd;
+
+               if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
+                       tempgrad = TRUE;
+                       cmd += strlen(" "TEMPGRAD);
+               }
+               if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
+                       showaslog = TRUE;
+                       cmd += strlen(" "LOGGRAPH);
+               }
+               obj->char_a = showaslog;
+               obj->char_b = tempgrad;
+               read_exec(cmd, p, p_max_size);
+               barnum = get_barnum(p);
+
+               if (barnum >= 0.0) {
+                       obj->f = barnum;
+               }
+               obj->data.execi.last_update = current_update_time;
+       }
+       new_graph(p, obj->a, obj->b, obj->c, obj->d, (int) (obj->f), 100, 1, obj->char_a, obj->char_b);
+}
+
+void print_execigauge(struct text_object *obj, char *p, int p_max_size)
+{
+       if (time_to_update(obj)) {
+               double barnum;
+
+               read_exec(obj->data.execi.cmd, p, p_max_size);
+               barnum = get_barnum(p);
+
+               if (barnum >= 0.0) {
+                       obj->f = 255 * barnum / 100.0;
+               }
+               obj->data.execi.last_update = current_update_time;
+       }
+       new_gauge(p, obj->a, obj->b, round_to_int(obj->f));
+}
+#endif /* X11 */
+
+void print_execbar(struct text_object *obj, char *p, int p_max_size)
+{
+       double barnum;
+       read_exec(obj->data.s, p, p_max_size);
+       barnum = get_barnum(p);
+
+       if (barnum >= 0.0) {
+#ifdef X11
+               if(output_methods & TO_X) {
+                       barnum /= 100;
+                       new_bar(p, obj->a, obj->b, round_to_int(barnum * 255.0));
+               }else
+#endif /* X11 */
+               {
+                       if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
+                       new_bar_in_shell(p, p_max_size, barnum, obj->a);
+               }
+       }
+}
+
+void print_execibar(struct text_object *obj, char *p, int p_max_size)
+{
+       double barnum;
+
+       if (time_to_update(obj)) {
+               read_exec(obj->data.execi.cmd, p, p_max_size);
+               barnum = get_barnum(p);
+
+               if (barnum >= 0.0) {
+                       obj->f = barnum;
+               }
+               obj->data.execi.last_update = current_update_time;
+       }
+#ifdef X11
+       if(output_methods & TO_X) {
+               new_bar(p, obj->a, obj->b, round_to_int(obj->f * 2.55));
+       } else
+#endif /* X11 */
+       {
+               if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
+               new_bar_in_shell(p, p_max_size, round_to_int(obj->f), obj->a);
+       }
+}
+
+void free_exec(struct text_object *obj)
+{
+       if (obj->data.s) {
+               free(obj->data.s);
+               obj->data.s = NULL;
+       }
+}
+
+void free_execi(struct text_object *obj)
+{
+       if (obj->data.execi.p_timed_thread)
+               timed_thread_destroy(obj->data.execi.p_timed_thread, &obj->data.execi.p_timed_thread);
+       if (obj->data.execi.cmd)
+               free(obj->data.execi.cmd);
+       if (obj->data.execi.buffer)
+               free(obj->data.execi.buffer);
+       memset(&obj->data.execi, 0, sizeof(obj->data.execi));
+}
diff --git a/src/exec.h b/src/exec.h
new file mode 100644 (file)
index 0000000..1a17477
--- /dev/null
@@ -0,0 +1,54 @@
+/* -*- mode: c; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: t -*-
+ * vim: ts=4 sw=4 noet ai cindent syntax=c
+ *
+ * Conky, a system monitor, based on torsmo
+ *
+ * Any original torsmo code is licensed under the BSD license
+ *
+ * All code written since the fork of torsmo is licensed under the GPL
+ *
+ * Please see COPYING for details
+ *
+ * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
+ * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
+ *   (see AUTHORS)
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef _EXEC_H
+#define _EXEC_H
+
+extern pid_t childpid;
+
+void scan_exec_arg(struct text_object *, const char *);
+void scan_pre_exec_arg(struct text_object *, const char *);
+void scan_execi_arg(struct text_object *, const char *);
+void print_exec(struct text_object *, char *, int);
+void print_execp(struct text_object *, char *, int);
+void print_execi(struct text_object *, char *, int);
+void print_execpi(struct text_object *, char *);
+void print_texeci(struct text_object *, char *, int);
+#ifdef X11
+void print_execgauge(struct text_object *, char *, int);
+void print_execgraph(struct text_object *, char *, int);
+void print_execigraph(struct text_object *, char *, int);
+void print_execigauge(struct text_object *, char *, int);
+#endif /* X11 */
+void print_execbar(struct text_object *, char *, int);
+void print_execibar(struct text_object *, char *, int);
+void free_exec(struct text_object *);
+void free_execi(struct text_object *);
+#endif /* _EXEC_H */
index b9c96fb..2bc3669 100644 (file)
@@ -511,15 +511,8 @@ struct text_object {
                        char *cmd;
                        char *buffer;
                        double data;
-               } execi;                /* 5 */
-
-               struct {
-                       float interval;
-                       char *cmd;
-                       char *buffer;
-                       double data;
                        timed_thread *p_timed_thread;
-               } texeci;
+               } execi;                /* 5 */
 
                struct {
                        int a, b;