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