src/utils_tail.[ch]: Added a generic interface for `tail'ing files.
[collectd.git] / src / utils_tail.c
1 /**
2  * collectd - src/utils_tail.c
3  * Copyright (C) 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  **/
25
26
27 #include "utils_tail.h"
28 #include <stdio.h>
29 #include <sys/stat.h>
30 #include <string.h>
31 #include <malloc.h>
32
33 struct cu_tail_s
34 {
35         char  *file;
36         FILE  *fd;
37         struct stat stat;
38 };
39
40 cu_tail_t *cu_tail_create (const char *file)
41 {
42         cu_tail_t *obj;
43
44         obj = (cu_tail_t *) malloc (sizeof (cu_tail_t));
45         if (obj == NULL)
46                 return (NULL);
47         memset (obj, '\0', sizeof (cu_tail_t));
48
49         obj->file = strdup (file);
50         if (obj->file == NULL)
51         {
52                 free (obj);
53                 return (NULL);
54         }
55
56         obj->fd = NULL;
57
58         return (obj);
59 } /* cu_tail_t *cu_tail_create */
60
61 int cu_tail_destroy (cu_tail_t *obj)
62 {
63         if (obj->fd != NULL)
64                 fclose (obj->fd);
65         free (obj->file);
66         free (obj);
67
68         return (0);
69 } /* int cu_tail_destroy */
70
71 int cu_tail_readline (cu_tail_t *obj, char *buf, int buflen)
72 {
73         struct stat stat_now;
74         FILE *new_fd;
75         int len;
76
77         if( buflen < 1 )
78                 return -1;
79         
80         *buf = '\0';
81
82         if (stat (obj->file, &stat_now) != 0)
83                 return 0;
84
85         if (stat_now.st_dev != obj->stat.st_dev ||
86                 stat_now.st_ino != obj->stat.st_ino)
87         {
88                 new_fd = fopen (obj->file, "r");
89                 if (new_fd == NULL)
90                         return -1;
91                 
92                 if (obj->stat.st_ino == 0)
93                         fseek (new_fd, 0, SEEK_END);
94                 if (obj->fd != NULL)
95                         fclose (obj->fd);
96                 obj->fd = new_fd;
97
98         }
99         else if (stat_now.st_size < obj->stat.st_size)
100         {
101                 rewind (obj->fd);
102         }
103
104         memcpy (&obj->stat, &stat_now, sizeof (struct stat));   
105         
106         if (fgets (buf, buflen, obj->fd) == NULL && feof (obj->fd) == 0)
107                 return -1;
108
109         len = strlen (buf);
110         if (len > 0 && *(buf + len - 1) != '\n' && feof (obj->fd))
111         {
112                 fseek (obj->fd, -len, SEEK_CUR);
113                 *buf = '\0';
114         }
115
116         return 0;
117 } /* int cu_tail_readline */
118
119 int cu_tail_read (cu_tail_t *obj, char *buf, int buflen, tailfunc *func, void *data)
120 {
121         int ret;
122
123         while ((ret = cu_tail_readline (obj, buf, buflen)) == 0)
124                 if (*buf == '\0' || (ret = func (data, buf, buflen)))
125                                 break;
126
127         return ret;
128 } /* int cu_tail_read */
129