1 /* Conky, a system monitor, based on torsmo
3 * Any original torsmo code is licensed under the BSD license
5 * All code written since the fork of torsmo is licensed under the GPL
7 * Please see COPYING for details
9 * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen
10 * Copyright (c) 2005-2009 Brenden Matthews, Philip Kovacs, et. al.
12 * All rights reserved.
14 * This program is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation, either version 3 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #include "text_object.h"
36 #include <sys/types.h>
40 static const void *memrchr(const void *buffer, char c, size_t n)
42 const unsigned char *p = buffer;
44 for (p += n; n; n--) {
53 int init_tailhead_object(enum tailhead_type type,
54 struct text_object *obj, const char *arg)
64 /* FIXME: use #define for that */
65 me = (type == TAIL) ? "tail" : "head";
68 ERR("%s needs arguments", me);
72 numargs = sscanf(arg, "%127s %i %i", buf, &n1, &n2);
74 if (numargs < 2 || numargs > 3) {
75 ERR("incorrect number of arguments given to %s object", me);
79 if (n1 < 1 || n1 > MAX_TAIL_LINES) {
80 ERR("invalid arg for %s, number of lines must be "
81 "between 1 and %i", me, MAX_TAIL_LINES);
85 obj->data.tail.fd = -1;
90 to_real_path(buf, buf);
91 if (stat(buf, &st) == 0) {
92 if (S_ISFIFO(st.st_mode)) {
93 fd = open(buf, O_RDONLY | O_NONBLOCK);
96 ERR("%s logfile does not exist, or you do "
97 "not have correct permissions", me);
101 obj->data.tail.fd = fd;
104 fp = fopen(buf, "r");
108 if (fp || obj->data.tail.fd != -1) {
109 obj->data.tail.logfile = malloc(text_buffer_size);
110 strcpy(obj->data.tail.logfile, buf);
111 obj->data.tail.wantedlines = n1;
112 obj->data.tail.interval = update_interval * 2;
114 if (obj->data.tail.fd == -1) {
119 ERR("%s logfile does not exist, or you do not have "
120 "correct permissions", me);
123 /* XXX: the following implies update_interval >= 1 ?! */
124 if (numargs == 3 && (n2 < 1 || n2 < update_interval)) {
125 ERR("%s interval must be greater than "
126 "0 and "PACKAGE_NAME"'s interval, ignoring", me);
127 } else if (numargs == 3) {
128 obj->data.tail.interval = n2;
130 /* asumming all else worked */
131 obj->data.tail.buffer = malloc(text_buffer_size * 20);
135 /* Allows reading from a FIFO (i.e., /dev/xconsole).
136 * The file descriptor is set to non-blocking which makes this possible.
138 * FIXME: Since lseek cannot seek a file descriptor long lines will break. */
139 static void tail_pipe(struct text_object *obj, char *dst, size_t dst_size)
141 #define TAIL_PIPE_BUFSIZE 4096
145 int fd = obj->data.tail.fd;
148 char buf[TAIL_PIPE_BUFSIZE];
149 ssize_t len = read(fd, buf, sizeof(buf));
153 if (errno != EAGAIN) {
154 strcpy(obj->data.tail.buffer, "Logfile Read Error");
155 snprintf(dst, dst_size, "Logfile Read Error");
159 } else if (len == 0) {
160 strcpy(obj->data.tail.buffer, "Logfile Empty");
161 snprintf(dst, dst_size, "Logfile Empty");
165 for (line_len = 0, i = 0; i < len; i++) {
169 if (buf[i] == '\n') {
172 if (obj->data.tail.readlines > 0) {
177 for (n = 0; obj->data.tail.buffer[n]; n++) {
178 if (obj->data.tail.buffer[n] == '\n') {
183 if (++olines < obj->data.tail.wantedlines) {
189 p = obj->data.tail.buffer + first_line;
190 pos = n - first_line;
191 memmove(obj->data.tail.buffer,
192 obj->data.tail.buffer + first_line, strlen(p));
193 obj->data.tail.buffer[pos] = 0;
201 memcpy(&(obj->data.tail.buffer[pos]), p, line_len);
202 obj->data.tail.buffer[pos + line_len] = 0;
205 obj->data.tail.readlines = lines;
213 snprintf(dst, dst_size, "%s", obj->data.tail.buffer);
216 static long rev_fcharfind(FILE *fp, char val, unsigned int step)
220 unsigned int count = 0;
221 static char buf[BUFSZ];
222 long orig_pos = ftell(fp);
224 long file_pos = orig_pos;
225 long buf_size = BUFSZ;
226 const char *cur_found;
228 while (count < step) {
230 if (file_pos > BUFSZ) {
231 fseek(fp, file_pos - BUFSZ, SEEK_SET);
234 fseek(fp, 0, SEEK_SET);
236 file_pos = ftell(fp);
237 buf_pos = fread(buf, 1, buf_size, fp);
239 cur_found = memrchr(buf, val, (size_t) buf_pos);
240 if (cur_found != NULL) {
241 buf_pos = cur_found - buf;
250 fseek(fp, orig_pos, SEEK_SET);
252 ret = file_pos + buf_pos;
257 int print_tail_object(struct text_object *obj, char *p, size_t p_max_size)
263 if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
264 snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
268 obj->data.tail.last_update = current_update_time;
270 if (obj->data.tail.fd != -1) {
271 tail_pipe(obj, p, p_max_size);
275 fp = fopen(obj->data.tail.logfile, "rt");
277 /* Send one message, but do not consistently spam
278 * on missing logfiles. */
279 if (obj->data.tail.readlines != 0) {
280 ERR("tail logfile failed to open");
281 strcpy(obj->data.tail.buffer, "Logfile Missing");
283 obj->data.tail.readlines = 0;
284 snprintf(p, p_max_size, "Logfile Missing");
286 obj->data.tail.readlines = 0;
287 /* -1 instead of 0 to avoid counting a trailing
289 fseek(fp, -1, SEEK_END);
290 bsize = ftell(fp) + 1;
291 for (iter = obj->data.tail.wantedlines; iter > 0;
293 nl = rev_fcharfind(fp, '\n', iter);
298 obj->data.tail.readlines = iter;
299 if (obj->data.tail.readlines
300 < obj->data.tail.wantedlines) {
301 fseek(fp, 0, SEEK_SET);
303 fseek(fp, nl + 1, SEEK_SET);
306 /* Make sure bsize is at least 1 byte smaller than the
307 * buffer max size. */
308 if (bsize > (long) ((text_buffer_size * 20) - 1)) {
309 fseek(fp, bsize - text_buffer_size * 20 - 1,
311 bsize = text_buffer_size * 20 - 1;
313 bsize = fread(obj->data.tail.buffer, 1, bsize, fp);
316 /* Clean up trailing newline, make sure the
317 * buffer is null terminated. */
318 if (obj->data.tail.buffer[bsize - 1] == '\n') {
319 obj->data.tail.buffer[bsize - 1] = '\0';
321 obj->data.tail.buffer[bsize] = '\0';
323 snprintf(p, p_max_size, "%s",
324 obj->data.tail.buffer);
326 strcpy(obj->data.tail.buffer, "Logfile Empty");
327 snprintf(p, p_max_size, "Logfile Empty");
333 long fwd_fcharfind(FILE *fp, char val, unsigned int step)
337 unsigned int count = 0;
338 static char buf[BUFSZ];
339 long orig_pos = ftell(fp);
341 long buf_size = BUFSZ;
342 char *cur_found = NULL;
344 while (count < step) {
345 if (cur_found == NULL) {
346 buf_size = fread(buf, 1, buf_size, fp);
349 cur_found = memchr(buf + buf_pos, val, buf_size - buf_pos);
350 if (cur_found != NULL) {
351 buf_pos = cur_found - buf + 1;
360 ret = ftell(fp) - buf_size + buf_pos - 1;
362 fseek(fp, orig_pos, SEEK_SET);
366 int print_head_object(struct text_object *obj, char *p, size_t p_max_size)
372 if (current_update_time - obj->data.tail.last_update < obj->data.tail.interval) {
373 snprintf(p, p_max_size, "%s", obj->data.tail.buffer);
377 obj->data.tail.last_update = current_update_time;
379 fp = fopen(obj->data.tail.logfile, "rt");
381 /* Send one message, but do not consistently spam
382 * on missing logfiles. */
383 if (obj->data.tail.readlines != 0) {
384 ERR("head logfile failed to open");
385 strcpy(obj->data.tail.buffer, "Logfile Missing");
387 obj->data.tail.readlines = 0;
388 snprintf(p, p_max_size, "Logfile Missing");
390 obj->data.tail.readlines = 0;
391 for (iter = obj->data.tail.wantedlines; iter > 0;
393 nl = fwd_fcharfind(fp, '\n', iter);
398 obj->data.tail.readlines = iter;
399 /* Make sure nl is at least 1 byte smaller than the
400 * buffer max size. */
401 if (nl > (long) ((text_buffer_size * 20) - 1)) {
402 nl = text_buffer_size * 20 - 1;
404 nl = fread(obj->data.tail.buffer, 1, nl, fp);
407 /* Clean up trailing newline, make sure the buffer
408 * is null terminated. */
409 if (obj->data.tail.buffer[nl - 1] == '\n') {
410 obj->data.tail.buffer[nl - 1] = '\0';
412 obj->data.tail.buffer[nl] = '\0';
414 snprintf(p, p_max_size, "%s",
415 obj->data.tail.buffer);
417 strcpy(obj->data.tail.buffer, "Logfile Empty");
418 snprintf(p, p_max_size, "Logfile Empty");
420 } /* if fp == null */