changes related to temperature and layout
[monky] / src / tailhead.c
index ab89d48..f0488cb 100644 (file)
@@ -1,4 +1,7 @@
-/* Conky, a system monitor, based on torsmo
+/* -*- 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
  *
@@ -7,7 +10,7 @@
  * Please see COPYING for details
  *
  * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
- * Copyright (c) 2005-2008 Brenden Matthews, Philip Kovacs, et. al.
+ * Copyright (c) 2005-2010 Brenden Matthews, Philip Kovacs, et. al.
  *     (see AUTHORS)
  * All rights reserved.
  *
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#include "config.h"
-#include "conky.h"
-#include "logging.h"
-#include "tailhead.h"
-#include "text_object.h"
 
-#include <errno.h>
+#define _GNU_SOURCE
+#include "common.h"
+#include "text_object.h"
+#include "logging.h"
+#include <sys/stat.h>
+#include <ctype.h>
 #include <fcntl.h>
+#include <string.h>
 #include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-int init_tailhead_object(enum tailhead_type type,
-               struct text_object *obj, const char *arg)
-{
-       char buf[64];
-       int n1, n2;
-       struct stat st;
-       FILE *fp = NULL;
-       int fd;
-       int numargs;
-       const char *me;
-
-       /* FIXME: use #define for that */
-       me = (type == TAIL) ? "tail" : "head";
-
-       if (!arg) {
-               ERR("%s needs arguments", me);
-               return 1;
-       }
-
-       numargs = sscanf(arg, "%63s %i %i", buf, &n1, &n2);
-
-       if (numargs < 2 || numargs > 3) {
-               ERR("incorrect number of arguments given to %s object", me);
-               return 1;
-       }
-
-       if (n1 < 1 || n1 > MAX_TAIL_LINES) {
-               ERR("invalid arg for %s, number of lines must be "
-                               "between 1 and %i", me, MAX_TAIL_LINES);
-               return 1;
-       }
 
-       obj->data.tail.fd = -1;
-
-       if (type == HEAD) {
-               goto NO_FIFO;
-       }
-       if (stat(buf, &st) == 0) {
-               if (S_ISFIFO(st.st_mode)) {
-                       fd = open(buf, O_RDONLY | O_NONBLOCK);
-
-                       if (fd == -1) {
-                               ERR("%s logfile does not exist, or you do "
-                                   "not have correct permissions", me);
-                               return 1;
+#define MAX_HEADTAIL_LINES 30
+#define DEFAULT_MAX_HEADTAIL_USES 2
+
+struct headtail {
+       int wantedlines;
+       char *logfile;
+       char *buffer;
+       int current_use;
+       int max_uses;
+       int reported;
+};
+
+static void tailstring(char *string, int endofstring, int wantedlines) {
+       int i, linescounted = 0;
+
+       string[endofstring] = 0;
+       if(endofstring > 0) {
+               if(string[endofstring-1] == '\n') {     //work with or without \n at end of file
+                       string[endofstring-1] = 0;
+               }
+               for(i = endofstring - 1; i >= 0 && linescounted < wantedlines; i--) {
+                       if(string[i] == '\n') {
+                               linescounted++;
                        }
-
-                       obj->data.tail.fd = fd;
-               } else {
-NO_FIFO:
-                       fp = fopen(buf, "r");
                }
-       }
-
-       if (fp || obj->data.tail.fd != -1) {
-               obj->data.tail.logfile = malloc(text_buffer_size);
-               strcpy(obj->data.tail.logfile, buf);
-               obj->data.tail.wantedlines = n1;
-               obj->data.tail.interval = update_interval * 2;
-
-               if (obj->data.tail.fd == -1) {
-                       fclose(fp);
+               if(i > 0) {
+                       strfold(string, i+2);
                }
-       } else {
-               // fclose(fp);
-               ERR("%s logfile does not exist, or you do not have "
-                               "correct permissions", me);
-               return 1;
-       }
-       /* XXX: the following implies update_interval >= 1 ?! */
-       if (numargs == 3 && (n2 < 1 || n2 < update_interval)) {
-               ERR("%s interval must be greater than "
-                   "0 and "PACKAGE_NAME"'s interval, ignoring", me);
-       } else if (numargs == 3) {
-                       obj->data.tail.interval = n2;
        }
-       /* asumming all else worked */
-       obj->data.tail.buffer = malloc(text_buffer_size * 20);
-       return 0;
 }
 
-/* Allows reading from a FIFO (i.e., /dev/xconsole).
- * The file descriptor is set to non-blocking which makes this possible.
- *
- * FIXME: Since lseek cannot seek a file descriptor long lines will break. */
-static void tail_pipe(struct text_object *obj, char *dst, size_t dst_size)
+void free_tailhead(struct text_object *obj)
 {
-#define TAIL_PIPE_BUFSIZE      4096
-       int lines = 0;
-       int line_len = 0;
-       int last_line = 0;
-       int fd = obj->data.tail.fd;
+       struct headtail *ht = obj->data.opaque;
+       if (!ht)
+               return;
+       if (ht->logfile)
+               free(ht->logfile);
+       if (ht->buffer)
+               free(ht->buffer);
+       free(obj->data.opaque);
+       obj->data.opaque = NULL;
+}
 
-       while (1) {
-               char buf[TAIL_PIPE_BUFSIZE];
-               ssize_t len = read(fd, buf, sizeof(buf));
-               int i;
+void init_tailhead(const char* type, const char* arg, struct text_object *obj, void* free_at_crash) {
+       unsigned int args;
+       struct headtail *ht;
 
-               if (len == -1) {
-                       if (errno != EAGAIN) {
-                               strcpy(obj->data.tail.buffer, "Logfile Read Error");
-                               snprintf(dst, dst_size, "Logfile Read Error");
-                       }
+       ht = malloc(sizeof(struct headtail));
+       memset(ht, 0, sizeof(struct headtail));
 
-                       break;
-               } else if (len == 0) {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(dst, dst_size, "Logfile Empty");
-                       break;
-               }
+       ht->logfile = malloc(DEFAULT_TEXT_BUFFER_SIZE);
+       memset(ht->logfile, 0, DEFAULT_TEXT_BUFFER_SIZE);
 
-               for (line_len = 0, i = 0; i < len; i++) {
-                       int pos = 0;
-                       char *p;
+       ht->max_uses = DEFAULT_MAX_HEADTAIL_USES;
 
-                       if (buf[i] == '\n') {
-                               lines++;
+       args = sscanf(arg, "%s %d %d", ht->logfile, &ht->wantedlines, &ht->max_uses);
+       if (args < 2 || args > 3) {
+               free_tailhead(obj);
+               CRIT_ERR(obj, free_at_crash, "%s needs a file as 1st and a number of lines as 2nd argument", type);
+       }
+       if (ht->max_uses < 1) {
+               free_tailhead(obj);
+               CRIT_ERR(obj, free_at_crash, "invalid arg for %s, next_check must be larger than 0", type);
+       }
+       if (ht->wantedlines > 0 && ht->wantedlines <= MAX_HEADTAIL_LINES) {
+               to_real_path(ht->logfile, ht->logfile);
+               ht->buffer = NULL;
+               ht->current_use = 0;
+       } else {
+               free_tailhead(obj);
+               CRIT_ERR(obj, free_at_crash, "invalid arg for %s, number of lines must be between 1 and %d", type, MAX_HEADTAIL_LINES);
+       }
+       obj->data.opaque = ht;
+}
 
-                               if (obj->data.tail.readlines > 0) {
-                                       int n;
-                                       int olines = 0;
-                                       int first_line = 0;
+void print_tailhead(const char* type, struct text_object *obj, char *p, int p_max_size) {
+       int fd, i, endofstring = 0, linescounted = 0;
+       FILE *fp;
+       struct stat st;
+       struct headtail *ht = obj->data.opaque;
 
-                                       for (n = 0; obj->data.tail.buffer[n]; n++) {
-                                               if (obj->data.tail.buffer[n] == '\n') {
-                                                       if (!first_line) {
-                                                               first_line = n + 1;
-                                                       }
+       if (!ht)
+               return;
 
-                                                       if (++olines < obj->data.tail.wantedlines) {
-                                                               pos = n + 1;
-                                                               continue;
+       //empty the buffer and reset the counter if we used it the max number of times
+       if(ht->buffer && ht->current_use >= ht->max_uses - 1) {
+               free(ht->buffer);
+               ht->buffer = NULL;
+               ht->current_use = 0;
+       }
+       //use the buffer if possible
+       if(ht->buffer) {
+               strcpy(p, ht->buffer);
+               ht->current_use++;
+       }else{  //otherwise find the needed data
+               if(stat(ht->logfile, &st) == 0) {
+                       if (S_ISFIFO(st.st_mode)) {
+                               fd = open_fifo(ht->logfile, &ht->reported);
+                               if(fd != -1) {
+                                       if(strcmp(type, "head") == 0) {
+                                               for(i = 0; linescounted < ht->wantedlines; i++) {
+                                                       read(fd, p + i, 1);
+                                                       if(p[i] == '\n') {
+                                                               linescounted++;
                                                        }
-
-                                                       n++;
-                                                       p = obj->data.tail.buffer + first_line;
-                                                       pos = n - first_line;
-                                                       memmove(obj->data.tail.buffer,
-                                                                       obj->data.tail.buffer + first_line, strlen(p));
-                                                       obj->data.tail.buffer[pos] = 0;
-                                                       break;
                                                }
+                                               p[i] = 0;
+                                       } else if(strcmp(type, "tail") == 0) {
+                                               i = read(fd, p, p_max_size - 1);
+                                               tailstring(p, i, ht->wantedlines);
+                                       } else {
+                                               CRIT_ERR(NULL, NULL, "If you are seeing this then there is a bug in the code, report it !");
                                        }
                                }
-
-                               p = buf + last_line;
-                               line_len++;
-                               memcpy(&(obj->data.tail.buffer[pos]), p, line_len);
-                               obj->data.tail.buffer[pos + line_len] = 0;
-                               last_line = i + 1;
-                               line_len = 0;
-                               obj->data.tail.readlines = lines;
-                               continue;
-                       }
-
-                       line_len++;
-               }
-       }
-
-       snprintf(dst, dst_size, "%s", obj->data.tail.buffer);
-}
-
-static long rev_fcharfind(FILE *fp, char val, unsigned int step)
-{
-#define BUFSZ 0x1000
-       long ret = -1;
-       unsigned int count = 0;
-       static char buf[BUFSZ];
-       long orig_pos = ftell(fp);
-       long buf_pos = -1;
-       long file_pos = orig_pos;
-       long buf_size = BUFSZ;
-       char *cur_found;
-
-       while (count < step) {
-               if (buf_pos <= 0) {
-                       if (file_pos > BUFSZ) {
-                               fseek(fp, file_pos - BUFSZ, SEEK_SET);
+                               close(fd);
                        } else {
-                               buf_size = file_pos;
-                               fseek(fp, 0, SEEK_SET);
+                               fp = open_file(ht->logfile, &ht->reported);
+                               if(fp != NULL) {
+                                       if(strcmp(type, "head") == 0) {
+                                               for(i = 0; i < ht->wantedlines; i++) {
+                                                       fgets(p + endofstring, p_max_size - endofstring, fp);
+                                                       endofstring = strlen(p);
+                                               }
+                                       } else if(strcmp(type, "tail") == 0) {
+                                               fseek(fp, - p_max_size, SEEK_END);
+                                               i = fread(p, 1, p_max_size - 1, fp);
+                                               tailstring(p, i, ht->wantedlines);
+                                       } else {
+                                               CRIT_ERR(NULL, NULL, "If you are seeing this then there is a bug in the code, report it !");
+                                       }
+                                       fclose(fp);
+                               }
                        }
-                       file_pos = ftell(fp);
-                       buf_pos = fread(buf, 1, buf_size, fp);
-               }
-               cur_found = memrchr(buf, val, (size_t) buf_pos);
-               if (cur_found != NULL) {
-                       buf_pos = cur_found - buf;
-                       count++;
+                       ht->buffer = strdup(p);
                } else {
-                       buf_pos = -1;
-                       if (file_pos == 0) {
-                               break;
-                       }
+                       CRIT_ERR(NULL, NULL, "$%s can't find information about %s", type, ht->logfile);
                }
        }
-       fseek(fp, orig_pos, SEEK_SET);
-       if (count == step) {
-               ret = file_pos + buf_pos;
-       }
-       return ret;
+       return;
 }
 
-int print_tail_object(struct text_object *obj, char *p, size_t p_max_size)
-{
-       FILE *fp;
-       long nl = 0, bsize;
-       int iter;
-
-       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
-               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
-               return 0;
-       }
-
-       obj->data.tail.last_update = current_update_time;
+/* FIXME: use something more general (see also tail.c, head.c */
+#define BUFSZ 0x1000
 
-       if (obj->data.tail.fd != -1) {
-               tail_pipe(obj, p, p_max_size);
-               return 0;
+void print_lines(struct text_object *obj, char *p, int p_max_size)
+{
+       static int rep = 0;
+       FILE *fp = open_file(obj->data.s, &rep);
+       char buf[BUFSZ];
+       int j, lines;
+
+       if (!fp) {
+               snprintf(p, p_max_size, "File Unreadable");
+               return;
        }
 
-       fp = fopen(obj->data.tail.logfile, "rt");
-       if (fp == NULL) {
-               /* Send one message, but do not consistently spam
-                * on missing logfiles. */
-               if (obj->data.tail.readlines != 0) {
-                       ERR("tail logfile failed to open");
-                       strcpy(obj->data.tail.buffer, "Logfile Missing");
-               }
-               obj->data.tail.readlines = 0;
-               snprintf(p, p_max_size, "Logfile Missing");
-       } else {
-               obj->data.tail.readlines = 0;
-               /* -1 instead of 0 to avoid counting a trailing
-                * newline */
-               fseek(fp, -1, SEEK_END);
-               bsize = ftell(fp) + 1;
-               for (iter = obj->data.tail.wantedlines; iter > 0;
-                               iter--) {
-                       nl = rev_fcharfind(fp, '\n', iter);
-                       if (nl >= 0) {
-                               break;
+       lines = 0;
+       while(fgets(buf, BUFSZ, fp) != NULL){
+               for(j = 0; buf[j] != 0; j++) {
+                       if(buf[j] == '\n') {
+                               lines++;
                        }
                }
-               obj->data.tail.readlines = iter;
-               if (obj->data.tail.readlines
-                               < obj->data.tail.wantedlines) {
-                       fseek(fp, 0, SEEK_SET);
-               } else {
-                       fseek(fp, nl + 1, SEEK_SET);
-                       bsize -= ftell(fp);
-               }
-               /* Make sure bsize is at least 1 byte smaller than the
-                * buffer max size. */
-               if (bsize > (long) ((text_buffer_size * 20) - 1)) {
-                       fseek(fp, bsize - text_buffer_size * 20 - 1,
-                                       SEEK_CUR);
-                       bsize = text_buffer_size * 20 - 1;
-               }
-               bsize = fread(obj->data.tail.buffer, 1, bsize, fp);
-               fclose(fp);
-               if (bsize > 0) {
-                       /* Clean up trailing newline, make sure the
-                        * buffer is null terminated. */
-                       if (obj->data.tail.buffer[bsize - 1] == '\n') {
-                               obj->data.tail.buffer[bsize - 1] = '\0';
-                       } else {
-                               obj->data.tail.buffer[bsize] = '\0';
-                       }
-                       snprintf(p, p_max_size, "%s",
-                                       obj->data.tail.buffer);
-               } else {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(p, p_max_size, "Logfile Empty");
-               }       /* bsize > 0 */
-       }               /* fp == NULL */
-       return 0;
-}
-
-long fwd_fcharfind(FILE *fp, char val, unsigned int step)
-{
-#define BUFSZ 0x1000
-       long ret = -1;
-       unsigned int count = 0;
-       static char buf[BUFSZ];
-       long orig_pos = ftell(fp);
-       long buf_pos = -1;
-       long buf_size = BUFSZ;
-       char *cur_found = NULL;
-
-       while (count < step) {
-               if (cur_found == NULL) {
-                       buf_size = fread(buf, 1, buf_size, fp);
-                       buf_pos = 0;
-               }
-               cur_found = memchr(buf + buf_pos, val, buf_size - buf_pos);
-               if (cur_found != NULL) {
-                       buf_pos = cur_found - buf + 1;
-                       count++;
-               } else {
-                       if (feof(fp)) {
-                               break;
-                       }
-               }
-       }
-       if (count == step) {
-               ret = ftell(fp) - buf_size + buf_pos - 1;
-       }
-       fseek(fp, orig_pos, SEEK_SET);
-       return ret;
+       }
+       snprintf(p, p_max_size, "%d", lines);
+       fclose(fp);
 }
 
-int print_head_object(struct text_object *obj, char *p, size_t p_max_size)
+void print_words(struct text_object *obj, char *p, int p_max_size)
 {
-       FILE *fp;
-       long nl = 0;
-       int iter;
-
-       if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
-               snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
-               return 0;
+       static int rep = 0;
+       FILE *fp = open_file(obj->data.s, &rep);
+       char buf[BUFSZ];
+       int j, words;
+       char inword = 0;
+
+       if(!fp) {
+               snprintf(p, p_max_size, "File Unreadable");
+               return;
        }
 
-       obj->data.tail.last_update = current_update_time;
-
-       fp = fopen(obj->data.tail.logfile, "rt");
-       if (fp == NULL) {
-               /* Send one message, but do not consistently spam
-                * on missing logfiles. */
-               if (obj->data.tail.readlines != 0) {
-                       ERR("head logfile failed to open");
-                       strcpy(obj->data.tail.buffer, "Logfile Missing");
-               }
-               obj->data.tail.readlines = 0;
-               snprintf(p, p_max_size, "Logfile Missing");
-       } else {
-               obj->data.tail.readlines = 0;
-               for (iter = obj->data.tail.wantedlines; iter > 0;
-                               iter--) {
-                       nl = fwd_fcharfind(fp, '\n', iter);
-                       if (nl >= 0) {
-                               break;
-                       }
-               }
-               obj->data.tail.readlines = iter;
-               /* Make sure nl is at least 1 byte smaller than the
-                * buffer max size. */
-               if (nl > (long) ((text_buffer_size * 20) - 1)) {
-                       nl = text_buffer_size * 20 - 1;
-               }
-               nl = fread(obj->data.tail.buffer, 1, nl, fp);
-               fclose(fp);
-               if (nl > 0) {
-                       /* Clean up trailing newline, make sure the buffer
-                        * is null terminated. */
-                       if (obj->data.tail.buffer[nl - 1] == '\n') {
-                               obj->data.tail.buffer[nl - 1] = '\0';
+       words = 0;
+       while(fgets(buf, BUFSZ, fp) != NULL){
+               for(j = 0; buf[j] != 0; j++) {
+                       if(!isspace(buf[j])) {
+                               if(!inword) {
+                                       words++;
+                                       inword = 1;
+                               }
                        } else {
-                               obj->data.tail.buffer[nl] = '\0';
+                               inword = 0;
                        }
-                       snprintf(p, p_max_size, "%s",
-                                       obj->data.tail.buffer);
-               } else {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(p, p_max_size, "Logfile Empty");
-               }       /* nl > 0 */
-       }               /* if fp == null */
-       return 0;
+               }
+       }
+       snprintf(p, p_max_size, "%d", words);
+       fclose(fp);
 }