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-2010 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"
36 #include "timed_thread.h"
38 #include <sys/types.h>
48 timed_thread *p_timed_thread;
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. */
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) {
62 int parentend, childend;
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) {
72 } else if(strcmp(mode, "w") == 0) {
86 } else if(*child > 0) {
88 waitpid(*child, NULL, 0);
90 //don't read from both stdin and pipe or write to both stdout and pipe
91 if(childend == ends[0]) {
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)
100 return fdopen(parentend, mode);
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
106 static void remove_deleted_chars(char *string){
108 while(string[i] != 0){
109 if(string[i] == BACKSPACE){
111 strcpy( &(string[i-1]), &(string[i+1]) );
113 }else strcpy( &(string[i]), &(string[i+1]) ); //necessary for ^H's at the start of a string
118 static inline double get_barnum(char *buf)
130 if (sscanf(buf, "%lf", &barnum) == 0) {
131 NORM_ERR("reading exec value failed (perhaps it's not the "
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");
143 static inline void read_exec(const char *data, char *buf, const int size, const
148 memset(buf, 0, size);
153 if (use_alarm) alarm(update_interval);
154 fp = pid_popen(data, "r", &childpid);
158 length = fread(buf, 1, size, fp);
161 if (length > 0 && buf[length - 1] == '\n') {
162 buf[length - 1] = '\0';
167 if (use_alarm) alarm(0);
170 static void *threaded_exec(void *) __attribute__((noreturn));
172 static void *threaded_exec(void *arg)
175 struct text_object *obj = arg;
176 struct execi_data *ed = obj->data.opaque;
179 buff = malloc(text_buffer_size);
180 read_exec(ed->cmd, buff, text_buffer_size, 0);
188 timed_thread_lock(ed->p_timed_thread);
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);
200 /* check the execi fields and return true if the given interval has passed */
201 static int time_to_update(struct execi_data *ed)
205 if (current_update_time - ed->last_update >= ed->interval)
210 void scan_exec_arg(struct text_object *obj, const char *arg)
212 obj->data.s = strndup(arg ? arg : "", text_buffer_size);
215 void scan_pre_exec_arg(struct text_object *obj, const char *arg)
219 obj->type = OBJ_text;
220 read_exec(arg, buf, sizeof(buf), 1);
221 obj->data.s = strndup(buf, text_buffer_size);
224 void scan_execi_arg(struct text_object *obj, const char *arg)
226 struct execi_data *ed;
229 ed = malloc(sizeof(struct execi_data));
230 memset(ed, 0, sizeof(struct execi_data));
232 if (sscanf(arg, "%f %n", &ed->interval, &n) <= 0) {
233 NORM_ERR("${execi* <interval> command}");
237 ed->cmd = strndup(arg + n, text_buffer_size);
238 obj->data.opaque = ed;
242 void scan_execgraph_arg(struct text_object *obj, const char *arg)
244 struct execi_data *ed;
247 ed = malloc(sizeof(struct execi_data));
248 memset(ed, 0, sizeof(struct execi_data));
250 buf = scan_graph(obj, arg, 100);
252 NORM_ERR("missing command argument to execgraph object");
256 obj->data.opaque = ed;
260 void print_exec(struct text_object *obj, char *p, int p_max_size)
262 read_exec(obj->data.s, p, p_max_size, 1);
263 remove_deleted_chars(p);
266 void print_execp(struct text_object *obj, char *p, int p_max_size)
268 struct information *tmp_info;
269 struct text_object subroot;
272 buf = malloc(text_buffer_size);
273 memset(buf, 0, text_buffer_size);
275 read_exec(obj->data.s, buf, text_buffer_size, 1);
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);
281 free_text_objects(&subroot, 1);
286 void print_execi(struct text_object *obj, char *p, int p_max_size)
288 struct execi_data *ed = obj->data.opaque;
293 if (time_to_update(ed)) {
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;
299 snprintf(p, p_max_size, "%s", ed->buffer);
302 void print_execpi(struct text_object *obj, char *p, int p_max_size)
304 struct execi_data *ed = obj->data.opaque;
305 struct text_object subroot;
306 struct information *tmp_info;
311 tmp_info = malloc(sizeof(struct information));
312 memcpy(tmp_info, &info, sizeof(struct information));
314 if (time_to_update(ed)) {
317 FILE *fp = pid_popen(ed->cmd, "r", &childpid);
320 ed->buffer = malloc(text_buffer_size);
322 length = fread(ed->buffer, 1, text_buffer_size, fp);
326 output[length] = '\0';
327 if (length > 0 && output[length - 1] == '\n') {
328 output[length - 1] = '\0';
331 ed->last_update = current_update_time;
333 parse_conky_vars(&subroot, ed->buffer, p, p_max_size, tmp_info);
334 free_text_objects(&subroot, 1);
338 void print_texeci(struct text_object *obj, char *p, int p_max_size)
340 struct execi_data *ed = obj->data.opaque;
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");
352 * note that we don't register this thread with the
353 * timed_thread list, because we destroy it manually
355 if (timed_thread_run(ed->p_timed_thread)) {
356 NORM_ERR("Error running texeci timed thread");
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);
365 void print_execgauge(struct text_object *obj, char *p, int p_max_size)
369 read_exec(obj->data.s, p, p_max_size, 1);
370 barnum = get_barnum(p); /*using the same function*/
374 new_gauge(obj, p, p_max_size, round_to_int(barnum * 255.0));
379 void print_execgraph(struct text_object *obj, char *p, int p_max_size)
382 struct execi_data *ed = obj->data.opaque;
387 read_exec(ed->cmd, p, p_max_size, 1);
388 barnum = get_barnum(p);
391 new_graph(obj, p, p_max_size, round_to_int(barnum));
395 void print_execigraph(struct text_object *obj, char *p, int p_max_size)
397 struct execi_data *ed = obj->data.opaque;
402 if (time_to_update(ed)) {
405 read_exec(ed->cmd, p, p_max_size, 1);
406 barnum = get_barnum(p);
411 ed->last_update = current_update_time;
413 new_graph(obj, p, p_max_size, (int) (ed->barnum));
417 void print_execigauge(struct text_object *obj, char *p, int p_max_size)
419 struct execi_data *ed = obj->data.opaque;
424 if (time_to_update(ed)) {
427 read_exec(ed->cmd, p, p_max_size, 1);
428 barnum = get_barnum(p);
431 ed->barnum = 255 * barnum / 100.0;
433 ed->last_update = current_update_time;
435 new_gauge(obj, p, p_max_size, round_to_int(ed->barnum));
438 void print_execbar(struct text_object *obj, char *p, int p_max_size)
441 read_exec(obj->data.s, p, p_max_size, 1);
442 barnum = get_barnum(p);
446 new_bar(obj, p, p_max_size, round_to_int(barnum * 255.0));
450 void print_execibar(struct text_object *obj, char *p, int p_max_size)
452 struct execi_data *ed = obj->data.opaque;
458 if (time_to_update(ed)) {
459 read_exec(ed->cmd, p, p_max_size, 1);
460 barnum = get_barnum(p);
465 ed->last_update = current_update_time;
467 new_bar(obj, p, p_max_size, round_to_int(ed->barnum * 2.55));
470 void free_exec(struct text_object *obj)
478 void free_execi(struct text_object *obj)
480 struct execi_data *ed = obj->data.opaque;
485 if (ed->p_timed_thread)
486 timed_thread_destroy(ed->p_timed_thread, &ed->p_timed_thread);
491 free(obj->data.opaque);
492 obj->data.opaque = NULL;