1 /**
2 * collectd - src/utils_tail.c
3 * Copyright (C) 2007-2008 C-Ware, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; only version 2 of the License is applicable.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 * Author:
19 * Luke Heberling <lukeh at c-ware.com>
20 *
21 * Description:
22 * Encapsulates useful code for plugins which must watch for appends to
23 * the end of a file.
24 **/
26 #include "collectd.h"
27 #include "common.h"
28 #include "utils_tail.h"
30 struct cu_tail_s
31 {
32 char *file;
33 FILE *fd;
34 struct stat stat;
35 };
37 cu_tail_t *cu_tail_create (const char *file)
38 {
39 cu_tail_t *obj;
41 obj = (cu_tail_t *) malloc (sizeof (cu_tail_t));
42 if (obj == NULL)
43 return (NULL);
44 memset (obj, '\0', sizeof (cu_tail_t));
46 obj->file = strdup (file);
47 if (obj->file == NULL)
48 {
49 free (obj);
50 return (NULL);
51 }
53 obj->fd = NULL;
55 return (obj);
56 } /* cu_tail_t *cu_tail_create */
58 int cu_tail_destroy (cu_tail_t *obj)
59 {
60 if (obj->fd != NULL)
61 fclose (obj->fd);
62 free (obj->file);
63 free (obj);
65 return (0);
66 } /* int cu_tail_destroy */
68 int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen)
69 {
70 struct stat stat_now;
71 int status;
73 if (buflen < 1)
74 {
75 ERROR ("utils_tail: cu_tail_readline: buflen too small: "
76 "%i bytes.", buflen);
77 return (-1);
78 }
80 if (stat (obj->file, &stat_now) != 0)
81 {
82 char errbuf[1024];
83 ERROR ("cu_tail_readline: stat (%s) failed: %s",
84 obj->file,
85 sstrerror (errno, errbuf, sizeof (errbuf)));
86 return (-1);
87 }
89 if ((stat_now.st_dev != obj->stat.st_dev) ||
90 (stat_now.st_ino != obj->stat.st_ino))
91 {
92 /*
93 * If the file was replaced open the new file and close the
94 * old filehandle
95 */
96 FILE *new_fd;
98 DEBUG ("utils_tail: cu_tail_readline: (Re)Opening %s..",
99 obj->file);
101 new_fd = fopen (obj->file, "r");
102 if (new_fd == NULL)
103 {
104 char errbuf[1024];
105 ERROR ("utils_tail: cu_tail_readline: open (%s) failed: %s",
106 obj->file,
107 sstrerror (errno, errbuf,
108 sizeof (errbuf)));
109 return (-1);
110 }
112 /* If there was no previous file, seek to the end. We don't
113 * want to read in the entire file, usually. */
114 if (obj->stat.st_ino == 0)
115 fseek (new_fd, 0, SEEK_END);
117 if (obj->fd != NULL)
118 fclose (obj->fd);
119 obj->fd = new_fd;
121 }
122 else if (stat_now.st_size < obj->stat.st_size)
123 {
124 /*
125 * Else, if the file was not replaces, but the file was
126 * truncated, seek to the beginning of the file.
127 */
128 assert (obj->fd != NULL);
129 rewind (obj->fd);
130 }
132 status = 0;
133 if (fgets (buf, buflen, obj->fd) == NULL)
134 {
135 if (feof (obj->fd) != 0)
136 buf[0] = '\0';
137 else /* an error occurred */
138 {
139 ERROR ("utils_tail: cu_tail_readline: fgets returned "
140 "an error.");
141 status = -1;
142 }
143 }
145 if (status == 0)
146 memcpy (&obj->stat, &stat_now, sizeof (struct stat));
148 return (status);
149 } /* int cu_tail_readline */
151 int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc_t *callback,
152 void *data)
153 {
154 int status;
156 while (42)
157 {
158 status = cu_tail_readline (obj, buf, buflen);
159 if (status != 0)
160 {
161 ERROR ("utils_tail: cu_tail_read: cu_tail_readline "
162 "failed.");
163 break;
164 }
166 /* check for EOF */
167 if (buf[0] == '\0')
168 break;
170 status = callback (data, buf, buflen);
171 if (status != 0)
172 {
173 ERROR ("utils_tail: cu_tail_read: callback returned "
174 "status %i.", status);
175 break;
176 }
177 }
179 return status;
180 } /* int cu_tail_read */