Fix bug with SF id 2808272 (tail and head)
authorNikolas Garofil <garo@dunaldi.garofil.be>
Tue, 21 Jul 2009 21:41:47 +0000 (23:41 +0200)
committerNikolas Garofil <garo@dunaldi.garofil.be>
Tue, 21 Jul 2009 21:41:47 +0000 (23:41 +0200)
To fix this bug the tail and head code was rewritten, everything should still
work except tailing a FIFO instead of a normal head.

doc/variables.xml
src/common.c
src/common.h
src/conky.c
src/tailhead.c
src/tailhead.h
src/text_object.h

index a026b11..fbdd5cb 100644 (file)
             <command>
                 <option>head</option>
             </command>
-            <option>logfile lines (interval)</option>
+            <option>logfile lines (next_check)</option>
         </term>
-        <listitem>Displays first N lines of supplied text text
-        file. If interval is not supplied, Conky assumes 2x Conky's
-        interval. Max of 30 lines can be displayed, or until the
-        text buffer is filled. 
+        <listitem>Displays first N lines of supplied text file. The
+        file is checked every 'next_check' update. If next_check
+        is not supplied, Conky defaults to 2. Max of 30 lines can be
+        displayed, or until the text buffer is filled.
         <para /></listitem>
     </varlistentry>
     <varlistentry>
             <command>
                 <option>tail</option>
             </command>
-            <option>logfile lines (interval)</option>
+            <option>logfile lines (next_check)</option>
         </term>
-        <listitem>Displays last N lines of supplied text text file.
-        If interval is not supplied, Conky assumes 2x Conky's
-        interval. Max of 30 lines can be displayed, or until the
-        text buffer is filled. 
+        <listitem>Displays last N lines of supplied text file. The
+        file is checked every 'next_check' update. If next_check
+        is not supplied, Conky defaults to 2. Max of 30 lines can be
+        displayed, or until the text buffer is filled.
         <para /></listitem>
     </varlistentry>
     <varlistentry>
index 2a852f7..697716c 100644 (file)
 /* OS specific prototypes to be implemented by linux.c & Co. */
 void update_entropy(void);
 
+/* folds a string over top of itself, like so:
+ *
+ * if start is "blah", and you call it with count = 1, the result will be "lah"
+ */
+void strfold(char *start, int count)
+{
+       char *curplace;
+       for (curplace = start + count; *curplace != 0; curplace++) {
+               *(curplace - count) = *curplace;
+       }
+       *(curplace - count) = 0;
+}
+
 #ifndef HAVE_STRNDUP
 // use our own strndup() if it's not available
 char *strndup(const char *s, size_t n)
index ddda4ab..d094c9d 100644 (file)
@@ -6,6 +6,7 @@
 #include <string.h>
 #include <sys/socket.h>
 
+void strfold(char *start, int count);
 int check_mount(char *s);
 void prepare_update(void);
 void update_uptime(void);
index 62901e0..0d7f44e 100644 (file)
@@ -773,9 +773,12 @@ static void free_text_objects(struct text_object *root, int internal)
                                free(data.ifblock.s);
                                free(data.ifblock.str);
                                break;
+                       case OBJ_head:
                        case OBJ_tail:
-                               free(data.tail.logfile);
-                               free(data.tail.buffer);
+                               free(data.headtail.logfile);
+                               if(data.headtail.buffer) {
+                                       free(data.headtail.buffer);
+                               }
                                break;
                        case OBJ_text:
                        case OBJ_font:
@@ -2067,15 +2070,9 @@ static struct text_object *construct_text_object(const char *s,
                }
 #endif /* __linux__ */
        END OBJ(tail, 0)
-               if (init_tail_object(obj, arg)) {
-                       obj->type = OBJ_text;
-                       obj->data.s = strndup("${tail}", text_buffer_size);
-               }
+               init_tailhead("tail", arg, obj, free_at_crash);
        END OBJ(head, 0)
-               if (init_head_object(obj, arg)) {
-                       obj->type = OBJ_text;
-                       obj->data.s = strndup("${head}", text_buffer_size);
-               }
+               init_tailhead("head", arg, obj, free_at_crash);
        END OBJ(lines, 0)
                if (arg) {
                        obj->data.s = strndup(arg, text_buffer_size);
@@ -3333,19 +3330,6 @@ static int text_contains_templates(const char *text)
        return 0;
 }
 
-/* folds a string over top of itself, like so:
- *
- * if start is "blah", and you call it with count = 1, the result will be "lah"
- */
-static void strfold(char *start, int count)
-{
-       char *curplace;
-       for (curplace = start + count; *curplace != 0; curplace++) {
-               *(curplace - count) = *curplace;
-       }
-       *(curplace - count) = 0;
-}
-
 /*
  * - assumes that *string is '#'
  * - removes the part from '#' to the end of line ('\n' or '\0')
@@ -5566,10 +5550,12 @@ static void generate_text_internal(char *p, int p_max_size,
 #endif
                                        }
                                }
-                       OBJ(tail)
-                               print_tail_object(obj, p, p_max_size);
-                       OBJ(head)
-                               print_head_object(obj, p, p_max_size);
+                       OBJ(tail) {
+                               print_tailhead("tail", obj, p, p_max_size);
+                       }
+                       OBJ(head) {
+                               print_tailhead("head", obj, p, p_max_size);
+                       }
                        OBJ(lines) {
                                FILE *fp = open_file(obj->data.s, &obj->a);
 
index b9e24cb..6645800 100644 (file)
  * 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>
-#include <fcntl.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#ifndef HAVE_MEMRCHR
-static const void *memrchr(const void *buffer, char c, size_t n)
-{
-       const unsigned char *p = buffer;
-
-       for (p += n; n; n--) {
-               if (*--p == c) {
-                       return p;
-               }
-       }
-       return NULL;
-}
-#endif
-
-int init_tailhead_object(enum tailhead_type type,
-               struct text_object *obj, const char *arg)
-{
-       char buf[128];
-       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, "%127s %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;
-       }
+#include "text_object.h"
+#include "logging.h"
 
-       obj->data.tail.fd = -1;
+#define MAX_HEADTAIL_LINES 30
+#define DEFAULT_MAX_HEADTAIL_USES 2
 
-       if (type == HEAD) {
-               goto NO_FIFO;
-       }
-       to_real_path(buf, buf);
-       if (stat(buf, &st) == 0) {
-               if (S_ISFIFO(st.st_mode)) {
-                       fd = open(buf, O_RDONLY | O_NONBLOCK);
+void init_tailhead(const char* type, const char* arg, struct text_object *obj, void* free_at_crash) {
+       unsigned int args;
 
-                       if (fd == -1) {
-                               ERR("%s logfile does not exist, or you do "
-                                   "not have correct permissions", me);
-                               return 1;
+       if(arg) {
+               obj->data.headtail.logfile=malloc(strlen(arg));
+               obj->data.headtail.max_uses = DEFAULT_MAX_HEADTAIL_USES;
+               args = sscanf(arg, "%s %d %d", obj->data.headtail.logfile, &obj->data.headtail.wantedlines, &obj->data.headtail.max_uses);
+               if(args == 2 || args == 3) {
+                       if(obj->data.headtail.max_uses < 1) {
+                               free(obj->data.headtail.logfile);
+                               CRIT_ERR(obj, free_at_crash, "invalid arg for %s, next_check must be larger than 0", type);
+                       }
+                       if (obj->data.headtail.wantedlines > 0 && obj->data.headtail.wantedlines <= MAX_HEADTAIL_LINES) {
+                               to_real_path(obj->data.headtail.logfile, obj->data.headtail.logfile);
+                               obj->data.headtail.buffer = NULL;
+                               obj->data.headtail.current_use = 0;
+                       }else{
+                               free(obj->data.headtail.logfile);
+                               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.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);
+                       free(obj->data.headtail.logfile);
+                       CRIT_ERR(obj, free_at_crash, "%s needs a file as 1st and a number of lines as 2nd argument", type);
                }
        } 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;
+               CRIT_ERR(obj, free_at_crash, "%s needs arguments", type);
        }
-       /* 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)
-{
-#define TAIL_PIPE_BUFSIZE      4096
-       int lines = 0;
-       int line_len = 0;
-       int last_line = 0;
-       int fd = obj->data.tail.fd;
-
-       while (1) {
-               char buf[TAIL_PIPE_BUFSIZE];
-               ssize_t len = read(fd, buf, sizeof(buf));
-               int i;
-
-               if (len == -1) {
-                       if (errno != EAGAIN) {
-                               strcpy(obj->data.tail.buffer, "Logfile Read Error");
-                               snprintf(dst, dst_size, "Logfile Read Error");
-                       }
-
-                       break;
-               } else if (len == 0) {
-                       strcpy(obj->data.tail.buffer, "Logfile Empty");
-                       snprintf(dst, dst_size, "Logfile Empty");
-                       break;
-               }
-
-               for (line_len = 0, i = 0; i < len; i++) {
-                       int pos = 0;
-                       char *p;
-
-                       if (buf[i] == '\n') {
-                               lines++;
-
-                               if (obj->data.tail.readlines > 0) {
-                                       int n;
-                                       int olines = 0;
-                                       int first_line = 0;
-
-                                       for (n = 0; obj->data.tail.buffer[n]; n++) {
-                                               if (obj->data.tail.buffer[n] == '\n') {
-                                                       if (!first_line) {
-                                                               first_line = n + 1;
-                                                       }
-
-                                                       if (++olines < obj->data.tail.wantedlines) {
-                                                               pos = n + 1;
-                                                               continue;
-                                                       }
+void print_tailhead(const char* type, struct text_object *obj, char *p, int p_max_size) {
+       int i, endofstring = 0, linescounted = 0;
+       FILE *fp;
 
-                                                       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;
-                                               }
+       if(obj->data.headtail.buffer && obj->data.headtail.current_use >= obj->data.headtail.max_uses - 1) {
+               free(obj->data.headtail.buffer);
+               obj->data.headtail.buffer = NULL;
+               obj->data.headtail.current_use = 0;
+       }
+       if(obj->data.headtail.buffer) {
+               strcpy(p, obj->data.headtail.buffer);
+               obj->data.headtail.current_use++;
+       }else{
+               fp = open_file(obj->data.headtail.logfile, &obj->a);
+               if(fp != NULL) {
+                       if(strcmp(type,"head") == 0) {
+                               for(i = 0; i < obj->data.headtail.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);
+                               fread(p, 1, p_max_size, fp);
+                               p[p_max_size - 1] = 0;
+                               if(p[strlen(p)-1] == '\n') {    //work with or without \n at end of file
+                                       p[strlen(p)-1] = 0;
+                               }
+                               for(i = strlen(p); i >= 0 && linescounted < obj->data.headtail.wantedlines; i--) {
+                                       if(p[i] == '\n') {
+                                               linescounted++;
                                        }
                                }
-
-                               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;
-       const char *cur_found;
-
-       while (count < step) {
-               if (buf_pos <= 0) {
-                       if (file_pos > BUFSZ) {
-                               fseek(fp, file_pos - BUFSZ, SEEK_SET);
+                               if(i > 0) {
+                                       strfold(p, i+2);
+                               }
                        } else {
-                               buf_size = file_pos;
-                               fseek(fp, 0, SEEK_SET);
-                       }
-                       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++;
-               } else {
-                       buf_pos = -1;
-                       if (file_pos == 0) {
-                               break;
-                       }
-               }
-       }
-       fseek(fp, orig_pos, SEEK_SET);
-       if (count == step) {
-               ret = file_pos + buf_pos;
-       }
-       return ret;
-}
-
-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;
-
-       if (obj->data.tail.fd != -1) {
-               tail_pipe(obj, p, p_max_size);
-               return 0;
-       }
-
-       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;
+                               CRIT_ERR(NULL, NULL, "If you are seeing this then there is a bug in the code, report it !");
                        }
+                       fclose(fp);
+                       obj->data.headtail.buffer = strdup(p);
                }
-               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;
-}
-
-int print_head_object(struct text_object *obj, char *p, size_t 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;
        }
-
-       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';
-                       } else {
-                               obj->data.tail.buffer[nl] = '\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;
+       return;
 }
index d7bda54..0df64c3 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  *
  */
-#ifndef _TAILHEAD_H
-#define _TAILHEAD_H
 
-#include "text_object.h"
-
-#define MAX_TAIL_LINES 100
-
-enum tailhead_type {
-       TAIL,
-       HEAD,
-};
-
-#define init_tail_object(o, a) init_tailhead_object(TAIL, o, a)
-#define init_head_object(o, a) init_tailhead_object(HEAD, o, a)
-
-int init_tailhead_object(enum tailhead_type,
-               struct text_object *, const char *);
-int print_head_object(struct text_object *, char *, size_t);
-int print_tail_object(struct text_object *, char *, size_t);
-
-#endif /* _TAILHEAD_H */
+void init_tailhead(const char* type, const char* arg, struct text_object *obj, void* free_at_crash);
+void print_tailhead(const char* type, struct text_object *obj, char *p, int p_max_size);
index 9c74e88..cf50a35 100644 (file)
@@ -493,15 +493,11 @@ struct text_object {
 
                struct {
                        int wantedlines;
-                       int readlines;
                        char *logfile;
-                       double last_update;
-                       float interval;
                        char *buffer;
-                       /* If not -1, a file descriptor to read from when
-                        * logfile is a FIFO. */
-                       int fd;
-               } tail;
+                       int current_use;
+                       int max_uses;
+               } headtail;
 
                struct {
                        double last_update;