1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-09-29 21:49:07 +00:00

Fix bug with SF id 2808272 (tail and head)

To fix this bug the tail and head code was rewritten, everything should still
work except tailing a FIFO instead of a normal head.
This commit is contained in:
Nikolas Garofil 2009-07-21 23:41:47 +02:00
parent caf0731565
commit 41a7cffbe7
7 changed files with 103 additions and 441 deletions

View File

@ -1261,12 +1261,12 @@
<command> <command>
<option>head</option> <option>head</option>
</command> </command>
<option>logfile lines (interval)</option> <option>logfile lines (next_check)</option>
</term> </term>
<listitem>Displays first N lines of supplied text text <listitem>Displays first N lines of supplied text file. The
file. If interval is not supplied, Conky assumes 2x Conky's file is checked every 'next_check' update. If next_check
interval. Max of 30 lines can be displayed, or until the is not supplied, Conky defaults to 2. Max of 30 lines can be
text buffer is filled. displayed, or until the text buffer is filled.
<para /></listitem> <para /></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
@ -2794,12 +2794,12 @@
<command> <command>
<option>tail</option> <option>tail</option>
</command> </command>
<option>logfile lines (interval)</option> <option>logfile lines (next_check)</option>
</term> </term>
<listitem>Displays last N lines of supplied text text file. <listitem>Displays last N lines of supplied text file. The
If interval is not supplied, Conky assumes 2x Conky's file is checked every 'next_check' update. If next_check
interval. Max of 30 lines can be displayed, or until the is not supplied, Conky defaults to 2. Max of 30 lines can be
text buffer is filled. displayed, or until the text buffer is filled.
<para /></listitem> <para /></listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>

View File

@ -51,6 +51,19 @@
/* OS specific prototypes to be implemented by linux.c & Co. */ /* OS specific prototypes to be implemented by linux.c & Co. */
void update_entropy(void); 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 #ifndef HAVE_STRNDUP
// use our own strndup() if it's not available // use our own strndup() if it's not available
char *strndup(const char *s, size_t n) char *strndup(const char *s, size_t n)

View File

@ -6,6 +6,7 @@
#include <string.h> #include <string.h>
#include <sys/socket.h> #include <sys/socket.h>
void strfold(char *start, int count);
int check_mount(char *s); int check_mount(char *s);
void prepare_update(void); void prepare_update(void);
void update_uptime(void); void update_uptime(void);

View File

@ -773,9 +773,12 @@ static void free_text_objects(struct text_object *root, int internal)
free(data.ifblock.s); free(data.ifblock.s);
free(data.ifblock.str); free(data.ifblock.str);
break; break;
case OBJ_head:
case OBJ_tail: case OBJ_tail:
free(data.tail.logfile); free(data.headtail.logfile);
free(data.tail.buffer); if(data.headtail.buffer) {
free(data.headtail.buffer);
}
break; break;
case OBJ_text: case OBJ_text:
case OBJ_font: case OBJ_font:
@ -2067,15 +2070,9 @@ static struct text_object *construct_text_object(const char *s,
} }
#endif /* __linux__ */ #endif /* __linux__ */
END OBJ(tail, 0) END OBJ(tail, 0)
if (init_tail_object(obj, arg)) { init_tailhead("tail", arg, obj, free_at_crash);
obj->type = OBJ_text;
obj->data.s = strndup("${tail}", text_buffer_size);
}
END OBJ(head, 0) END OBJ(head, 0)
if (init_head_object(obj, arg)) { init_tailhead("head", arg, obj, free_at_crash);
obj->type = OBJ_text;
obj->data.s = strndup("${head}", text_buffer_size);
}
END OBJ(lines, 0) END OBJ(lines, 0)
if (arg) { if (arg) {
obj->data.s = strndup(arg, text_buffer_size); obj->data.s = strndup(arg, text_buffer_size);
@ -3333,19 +3330,6 @@ static int text_contains_templates(const char *text)
return 0; 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 '#' * - assumes that *string is '#'
* - removes the part from '#' to the end of line ('\n' or '\0') * - 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 #endif
} }
} }
OBJ(tail) OBJ(tail) {
print_tail_object(obj, p, p_max_size); print_tailhead("tail", obj, p, p_max_size);
OBJ(head) }
print_head_object(obj, p, p_max_size); OBJ(head) {
print_tailhead("head", obj, p, p_max_size);
}
OBJ(lines) { OBJ(lines) {
FILE *fp = open_file(obj->data.s, &obj->a); FILE *fp = open_file(obj->data.s, &obj->a);

View File

@ -24,399 +24,83 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * 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 "text_object.h"
#include "logging.h"
#include <errno.h> #define MAX_HEADTAIL_LINES 30
#include <fcntl.h> #define DEFAULT_MAX_HEADTAIL_USES 2
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef HAVE_MEMRCHR void init_tailhead(const char* type, const char* arg, struct text_object *obj, void* free_at_crash) {
static const void *memrchr(const void *buffer, char c, size_t n) unsigned int args;
{
const unsigned char *p = buffer;
for (p += n; n; n--) { if(arg) {
if (*--p == c) { obj->data.headtail.logfile=malloc(strlen(arg));
return p; 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) {
return NULL; if(obj->data.headtail.max_uses < 1) {
} free(obj->data.headtail.logfile);
#endif CRIT_ERR(obj, free_at_crash, "invalid arg for %s, next_check must be larger than 0", type);
}
int init_tailhead_object(enum tailhead_type type, if (obj->data.headtail.wantedlines > 0 && obj->data.headtail.wantedlines <= MAX_HEADTAIL_LINES) {
struct text_object *obj, const char *arg) to_real_path(obj->data.headtail.logfile, obj->data.headtail.logfile);
{ obj->data.headtail.buffer = NULL;
char buf[128]; obj->data.headtail.current_use = 0;
int n1, n2; }else{
struct stat st; free(obj->data.headtail.logfile);
FILE *fp = NULL; CRIT_ERR(obj, free_at_crash, "invalid arg for %s, number of lines must be between 1 and %d", type, MAX_HEADTAIL_LINES);
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;
}
obj->data.tail.fd = -1;
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);
if (fd == -1) {
ERR("%s logfile does not exist, or you do "
"not have correct permissions", me);
return 1;
} }
obj->data.tail.fd = fd;
} else { } else {
NO_FIFO: free(obj->data.headtail.logfile);
fp = fopen(buf, "r"); CRIT_ERR(obj, free_at_crash, "%s needs a file as 1st and a number of lines as 2nd argument", type);
}
}
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);
} }
} else { } else {
// fclose(fp); CRIT_ERR(obj, free_at_crash, "%s needs arguments", type);
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). void print_tailhead(const char* type, struct text_object *obj, char *p, int p_max_size) {
* The file descriptor is set to non-blocking which makes this possible. int i, endofstring = 0, linescounted = 0;
* FILE *fp;
* 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) { if(obj->data.headtail.buffer && obj->data.headtail.current_use >= obj->data.headtail.max_uses - 1) {
char buf[TAIL_PIPE_BUFSIZE]; free(obj->data.headtail.buffer);
ssize_t len = read(fd, buf, sizeof(buf)); obj->data.headtail.buffer = NULL;
int i; obj->data.headtail.current_use = 0;
}
if (len == -1) { if(obj->data.headtail.buffer) {
if (errno != EAGAIN) { strcpy(p, obj->data.headtail.buffer);
strcpy(obj->data.tail.buffer, "Logfile Read Error"); obj->data.headtail.current_use++;
snprintf(dst, dst_size, "Logfile Read Error"); }else{
} fp = open_file(obj->data.headtail.logfile, &obj->a);
if(fp != NULL) {
break; if(strcmp(type,"head") == 0) {
} else if (len == 0) { for(i = 0; i < obj->data.headtail.wantedlines; i++) {
strcpy(obj->data.tail.buffer, "Logfile Empty"); fgets(p + endofstring, p_max_size - endofstring, fp);
snprintf(dst, dst_size, "Logfile Empty"); endofstring = strlen(p);
break; }
} } else if(strcmp(type,"tail") == 0) {
fseek(fp, - p_max_size, SEEK_END);
for (line_len = 0, i = 0; i < len; i++) { fread(p, 1, p_max_size, fp);
int pos = 0; p[p_max_size - 1] = 0;
char *p; if(p[strlen(p)-1] == '\n') { //work with or without \n at end of file
p[strlen(p)-1] = 0;
if (buf[i] == '\n') { }
lines++; for(i = strlen(p); i >= 0 && linescounted < obj->data.headtail.wantedlines; i--) {
if(p[i] == '\n') {
if (obj->data.tail.readlines > 0) { linescounted++;
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;
}
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(i > 0) {
p = buf + last_line; strfold(p, i+2);
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);
} else { } else {
buf_size = file_pos; CRIT_ERR(NULL, NULL, "If you are seeing this then there is a bug in the code, report it !");
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;
} }
fclose(fp);
obj->data.headtail.buffer = strdup(p);
} }
} }
fseek(fp, orig_pos, SEEK_SET); return;
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;
}
}
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;
} }

View File

@ -24,24 +24,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
#ifndef _TAILHEAD_H
#define _TAILHEAD_H
#include "text_object.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);
#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 */

View File

@ -493,15 +493,11 @@ struct text_object {
struct { struct {
int wantedlines; int wantedlines;
int readlines;
char *logfile; char *logfile;
double last_update;
float interval;
char *buffer; char *buffer;
/* If not -1, a file descriptor to read from when int current_use;
* logfile is a FIFO. */ int max_uses;
int fd; } headtail;
} tail;
struct { struct {
double last_update; double last_update;