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