1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-09-30 05:59:07 +00:00

exec: put all exec-related stuff into it's own file

While here, also merge the execi and texeci fields of struct
text_object, so more common code can be shared in between.
This commit is contained in:
Phil Sutter 2009-10-15 21:51:21 +02:00
parent 2631f42820
commit 7a168c377b
7 changed files with 552 additions and 436 deletions

View File

@ -50,11 +50,11 @@ endif # BUILD_CONFIG_OUTPUT
# source files always needed for compiling # source files always needed for compiling
mandatory_sources = colours.c colours.h common.c common.h conky.c conky.h \ 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 \ core.c core.h diskio.c diskio.h exec.c exec.h fs.c fs.h logging.h \
mixer.c mixer.h template.c template.h timed_thread.c timed_thread.h \ mail.c mail.h mixer.c mixer.h template.c template.h timed_thread.c \
mboxscan.c mboxscan.h specials.c specials.h tailhead.c tailhead.h \ timed_thread.h mboxscan.c mboxscan.h specials.c specials.h tailhead.c \
temphelper.c temphelper.h text_object.c text_object.h timeinfo.c \ tailhead.h temphelper.c temphelper.h text_object.c text_object.h \
timeinfo.h algebra.c algebra.h timeinfo.c timeinfo.h algebra.c algebra.h
# source files only needed when the apropriate option is enabled # source files only needed when the apropriate option is enabled
audacious = audacious.c audacious.h audacious = audacious.c audacious.h

View File

@ -76,6 +76,7 @@
#include "build.h" #include "build.h"
#include "colours.h" #include "colours.h"
#include "diskio.h" #include "diskio.h"
#include "exec.h"
#ifdef X11 #ifdef X11
#include "fonts.h" #include "fonts.h"
#endif #endif
@ -163,7 +164,6 @@ double update_interval;
double update_interval_old; double update_interval_old;
double update_interval_bat; double update_interval_bat;
void *global_cpu = NULL; void *global_cpu = NULL;
pid_t childpid = 0;
int argc_copy; int argc_copy;
char** argv_copy; char** argv_copy;
@ -631,107 +631,6 @@ static void human_readable(long long num, char *buf, int size)
/* global object list root element */ /* global object list root element */
static struct text_object global_root_object; 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; static long current_text_color;
void set_current_text_color(long colour) 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); 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, static inline void format_media_player_time(char *buf, const int size,
int seconds) 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 /* substitutes all occurrences of '\n' with SECRIT_MULTILINE_CHAR, which allows
* multiline objects like $exec work with $align[rc] and friends * 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); evaluate(obj->data.s, p);
} }
OBJ(exec) { OBJ(exec) {
read_exec(obj->data.s, p, text_buffer_size); print_exec(obj, p, p_max_size);
remove_deleted_chars(p);
} }
OBJ(execp) { OBJ(execp) {
struct information *tmp_info; print_execp(obj, p, p_max_size);
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);
} }
#ifdef X11 #ifdef X11
OBJ(execgauge) { OBJ(execgauge) {
double barnum; print_execgauge(obj, p, p_max_size);
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));
}
} }
#endif /* X11 */ #endif /* X11 */
OBJ(execbar) { OBJ(execbar) {
double barnum; print_execbar(obj, p, p_max_size);
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 */
}
} }
#ifdef X11 #ifdef X11
OBJ(execgraph) { OBJ(execgraph) {
char showaslog = FALSE; print_execgraph(obj, p, p_max_size);
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);
}
} }
#endif /* X11 */ #endif /* X11 */
OBJ(execibar) { OBJ(execibar) {
if (current_update_time - obj->data.execi.last_update print_execibar(obj, p, p_max_size);
>= 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 */
} }
#ifdef X11 #ifdef X11
OBJ(execigraph) { OBJ(execigraph) {
if (current_update_time - obj->data.execi.last_update print_execigraph(obj, p, p_max_size);
>= 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);
} }
OBJ(execigauge) { OBJ(execigauge) {
if (current_update_time - obj->data.execi.last_update print_execigauge(obj, p, p_max_size);
>= 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));
} }
#endif /* X11 */ #endif /* X11 */
OBJ(execi) { OBJ(execi) {
if (current_update_time - obj->data.execi.last_update print_execi(obj, p, p_max_size);
>= 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);
} }
OBJ(execpi) { OBJ(execpi) {
struct text_object subroot; print_execpi(obj, p);
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);
} }
OBJ(texeci) { OBJ(texeci) {
if (!obj->data.texeci.p_timed_thread) { print_texeci(obj, p, p_max_size);
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);
}
} }
OBJ(imap_unseen) { OBJ(imap_unseen) {
struct mail_s *mail = ensure_mail_thread(obj, imap_thread, "imap"); struct mail_s *mail = ensure_mail_thread(obj, imap_thread, "imap");

View File

@ -336,9 +336,6 @@ extern unsigned int max_user_text;
/* path to config file */ /* path to config file */
extern char *current_config; 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 #ifdef X11
#define TO_X 1 #define TO_X 1
#endif /* X11 */ #endif /* X11 */
@ -367,4 +364,7 @@ void set_update_interval(double interval);
#define UNUSED(a) (void)a #define UNUSED(a) (void)a
#define UNUSED_ATTR __attribute__ ((unused)) #define UNUSED_ATTR __attribute__ ((unused))
void parse_conky_vars(struct text_object *, const char *,
char *, struct information *);
#endif /* _conky_h_ */ #endif /* _conky_h_ */

View File

@ -35,6 +35,7 @@
#include "build.h" #include "build.h"
#include "colours.h" #include "colours.h"
#include "diskio.h" #include "diskio.h"
#include "exec.h"
#ifdef X11 #ifdef X11
#include "fonts.h" #include "fonts.h"
#endif #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); obj->data.s = strndup(arg ? arg : "", text_buffer_size);
#endif /* IMLIB2 */ #endif /* IMLIB2 */
END OBJ(exec, 0) END OBJ(exec, 0)
obj->data.s = strndup(arg ? arg : "", text_buffer_size); scan_exec_arg(obj, arg);
END OBJ(execp, 0) END OBJ(execp, 0)
obj->data.s = strndup(arg ? arg : "", text_buffer_size); scan_exec_arg(obj, arg);
END OBJ(execbar, 0) END OBJ(execbar, 0)
SIZE_DEFAULTS(bar); SIZE_DEFAULTS(bar);
obj->data.s = strndup(arg ? arg : "", text_buffer_size); scan_exec_arg(obj, arg);
#ifdef X11 #ifdef X11
END OBJ(execgauge, 0) END OBJ(execgauge, 0)
SIZE_DEFAULTS(gauge); SIZE_DEFAULTS(gauge);
obj->data.s = strndup(arg ? arg : "", text_buffer_size); scan_exec_arg(obj, arg);
END OBJ(execgraph, 0) END OBJ(execgraph, 0)
SIZE_DEFAULTS(graph); SIZE_DEFAULTS(graph);
obj->data.s = strndup(arg ? arg : "", text_buffer_size); scan_exec_arg(obj, arg);
#endif /* X11 */ #endif /* X11 */
END OBJ(execibar, 0) END OBJ_ARG(execibar, 0, "execibar needs arguments")
int n;
SIZE_DEFAULTS(bar); SIZE_DEFAULTS(bar);
scan_execi_arg(obj, arg);
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);
}
#ifdef X11 #ifdef X11
END OBJ(execigraph, 0) END OBJ_ARG(execigraph, 0, "execigraph needs arguments")
int n;
SIZE_DEFAULTS(graph); SIZE_DEFAULTS(graph);
scan_execi_arg(obj, arg);
if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) { END OBJ_ARG(execigauge, 0, "execigauge needs arguments")
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;
SIZE_DEFAULTS(gauge); SIZE_DEFAULTS(gauge);
scan_execi_arg(obj, arg);
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);
}
#endif /* X11 */ #endif /* X11 */
END OBJ(execi, 0) END OBJ_ARG(execi, 0, "execi needs arguments")
int n; scan_execi_arg(obj, arg);
END OBJ_ARG(execpi, 0, "execpi needs arguments")
if (!arg || sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) { scan_execi_arg(obj, arg);
char buf[256]; END OBJ_ARG(texeci, 0, "texeci needs arguments")
scan_execi_arg(obj, arg);
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(pre_exec, 0) END OBJ(pre_exec, 0)
obj->type = OBJ_text; scan_pre_exec_arg(obj, arg);
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);
}
END OBJ(fs_bar, &update_fs_stats) END OBJ(fs_bar, &update_fs_stats)
init_fs_bar(obj, arg); init_fs_bar(obj, arg);
END OBJ(fs_bar_free, &update_fs_stats) 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: case OBJ_execgraph:
#endif #endif
case OBJ_execp: case OBJ_execp:
free(data.s); free_exec(obj);
break; break;
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
case OBJ_iconv_start: case OBJ_iconv_start:
@ -2118,17 +2041,12 @@ void free_text_objects(struct text_object *root, int internal)
case OBJ_execpi: case OBJ_execpi:
case OBJ_execi: case OBJ_execi:
case OBJ_execibar: case OBJ_execibar:
case OBJ_texeci:
#ifdef X11 #ifdef X11
case OBJ_execigraph: case OBJ_execigraph:
case OBJ_execigauge: case OBJ_execigauge:
#endif /* X11 */ #endif /* X11 */
free(data.execi.cmd); free_execi(obj);
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);
break; break;
case OBJ_nameserver: case OBJ_nameserver:
free_dns_data(); free_dns_data();

455
src/exec.c Normal file
View File

@ -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));
}

54
src/exec.h Normal file
View File

@ -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 */

View File

@ -511,15 +511,8 @@ struct text_object {
char *cmd; char *cmd;
char *buffer; char *buffer;
double data; double data;
} execi; /* 5 */
struct {
float interval;
char *cmd;
char *buffer;
double data;
timed_thread *p_timed_thread; timed_thread *p_timed_thread;
} texeci; } execi; /* 5 */
struct { struct {
int a, b; int a, b;