]> git.ipfire.org Git - thirdparty/squid.git/blob - src/log/ModDaemon.cc
Sync with trunk rev.13542
[thirdparty/squid.git] / src / log / ModDaemon.cc
1 /*
2 * DEBUG: section 50 Log file handling
3 * AUTHOR: Adrian Chadd <adrian@squid-cache.org>
4 *
5 * SQUID Web Proxy Cache http://www.squid-cache.org/
6 * ----------------------------------------------------------
7 *
8 * Squid is the result of efforts by numerous individuals from
9 * the Internet community; see the CONTRIBUTORS file for full
10 * details. Many organizations have provided support for Squid's
11 * development; see the SPONSORS file for full details. Squid is
12 * Copyrighted (C) 2001 by the Regents of the University of
13 * California; see the COPYRIGHT file for full details. Squid
14 * incorporates software developed and/or copyrighted by other
15 * sources; see the CREDITS file for full details.
16 *
17 * This program is free software; you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation; either version 2 of the License, or
20 * (at your option) any later version.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
30 *
31 */
32
33 #include "squid.h"
34 #include "cbdata.h"
35 #include "comm/Loops.h"
36 #include "fde.h"
37 #include "globals.h"
38 #include "log/Config.h"
39 #include "log/File.h"
40 #include "log/ModDaemon.h"
41 #include "SquidConfig.h"
42 #include "SquidIpc.h"
43 #include "SquidTime.h"
44
45 #include <cerrno>
46
47 /* How many buffers to keep before we say we've buffered too much */
48 #define LOGFILE_MAXBUFS 128
49
50 /* Size of the logfile buffer */
51 /*
52 * For optimal performance this should match LOGFILE_BUFSIZ in logfile-daemon.c
53 */
54 #define LOGFILE_BUFSZ 32768
55
56 /* How many seconds between warnings */
57 #define LOGFILE_WARN_TIME 30
58
59 static LOGWRITE logfile_mod_daemon_writeline;
60 static LOGLINESTART logfile_mod_daemon_linestart;
61 static LOGLINEEND logfile_mod_daemon_lineend;
62 static LOGROTATE logfile_mod_daemon_rotate;
63 static LOGFLUSH logfile_mod_daemon_flush;
64 static LOGCLOSE logfile_mod_daemon_close;
65
66 static void logfile_mod_daemon_append(Logfile * lf, const char *buf, int len);
67
68 struct _l_daemon {
69 int rfd, wfd;
70 char eol;
71 pid_t pid;
72 int flush_pending;
73 dlink_list bufs;
74 int nbufs;
75 int last_warned;
76 };
77
78 typedef struct _l_daemon l_daemon_t;
79
80 /* Internal code */
81 static void
82 logfileNewBuffer(Logfile * lf)
83 {
84 l_daemon_t *ll = (l_daemon_t *) lf->data;
85 logfile_buffer_t *b;
86
87 debugs(50, 5, "logfileNewBuffer: " << lf->path << ": new buffer");
88
89 b = static_cast<logfile_buffer_t*>(xcalloc(1, sizeof(logfile_buffer_t)));
90 assert(b != NULL);
91 b->buf = static_cast<char*>(xcalloc(1, LOGFILE_BUFSZ));
92 assert(b->buf != NULL);
93 b->size = LOGFILE_BUFSZ;
94 b->written_len = 0;
95 b->len = 0;
96 dlinkAddTail(b, &b->node, &ll->bufs);
97 ++ ll->nbufs;
98 }
99
100 static void
101 logfileFreeBuffer(Logfile * lf, logfile_buffer_t * b)
102 {
103 l_daemon_t *ll = (l_daemon_t *) lf->data;
104 assert(b != NULL);
105 dlinkDelete(&b->node, &ll->bufs);
106 -- ll->nbufs;
107 xfree(b->buf);
108 xfree(b);
109 }
110
111 static void
112 logfileHandleWrite(int fd, void *data)
113 {
114 Logfile *lf = static_cast<Logfile *>(data);
115 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
116
117 /*
118 * We'll try writing the first entry until its done - if we
119 * get a partial write then we'll re-schedule until its completed.
120 * Its naive but it'll do for now.
121 */
122 if (!ll->bufs.head) // abort if there is nothing pending right now.
123 return;
124
125 logfile_buffer_t *b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
126 assert(b != NULL);
127 ll->flush_pending = 0;
128
129 int ret = FD_WRITE_METHOD(ll->wfd, b->buf + b->written_len, b->len - b->written_len);
130 debugs(50, 3, lf->path << ": write returned " << ret);
131 if (ret < 0) {
132 if (ignoreErrno(errno)) {
133 /* something temporary */
134 Comm::SetSelect(ll->wfd, COMM_SELECT_WRITE, logfileHandleWrite, lf, 0);
135 ll->flush_pending = 1;
136 return;
137 }
138 debugs(50, DBG_IMPORTANT,"logfileHandleWrite: " << lf->path << ": error writing (" << xstrerror() << ")");
139 /* XXX should handle this better */
140 fatal("I don't handle this error well!");
141 }
142 if (ret == 0) {
143 /* error? */
144 debugs(50, DBG_IMPORTANT, "logfileHandleWrite: " << lf->path << ": wrote 0 bytes?");
145 /* XXX should handle this better */
146 fatal("I don't handle this error well!");
147 }
148 /* ret > 0, so something was written */
149 b->written_len += ret;
150 assert(b->written_len <= b->len);
151 if (b->written_len == b->len) {
152 /* written the whole buffer! */
153 logfileFreeBuffer(lf, b);
154 b = NULL;
155 }
156 /* Is there more to write? */
157 if (!ll->bufs.head)
158 return;
159 /* there is, so schedule more */
160
161 Comm::SetSelect(ll->wfd, COMM_SELECT_WRITE, logfileHandleWrite, lf, 0);
162 ll->flush_pending = 1;
163 return;
164 }
165
166 static void
167 logfileQueueWrite(Logfile * lf)
168 {
169 l_daemon_t *ll = (l_daemon_t *) lf->data;
170 if (ll->flush_pending || ll->bufs.head == NULL) {
171 return;
172 }
173 ll->flush_pending = 1;
174 if (ll->bufs.head) {
175 logfile_buffer_t *b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
176 if (b->len + 2 <= b->size)
177 logfile_mod_daemon_append(lf, "F\n", 2);
178 }
179 /* Ok, schedule a write-event */
180 Comm::SetSelect(ll->wfd, COMM_SELECT_WRITE, logfileHandleWrite, lf, 0);
181 }
182
183 static void
184 logfile_mod_daemon_append(Logfile * lf, const char *buf, int len)
185 {
186 l_daemon_t *ll = (l_daemon_t *) lf->data;
187 logfile_buffer_t *b;
188 int s;
189
190 /* Is there a buffer? If not, create one */
191 if (ll->bufs.head == NULL) {
192 logfileNewBuffer(lf);
193 }
194 debugs(50, 3, "logfile_mod_daemon_append: " << lf->path << ": appending " << len << " bytes");
195 /* Copy what can be copied */
196 while (len > 0) {
197 b = static_cast<logfile_buffer_t*>(ll->bufs.tail->data);
198 debugs(50, 3, "logfile_mod_daemon_append: current buffer has " << b->len << " of " << b->size << " bytes before append");
199 s = min(len, (b->size - b->len));
200 memcpy(b->buf + b->len, buf, s);
201 len = len - s;
202 buf = buf + s;
203 b->len = b->len + s;
204 assert(b->len <= LOGFILE_BUFSZ);
205 assert(len >= 0);
206 if (len > 0) {
207 logfileNewBuffer(lf);
208 }
209 }
210 }
211
212 /*
213 * only schedule a flush (write) if one isn't scheduled.
214 */
215 static void
216 logfileFlushEvent(void *data)
217 {
218 Logfile *lf = static_cast<Logfile *>(data);
219
220 /*
221 * This might work better if we keep track of when we wrote last and only
222 * schedule a write if we haven't done so in the last second or two.
223 */
224 logfileQueueWrite(lf);
225 eventAdd("logfileFlush", logfileFlushEvent, lf, 1.0, 1);
226 }
227
228 /* External code */
229
230 int
231 logfile_mod_daemon_open(Logfile * lf, const char *path, size_t bufsz, int fatal_flag)
232 {
233 const char *args[5];
234 char *tmpbuf;
235 l_daemon_t *ll;
236
237 lf->f_close = logfile_mod_daemon_close;
238 lf->f_linewrite = logfile_mod_daemon_writeline;
239 lf->f_linestart = logfile_mod_daemon_linestart;
240 lf->f_lineend = logfile_mod_daemon_lineend;
241 lf->f_flush = logfile_mod_daemon_flush;
242 lf->f_rotate = logfile_mod_daemon_rotate;
243
244 cbdataInternalLock(lf); // WTF?
245 debugs(50, DBG_IMPORTANT, "Logfile Daemon: opening log " << path);
246 ll = static_cast<l_daemon_t*>(xcalloc(1, sizeof(*ll)));
247 lf->data = ll;
248 ll->eol = 1;
249 {
250 Ip::Address localhost;
251 args[0] = "(logfile-daemon)";
252 args[1] = path;
253 args[2] = NULL;
254 localhost.setLocalhost();
255 ll->pid = ipcCreate(IPC_STREAM, Log::TheConfig.logfile_daemon, args, "logfile-daemon", localhost, &ll->rfd, &ll->wfd, NULL);
256 if (ll->pid < 0)
257 fatal("Couldn't start logfile helper");
258 }
259 ll->nbufs = 0;
260
261 /* Queue the initial control data */
262 tmpbuf = static_cast<char*>(xmalloc(BUFSIZ));
263 snprintf(tmpbuf, BUFSIZ, "r%d\nb%d\n", Config.Log.rotateNumber, Config.onoff.buffered_logs);
264 logfile_mod_daemon_append(lf, tmpbuf, strlen(tmpbuf));
265 xfree(tmpbuf);
266
267 /* Start the flush event */
268 eventAdd("logfileFlush", logfileFlushEvent, lf, 1.0, 1);
269
270 return 1;
271 }
272
273 static void
274 logfile_mod_daemon_close(Logfile * lf)
275 {
276 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
277 debugs(50, DBG_IMPORTANT, "Logfile Daemon: closing log " << lf->path);
278 logfileFlush(lf);
279 if (ll->rfd == ll->wfd)
280 comm_close(ll->rfd);
281 else {
282 comm_close(ll->rfd);
283 comm_close(ll->wfd);
284 }
285 kill(ll->pid, SIGTERM);
286 eventDelete(logfileFlushEvent, lf);
287 xfree(ll);
288 lf->data = NULL;
289 cbdataInternalUnlock(lf); // WTF??
290 }
291
292 static void
293 logfile_mod_daemon_rotate(Logfile * lf)
294 {
295 char tb[3];
296 debugs(50, DBG_IMPORTANT, "logfileRotate: " << lf->path);
297 tb[0] = 'R';
298 tb[1] = '\n';
299 tb[2] = '\0';
300 logfile_mod_daemon_append(lf, tb, 2);
301 }
302
303 /*
304 * This routine assumes that up to one line is written. Don't try to
305 * call this routine with more than one line or subsequent lines
306 * won't be prefixed with the command type and confuse the logging
307 * daemon somewhat.
308 */
309 static void
310 logfile_mod_daemon_writeline(Logfile * lf, const char *buf, size_t len)
311 {
312 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
313 /* Make sure the logfile buffer isn't too large */
314 if (ll->nbufs > LOGFILE_MAXBUFS) {
315 if (ll->last_warned < squid_curtime - LOGFILE_WARN_TIME) {
316 ll->last_warned = squid_curtime;
317 debugs(50, DBG_IMPORTANT, "Logfile: " << lf->path << ": queue is too large; some log messages have been lost.");
318 }
319 return;
320 }
321 /* Append this data to the end buffer; create a new one if needed */
322 /* Are we eol? If so, prefix with our logfile command byte */
323 logfile_mod_daemon_append(lf, buf, len);
324 }
325
326 static void
327 logfile_mod_daemon_linestart(Logfile * lf)
328 {
329 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
330 char tb[2];
331 assert(ll->eol == 1);
332 ll->eol = 0;
333 tb[0] = 'L';
334 tb[1] = '\0';
335 logfile_mod_daemon_append(lf, tb, 1);
336 }
337
338 static void
339 logfile_mod_daemon_lineend(Logfile * lf)
340 {
341 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
342 logfile_buffer_t *b;
343 assert(ll->eol == 0);
344 ll->eol = 1;
345 /* Kick a write off if the head buffer is -full- */
346 if (ll->bufs.head != NULL) {
347 b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
348 if (b->node.next != NULL || !Config.onoff.buffered_logs)
349 logfileQueueWrite(lf);
350 }
351 }
352
353 static void
354 logfile_mod_daemon_flush(Logfile * lf)
355 {
356 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
357 if (commUnsetNonBlocking(ll->wfd)) {
358 debugs(50, DBG_IMPORTANT, "Logfile Daemon: Couldn't set the pipe blocking for flush! You're now missing some log entries.");
359 return;
360 }
361 while (ll->bufs.head != NULL) {
362 logfileHandleWrite(ll->wfd, lf);
363 }
364 if (commSetNonBlocking(ll->wfd)) {
365 fatalf("Logfile Daemon: %s: Couldn't set the pipe non-blocking for flush!\n", lf->path);
366 return;
367 }
368 }