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
4 * Conky, a system monitor, based on torsmo
6 * Any original torsmo code is licensed under the BSD license
8 * All code written since the fork of torsmo is licensed under the GPL
10 * Please see COPYING for details
12 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
13 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
15 * All rights reserved.
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.
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/>.
35 #include "text_object.h"
37 #include <sys/types.h>
40 /* FIXME: this will probably not work, since the variable is being reused
41 * between different text objects. So when a process really hangs, it's PID
42 * will be overwritten at the next iteration. */
45 //our own implementation of popen, the difference : the value of 'childpid' will be filled with
46 //the pid of the running 'command'. This is useful if want to kill it when it hangs while reading
47 //or writing to it. We have to kill it because pclose will wait until the process dies by itself
48 static FILE* pid_popen(const char *command, const char *mode, pid_t *child) {
50 int parentend, childend;
52 //by running pipe after the strcmp's we make sure that we don't have to create a pipe
53 //and close the ends if mode is something illegal
54 if(strcmp(mode, "r") == 0) {
60 } else if(strcmp(mode, "w") == 0) {
74 } else if(*child > 0) {
76 waitpid(*child, NULL, 0);
78 //don't read from both stdin and pipe or write to both stdout and pipe
79 if(childend == ends[0]) {
84 dup(childend); //by dupping childend, the returned fd will have close-on-exec turned off
85 execl("/bin/sh", "sh", "-c", command, (char *) NULL);
86 _exit(EXIT_FAILURE); //child should die here, (normally execl will take care of this but it can fail)
88 return fdopen(parentend, mode);
91 //remove backspaced chars, example: "dog^H^H^Hcat" becomes "cat"
92 //string has to end with \0 and it's length should fit in a int
94 static void remove_deleted_chars(char *string){
96 while(string[i] != 0){
97 if(string[i] == BACKSPACE){
99 strcpy( &(string[i-1]), &(string[i+1]) );
101 }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
106 static inline double get_barnum(char *buf)
118 if (sscanf(buf, "%lf", &barnum) == 0) {
119 NORM_ERR("reading exec value failed (perhaps it's not the "
123 if (barnum > 100.0 || barnum < 0.0) {
124 NORM_ERR("your exec value is not between 0 and 100, "
125 "therefore it will be ignored");
131 static inline void read_exec(const char *data, char *buf, const int size)
135 memset(buf, 0, size);
140 alarm(update_interval);
141 fp = pid_popen(data, "r", &childpid);
145 length = fread(buf, 1, size, fp);
148 if (length > 0 && buf[length - 1] == '\n') {
149 buf[length - 1] = '\0';
157 static void *threaded_exec(void *) __attribute__((noreturn));
159 static void *threaded_exec(void *arg)
162 struct text_object *obj = arg;
165 buff = malloc(text_buffer_size);
166 read_exec(obj->data.execi.cmd, buff, text_buffer_size);
174 timed_thread_lock(obj->data.execi.p_timed_thread);
175 if (obj->data.execi.buffer)
176 free(obj->data.execi.buffer);
177 obj->data.execi.buffer = buff;
178 timed_thread_unlock(obj->data.execi.p_timed_thread);
179 if (timed_thread_test(obj->data.execi.p_timed_thread, 0)) {
180 timed_thread_exit(obj->data.execi.p_timed_thread);
186 /* check the execi fields and return true if the given interval has passed */
187 static int time_to_update(struct text_object *obj)
189 if (!obj->data.execi.interval)
191 if (current_update_time - obj->data.execi.last_update >= obj->data.execi.interval)
196 void scan_exec_arg(struct text_object *obj, const char *arg)
198 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
201 void scan_pre_exec_arg(struct text_object *obj, const char *arg)
205 obj->type = OBJ_text;
206 read_exec(arg, buf, sizeof(buf));
207 obj->data.s = strndup(buf, text_buffer_size);
210 void scan_execi_arg(struct text_object *obj, const char *arg)
214 if (sscanf(arg, "%f %n", &obj->data.execi.interval, &n) <= 0) {
215 NORM_ERR("${execi* <interval> command}");
218 obj->data.execi.cmd = strndup(arg + n, text_buffer_size);
221 void print_exec(struct text_object *obj, char *p, int p_max_size)
223 read_exec(obj->data.s, p, p_max_size);
224 remove_deleted_chars(p);
227 void print_execp(struct text_object *obj, char *p, int p_max_size)
229 struct information *tmp_info;
230 struct text_object subroot;
232 read_exec(obj->data.s, p, p_max_size);
234 tmp_info = malloc(sizeof(struct information));
235 memcpy(tmp_info, &info, sizeof(struct information));
236 parse_conky_vars(&subroot, p, p, tmp_info);
238 free_text_objects(&subroot, 1);
242 void print_execi(struct text_object *obj, char *p, int p_max_size)
244 if (time_to_update(obj)) {
245 if (!obj->data.execi.buffer)
246 obj->data.execi.buffer = malloc(text_buffer_size);
247 read_exec(obj->data.execi.cmd, obj->data.execi.buffer, text_buffer_size);
248 obj->data.execi.last_update = current_update_time;
250 snprintf(p, p_max_size, "%s", obj->data.execi.buffer);
253 void print_execpi(struct text_object *obj, char *p)
255 struct text_object subroot;
256 struct information *tmp_info;
258 tmp_info = malloc(sizeof(struct information));
259 memcpy(tmp_info, &info, sizeof(struct information));
261 if (!time_to_update(obj)) {
262 parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
266 FILE *fp = pid_popen(obj->data.execi.cmd, "r", &childpid);
268 if (!obj->data.execi.buffer)
269 obj->data.execi.buffer = malloc(text_buffer_size);
271 length = fread(obj->data.execi.buffer, 1, text_buffer_size, fp);
274 output = obj->data.execi.buffer;
275 output[length] = '\0';
276 if (length > 0 && output[length - 1] == '\n') {
277 output[length - 1] = '\0';
280 parse_conky_vars(&subroot, obj->data.execi.buffer, p, tmp_info);
281 obj->data.execi.last_update = current_update_time;
283 free_text_objects(&subroot, 1);
287 void print_texeci(struct text_object *obj, char *p, int p_max_size)
289 if (!obj->data.execi.p_timed_thread) {
290 obj->data.execi.p_timed_thread =
291 timed_thread_create(&threaded_exec,
292 (void *) obj, obj->data.execi.interval * 1000000);
293 if (!obj->data.execi.p_timed_thread) {
294 NORM_ERR("Error creating texeci timed thread");
297 * note that we don't register this thread with the
298 * timed_thread list, because we destroy it manually
300 if (timed_thread_run(obj->data.execi.p_timed_thread)) {
301 NORM_ERR("Error running texeci timed thread");
304 timed_thread_lock(obj->data.execi.p_timed_thread);
305 snprintf(p, p_max_size, "%s", obj->data.execi.buffer);
306 timed_thread_unlock(obj->data.execi.p_timed_thread);
311 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
315 read_exec(obj->data.s, p, p_max_size);
316 barnum = get_barnum(p); /*using the same function*/
320 new_gauge(p, obj->a, obj->b, round_to_int(barnum * 255.0));
324 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
326 char showaslog = FALSE;
327 char tempgrad = FALSE;
329 char *cmd = obj->data.execi.cmd;
331 if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
333 cmd += strlen(" "TEMPGRAD);
335 if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
337 cmd += strlen(" "LOGGRAPH);
339 read_exec(cmd, p, p_max_size);
340 barnum = get_barnum(p);
343 new_graph(p, obj->a, obj->b, obj->c, obj->d, round_to_int(barnum),
344 100, 1, showaslog, tempgrad);
348 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
350 if (time_to_update(obj)) {
352 char showaslog = FALSE;
353 char tempgrad = FALSE;
354 char *cmd = obj->data.execi.cmd;
356 if (strstr(cmd, " "TEMPGRAD) && strlen(cmd) > strlen(" "TEMPGRAD)) {
358 cmd += strlen(" "TEMPGRAD);
360 if (strstr(cmd, " "LOGGRAPH) && strlen(cmd) > strlen(" "LOGGRAPH)) {
362 cmd += strlen(" "LOGGRAPH);
364 obj->char_a = showaslog;
365 obj->char_b = tempgrad;
366 read_exec(cmd, p, p_max_size);
367 barnum = get_barnum(p);
372 obj->data.execi.last_update = current_update_time;
374 new_graph(p, obj->a, obj->b, obj->c, obj->d, (int) (obj->f), 100, 1, obj->char_a, obj->char_b);
377 void print_execigauge(struct text_object *obj, char *p, int p_max_size)
379 if (time_to_update(obj)) {
382 read_exec(obj->data.execi.cmd, p, p_max_size);
383 barnum = get_barnum(p);
386 obj->f = 255 * barnum / 100.0;
388 obj->data.execi.last_update = current_update_time;
390 new_gauge(p, obj->a, obj->b, round_to_int(obj->f));
394 void print_execbar(struct text_object *obj, char *p, int p_max_size)
397 read_exec(obj->data.s, p, p_max_size);
398 barnum = get_barnum(p);
402 if(output_methods & TO_X) {
404 new_bar(p, obj->a, obj->b, round_to_int(barnum * 255.0));
408 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
409 new_bar_in_shell(p, p_max_size, barnum, obj->a);
414 void print_execibar(struct text_object *obj, char *p, int p_max_size)
418 if (time_to_update(obj)) {
419 read_exec(obj->data.execi.cmd, p, p_max_size);
420 barnum = get_barnum(p);
425 obj->data.execi.last_update = current_update_time;
428 if(output_methods & TO_X) {
429 new_bar(p, obj->a, obj->b, round_to_int(obj->f * 2.55));
433 if(!obj->a) obj->a = DEFAULT_BAR_WIDTH_NO_X;
434 new_bar_in_shell(p, p_max_size, round_to_int(obj->f), obj->a);
438 void free_exec(struct text_object *obj)
446 void free_execi(struct text_object *obj)
448 if (obj->data.execi.p_timed_thread)
449 timed_thread_destroy(obj->data.execi.p_timed_thread, &obj->data.execi.p_timed_thread);
450 if (obj->data.execi.cmd)
451 free(obj->data.execi.cmd);
452 if (obj->data.execi.buffer)
453 free(obj->data.execi.buffer);
454 memset(&obj->data.execi, 0, sizeof(obj->data.execi));