1 /*
2 *
3 * Copyright (c) 2001 Fredrik Sjoholm <fredrik@sjoholm.com>
4 * All rights reserved.
5 * License: GPL - The GNU General Public License
6 *
7 */
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <errno.h>
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #include <fcntl.h>
16 #include <signal.h>
17 #include <time.h>
18 #include <string.h>
19
20
21
22 struct {
23 char* data;
24 int used;
25 int size;
26 } buf;
27
28 struct FileInfo {
29 char *name;
30 int fd;
31 struct stat stat; // remember info about file so we can know if it's been rotated
32 } out;
33
34 struct {
35 char timestamp; // { 0 | 1 }
36 char * time_format;
37 int max_len;
38 int zap_if_disk_full;
39 } conf;
40
41 #define DEFAULT_TIME_FORMAT "%Y%m%d;%T: "
42
43 volatile int gotHUP;
44
45 void growbuf(int size);
46 int openfile (struct FileInfo* file);
47 int filechanged (struct FileInfo* file);
48 void catchHUP (int sig);
49 void handleHUP ();
50 int dotime (char* prepend_to);
51
main(int argc,char ** argv)52 int main(int argc, char** argv)
53 {
54 char *eol, *pos;
55 int done = 0;
56 int opt = 0;
57 int totalwn = 0;
58 int disk_is_full = 0;
59
60 // look for switches
61 conf.time_format = NULL;
62 while (++opt < argc) {
63 if (!strcmp(argv[opt], "-t")) {
64 conf.timestamp = 1;
65 conf.time_format = DEFAULT_TIME_FORMAT;
66 }
67 else if (!strcmp(argv[opt], "-T")) {
68 opt++;
69 if (opt < argc) {
70 conf.timestamp = 1;
71 conf.time_format = argv[opt];
72 }
73 }
74 else if (!strcmp(argv[opt], "-l")) {
75 opt++;
76 if (opt < argc) {
77 conf.max_len = atoi (argv[opt]);
78 }
79 }
80 else if (!strcmp(argv[opt], "-z")) {
81 conf.zap_if_disk_full = 1;
82 }
83 else {
84 break;
85 }
86 }
87
88 if (opt >= argc) {
89 fprintf (stderr,
90 "Usage: pipeline| %s [options] {logfile|-} # SIGHUP will reopen logfile (v1.8)\n"
91 " -t prepend each line with \"YYYYMMDD;HH:MM:SS: \"\n"
92 " -T <format> prepend each line with specified strftime(3) format\n"
93 " -l <number> log file length limit (force truncation)\n"
94 " -z zap (truncate) log if disk gets full (default: grow buffer)\n",
95 argv[0]);
96 exit(1);
97 }
98
99 out.name = argv[opt];
100 openfile (&out);
101
102 signal (SIGHUP, catchHUP);
103
104 growbuf (4096);
105
106 while (!done) {
107 int size;
108
109 if (buf.used >= buf.size) growbuf (buf.size*2);
110
111 size = read (0, buf.data+buf.used, buf.size-buf.used);
112 if (size > 0) {
113 buf.used += size;
114 }
115 else if (size == 0) {
116 done = 1;
117 }
118 else { }
119
120 for (;;) {
121
122 if (gotHUP) {
123 handleHUP();
124 signal (SIGHUP, catchHUP);
125 gotHUP = 0;
126 }
127
128 pos = buf.data;
129 eol = (char*) memchr(buf.data, '\n', buf.used);
130 if (eol == 0) break;
131 eol ++;
132
133 if (conf.timestamp && !disk_is_full) {
134 pos -= dotime (pos); // don't prepend time again if we already did it before disk got full.
135 }
136
137 while (pos < eol) {
138 int wn = write(out.fd, pos, eol-pos);
139 if (wn > 0) {
140 pos += wn;
141 totalwn += wn;
142 disk_is_full = 0;
143 }
144 else if (errno == ENOSPC) {
145 if (conf.zap_if_disk_full) {
146 char str[256];
147 if (ftruncate(out.fd,0)) {
148 fprintf(stderr, "truncating %s: %d %s\n", out.name, errno, strerror(errno));
149 exit(1);
150 }
151 write (out.fd, str, snprintf(str, sizeof(str), "Device full: truncating %s\n\n", out.name));
152 }
153 else {
154 disk_is_full = 1;
155 break; // give up trying to write to disk until next input
156 }
157 }
158 else {
159 fprintf(stderr, "write %s: %d %s\n", out.name, errno, strerror(errno));
160 exit(1);
161 }
162 if (conf.max_len && (totalwn > conf.max_len)) {
163 char str[256];
164 if (ftruncate(out.fd,0)) {
165 fprintf(stderr, "truncating %s: %d %s\n", out.name, errno, strerror(errno));
166 exit(1);
167 }
168 write (out.fd, str, snprintf(str, sizeof(str), "File size limit: truncating %s\n\n", out.name));
169 totalwn = 0;
170 }
171 }
172
173 buf.used = buf.data + buf.used - pos;
174 memmove (buf.data, pos, buf.used);
175 if (disk_is_full) { break; }
176 }
177
178 }
179
180
181
182 exit(0);
183 }
184
185
186
growbuf(int size)187 void growbuf (int size)
188 {
189 if (size > buf.size) {
190 // pad beginning of buffer with 100 bytes used by dotime()
191 // pad end with the same, so the memmove() at end of loop above is always safe
192 // even in case of a full disk while writing a prepended timestamp.
193 buf.data = (char*) realloc (buf.data ? buf.data-100 : 0, size+100*2) + 100;
194 buf.size = size;
195 }
196 }
197
198
199
200
openfile(struct FileInfo * file)201 int openfile (struct FileInfo* file)
202 {
203 int fd;
204 if (strcmp(file->name,"-") != 0) {
205 #ifdef O_LARGEFILE
206 fd = open (file->name, O_CREAT|O_APPEND|O_WRONLY, 0666);
207 #else
208 fd = open (file->name, O_CREAT|O_APPEND|O_WRONLY, 0666);
209 #endif
210 } else {
211 fd = 2;
212 }
213 if (fd < 0) {
214 fprintf (stderr, "open write %s: %s\n", file->name, strerror(errno));
215 exit(1);
216 }
217 file->fd = fd;
218 stat (file->name, &file->stat); // update stat buffer
219 return fd;
220 }
221
222
reopenfile(struct FileInfo * file)223 int reopenfile (struct FileInfo* file)
224 {
225 if (strcmp(file->name,"-") != 0) {
226 close (file->fd);
227 return openfile (file);
228 } else {
229 return file->fd;
230 }
231 }
232
233
234
235
236
filechanged(struct FileInfo * file)237 int filechanged (struct FileInfo* file)
238 {
239 struct stat stat2;
240
241 if (strcmp(file->name,"-") == 0) { return 0; }
242
243 if (stat (file->name, &stat2) < 0) {
244 return 1; // file removed or something
245 }
246
247 if (stat2.st_ino != file->stat.st_ino || stat2.st_dev != file->stat.st_dev) {
248 // file changed or was moved to a different device
249 return 1;
250 }
251
252 return 0;
253 }
254
255
256
catchHUP(int sig)257 void catchHUP (int sig)
258 {
259 gotHUP = 1;
260 }
261
262
263
handleHUP()264 void handleHUP ()
265 {
266 char str[256];
267 if (filechanged(&out)) {
268 // only reopen file and print msg if the file on disk was changed or removed
269 write (out.fd, str, snprintf (str, sizeof(str), "Caught SIGHUP: Reopening %s (closed)\n\n", out.name));
270 reopenfile (&out);
271 write (out.fd, str, snprintf (str, sizeof(str), "Caught SIGHUP: Reopening %s (opened)\n\n", out.name));
272 }
273 }
274
275
276
277 // format: "YYYYMMDD;HH.MM.SS: "
278 // make sure it's safe to prepend before the prepend_to pointer.
dotime(char * prepend_to)279 int dotime (char* prepend_to)
280 {
281 time_t now;
282 struct tm *t;
283 static char buf[100];
284 int len;
285
286 time (&now);
287 t = localtime (&now);
288 len = strftime(buf, sizeof(buf), conf.time_format, t);
289 memcpy (prepend_to-len, buf, len);
290 return len;
291 }
292
293
294
295
296