2 * collectd - src/utils_tail.c
3 * Copyright (C) 2007-2008 C-Ware, Inc.
4 * Copyright (C) 2008 Florian Forster
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
25 * Luke Heberling <lukeh at c-ware.com>
26 * Florian Forster <octo at collectd.org>
29 * Encapsulates useful code for plugins which must watch for appends to
36 #include "utils_tail.h"
44 static int cu_tail_reopen(cu_tail_t *obj) {
46 struct stat stat_buf = {0};
48 int status = stat(obj->file, &stat_buf);
50 P_ERROR("utils_tail: stat (%s) failed: %s", obj->file, STRERRNO);
54 /* The file is already open.. */
55 if ((obj->fh != NULL) && (stat_buf.st_ino == obj->stat.st_ino)) {
56 /* Seek to the beginning if file was truncated */
57 if (stat_buf.st_size < obj->stat.st_size) {
58 P_INFO("utils_tail: File `%s' was truncated.", obj->file);
59 status = fseek(obj->fh, 0, SEEK_SET);
61 P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
67 memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
71 /* Seek to the end if we re-open the same file again or the file opened
72 * is the first at all or the first after an error */
73 if ((obj->stat.st_ino == 0) || (obj->stat.st_ino == stat_buf.st_ino))
76 FILE *fh = fopen(obj->file, "r");
78 P_ERROR("utils_tail: fopen (%s) failed: %s", obj->file, STRERRNO);
83 status = fseek(fh, 0, SEEK_END);
85 P_ERROR("utils_tail: fseek (%s) failed: %s", obj->file, STRERRNO);
94 memcpy(&obj->stat, &stat_buf, sizeof(struct stat));
97 } /* int cu_tail_reopen */
99 cu_tail_t *cu_tail_create(const char *file) {
102 obj = calloc(1, sizeof(*obj));
106 obj->file = strdup(file);
107 if (obj->file == NULL) {
115 } /* cu_tail_t *cu_tail_create */
117 int cu_tail_destroy(cu_tail_t *obj) {
124 } /* int cu_tail_destroy */
126 int cu_tail_readline(cu_tail_t *obj, char *buf, int buflen) {
130 ERROR("utils_tail: cu_tail_readline: buflen too small: %i bytes.", buflen);
134 if (obj->fh == NULL) {
135 status = cu_tail_reopen(obj);
139 assert(obj->fh != NULL);
141 /* Try to read from the filehandle. If that succeeds, everything appears to
142 * be fine and we can return. */
144 if (fgets(buf, buflen, obj->fh) != NULL) {
145 buf[buflen - 1] = '\0';
149 /* Check if we encountered an error */
150 if (ferror(obj->fh) != 0) {
151 /* Jupp, error. Force `cu_tail_reopen' to reopen the file.. */
155 /* else: eof -> check if the file was moved away and reopen the new file if
158 status = cu_tail_reopen(obj);
159 /* error -> return with error */
162 /* file end reached and file not reopened -> nothing more to read */
163 else if (status > 0) {
168 /* If we get here: file was re-opened and there may be more to read.. Let's
170 if (fgets(buf, buflen, obj->fh) != NULL) {
171 buf[buflen - 1] = '\0';
175 if (ferror(obj->fh) != 0) {
176 WARNING("utils_tail: fgets (%s) returned an error: %s", obj->file,
183 /* EOf, well, apparently the new file is empty.. */
186 } /* int cu_tail_readline */
188 int cu_tail_read(cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
195 status = cu_tail_readline(obj, buf, buflen);
197 ERROR("utils_tail: cu_tail_read: cu_tail_readline "
208 if (buf[len - 1] != '\n')
214 status = callback(data, buf, buflen);
216 ERROR("utils_tail: cu_tail_read: callback returned "
224 } /* int cu_tail_read */