ModDaemon.cc
Go to the documentation of this file.
1/*
2 * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9/* DEBUG: section 50 Log file handling */
10
11#include "squid.h"
12#include "cbdata.h"
13#include "comm/Loops.h"
14#include "fatal.h"
15#include "fde.h"
16#include "globals.h"
17#include "log/Config.h"
18#include "log/File.h"
19#include "log/ModDaemon.h"
20#include "SquidConfig.h"
21#include "SquidIpc.h"
22
23#include <cerrno>
24
25/* How many buffers to keep before we say we've buffered too much */
26#define LOGFILE_MAXBUFS 128
27
28/* Size of the logfile buffer */
29/*
30 * For optimal performance this should match LOGFILE_BUFSIZ in logfile-daemon.c
31 */
32#define LOGFILE_BUFSZ 32768
33
34/* How many seconds between warnings */
35#define LOGFILE_WARN_TIME 30
36
43
44static void logfile_mod_daemon_append(Logfile * lf, const char *buf, int len);
45
46struct _l_daemon {
47 int rfd, wfd;
48 char eol;
49 pid_t pid;
52 int nbufs;
54};
55
56typedef struct _l_daemon l_daemon_t;
57
58/* Internal code */
59static void
61{
62 l_daemon_t *ll = (l_daemon_t *) lf->data;
64
65 debugs(50, 5, "logfileNewBuffer: " << lf->path << ": new buffer");
66
67 b = static_cast<logfile_buffer_t*>(xcalloc(1, sizeof(logfile_buffer_t)));
68 assert(b != nullptr);
69 b->buf = static_cast<char*>(xcalloc(1, LOGFILE_BUFSZ));
70 assert(b->buf != nullptr);
71 b->size = LOGFILE_BUFSZ;
72 b->written_len = 0;
73 b->len = 0;
74 dlinkAddTail(b, &b->node, &ll->bufs);
75 ++ ll->nbufs;
76}
77
78static void
80{
81 l_daemon_t *ll = (l_daemon_t *) lf->data;
82 assert(b != nullptr);
83 dlinkDelete(&b->node, &ll->bufs);
84 -- ll->nbufs;
85 xfree(b->buf);
86 xfree(b);
87}
88
89static void
90logfileHandleWrite(int, void *data)
91{
92 Logfile *lf = static_cast<Logfile *>(data);
93 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
94
95 /*
96 * We'll try writing the first entry until its done - if we
97 * get a partial write then we'll re-schedule until its completed.
98 * Its naive but it'll do for now.
99 */
100 if (!ll->bufs.head) // abort if there is nothing pending right now.
101 return;
102
103 logfile_buffer_t *b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
104 assert(b != nullptr);
105 ll->flush_pending = 0;
106
107 int ret = FD_WRITE_METHOD(ll->wfd, b->buf + b->written_len, b->len - b->written_len);
108 int xerrno = errno;
109 debugs(50, 3, lf->path << ": write returned " << ret);
110 if (ret < 0) {
111 if (ignoreErrno(xerrno)) {
112 /* something temporary */
114 ll->flush_pending = 1;
115 return;
116 }
117 debugs(50, DBG_IMPORTANT, "ERROR: logfileHandleWrite: " << lf->path << ": error writing (" << xstrerr(xerrno) << ")");
118 /* XXX should handle this better */
119 fatal("I don't handle this error well!");
120 }
121 if (ret == 0) {
122 /* error? */
123 debugs(50, DBG_IMPORTANT, "logfileHandleWrite: " << lf->path << ": wrote 0 bytes?");
124 /* XXX should handle this better */
125 fatal("I don't handle this error well!");
126 }
127 /* ret > 0, so something was written */
128 b->written_len += ret;
129 assert(b->written_len <= b->len);
130 if (b->written_len == b->len) {
131 /* written the whole buffer! */
132 logfileFreeBuffer(lf, b);
133 b = nullptr;
134 }
135 /* Is there more to write? */
136 if (!ll->bufs.head)
137 return;
138 /* there is, so schedule more */
139
141 ll->flush_pending = 1;
142 return;
143}
144
145static void
147{
148 l_daemon_t *ll = (l_daemon_t *) lf->data;
149 if (ll->flush_pending || ll->bufs.head == nullptr) {
150 return;
151 }
152 ll->flush_pending = 1;
153 if (ll->bufs.head) {
154 logfile_buffer_t *b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
155 if (b->len + 2 <= b->size)
156 logfile_mod_daemon_append(lf, "F\n", 2);
157 }
158 /* Ok, schedule a write-event */
160}
161
162static void
163logfile_mod_daemon_append(Logfile * lf, const char *buf, int len)
164{
165 l_daemon_t *ll = (l_daemon_t *) lf->data;
167 int s;
168
169 /* Is there a buffer? If not, create one */
170 if (ll->bufs.head == nullptr) {
172 }
173 debugs(50, 3, "logfile_mod_daemon_append: " << lf->path << ": appending " << len << " bytes");
174 /* Copy what can be copied */
175 while (len > 0) {
176 b = static_cast<logfile_buffer_t*>(ll->bufs.tail->data);
177 debugs(50, 3, "logfile_mod_daemon_append: current buffer has " << b->len << " of " << b->size << " bytes before append");
178 s = min(len, (b->size - b->len));
179 memcpy(b->buf + b->len, buf, s);
180 len = len - s;
181 buf = buf + s;
182 b->len = b->len + s;
183 assert(b->len <= LOGFILE_BUFSZ);
184 assert(len >= 0);
185 if (len > 0) {
187 }
188 }
189}
190
191/*
192 * only schedule a flush (write) if one isn't scheduled.
193 */
194static void
196{
197 Logfile *lf = static_cast<Logfile *>(data);
198
199 /*
200 * This might work better if we keep track of when we wrote last and only
201 * schedule a write if we haven't done so in the last second or two.
202 */
204 eventAdd("logfileFlush", logfileFlushEvent, lf, 1.0, 1);
205}
206
207/* External code */
208
209int
210logfile_mod_daemon_open(Logfile * lf, const char *path, size_t, int)
211{
212 const char *args[5];
213 char *tmpbuf;
214 l_daemon_t *ll;
215
222
223 cbdataInternalLock(lf); // WTF?
224 debugs(50, DBG_IMPORTANT, "Logfile Daemon: opening log " << path);
225 ll = static_cast<l_daemon_t*>(xcalloc(1, sizeof(*ll)));
226 lf->data = ll;
227 ll->eol = 1;
228 {
229 Ip::Address localhost;
230 args[0] = "(logfile-daemon)";
231 args[1] = path;
232 args[2] = nullptr;
233 localhost.setLocalhost();
234 ll->pid = ipcCreate(IPC_STREAM, Log::TheConfig.logfile_daemon, args, "logfile-daemon", localhost, &ll->rfd, &ll->wfd, nullptr);
235 if (ll->pid < 0)
236 fatal("Couldn't start logfile helper");
237 }
238 ll->nbufs = 0;
239
240 /* Queue the initial control data */
241 tmpbuf = static_cast<char*>(xmalloc(BUFSIZ));
242 snprintf(tmpbuf, BUFSIZ, "r%d\nb%d\n", Config.Log.rotateNumber, Config.onoff.buffered_logs);
243 logfile_mod_daemon_append(lf, tmpbuf, strlen(tmpbuf));
244 xfree(tmpbuf);
245
246 /* Start the flush event */
247 eventAdd("logfileFlush", logfileFlushEvent, lf, 1.0, 1);
248
249 return 1;
250}
251
252static void
254{
255 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
256 debugs(50, DBG_IMPORTANT, "Logfile Daemon: closing log " << lf->path);
257 logfileFlush(lf);
258 if (ll->rfd == ll->wfd)
259 comm_close(ll->rfd);
260 else {
261 comm_close(ll->rfd);
262 comm_close(ll->wfd);
263 }
264 kill(ll->pid, SIGTERM);
266 xfree(ll);
267 lf->data = nullptr;
268 cbdataInternalUnlock(lf); // WTF??
269}
270
271static void
273{
274 char tb[3];
275 debugs(50, DBG_IMPORTANT, "logfileRotate: " << lf->path);
276 tb[0] = 'R';
277 tb[1] = '\n';
278 tb[2] = '\0';
279 logfile_mod_daemon_append(lf, tb, 2);
280}
281
282/*
283 * This routine assumes that up to one line is written. Don't try to
284 * call this routine with more than one line or subsequent lines
285 * won't be prefixed with the command type and confuse the logging
286 * daemon somewhat.
287 */
288static void
289logfile_mod_daemon_writeline(Logfile * lf, const char *buf, size_t len)
290{
291 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
292 /* Make sure the logfile buffer isn't too large */
293 if (ll->nbufs > LOGFILE_MAXBUFS) {
296 debugs(50, DBG_IMPORTANT, "Logfile: " << lf->path << ": queue is too large; some log messages have been lost.");
297 }
298 return;
299 }
300
301 /* Are we eol? If so, prefix with our logfile command byte */
302 if (ll->eol == 1) {
303 logfile_mod_daemon_append(lf, "L", 1);
304 ll->eol = 0;
305 }
306
307 /* Append this data to the end buffer; create a new one if needed */
308 logfile_mod_daemon_append(lf, buf, len);
309}
310
311static void
313{
314 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
315 assert(ll->eol == 1);
316 // logfile_mod_daemon_writeline() sends the starting command
317}
318
319static void
321{
322 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
324 if (ll->eol == 1) // logfile_mod_daemon_writeline() wrote nothing
325 return;
326 ll->eol = 1;
327 /* Kick a write off if the head buffer is -full- */
328 if (ll->bufs.head != nullptr) {
329 b = static_cast<logfile_buffer_t*>(ll->bufs.head->data);
330 if (b->node.next != nullptr || !Config.onoff.buffered_logs)
332 }
333}
334
335static void
337{
338 l_daemon_t *ll = static_cast<l_daemon_t *>(lf->data);
339 if (commUnsetNonBlocking(ll->wfd)) {
340 debugs(50, DBG_IMPORTANT, "ERROR: Logfile Daemon: Could not set the pipe blocking for flush! You are now missing some log entries.");
341 return;
342 }
343 while (ll->bufs.head != nullptr) {
344 logfileHandleWrite(ll->wfd, lf);
345 }
346 if (commSetNonBlocking(ll->wfd)) {
347 fatalf("Logfile Daemon: %s: Couldn't set the pipe non-blocking for flush!\n", lf->path);
348 return;
349 }
350}
351
static LOGLINESTART logfile_mod_daemon_linestart
Definition: ModDaemon.cc:38
static LOGFLUSH logfile_mod_daemon_flush
Definition: ModDaemon.cc:41
#define LOGFILE_BUFSZ
Definition: ModDaemon.cc:32
static LOGWRITE logfile_mod_daemon_writeline
Definition: ModDaemon.cc:37
static LOGROTATE logfile_mod_daemon_rotate
Definition: ModDaemon.cc:40
#define LOGFILE_MAXBUFS
Definition: ModDaemon.cc:26
int logfile_mod_daemon_open(Logfile *lf, const char *path, size_t, int)
Definition: ModDaemon.cc:210
static void logfileQueueWrite(Logfile *lf)
Definition: ModDaemon.cc:146
#define LOGFILE_WARN_TIME
Definition: ModDaemon.cc:35
static void logfileNewBuffer(Logfile *lf)
Definition: ModDaemon.cc:60
static LOGCLOSE logfile_mod_daemon_close
Definition: ModDaemon.cc:42
static void logfileHandleWrite(int, void *data)
Definition: ModDaemon.cc:90
static LOGLINEEND logfile_mod_daemon_lineend
Definition: ModDaemon.cc:39
static void logfileFlushEvent(void *data)
Definition: ModDaemon.cc:195
static void logfile_mod_daemon_append(Logfile *lf, const char *buf, int len)
Definition: ModDaemon.cc:163
static void logfileFreeBuffer(Logfile *lf, logfile_buffer_t *b)
Definition: ModDaemon.cc:79
time_t squid_curtime
Definition: stub_libtime.cc:20
class SquidConfig Config
Definition: SquidConfig.cc:12
#define assert(EX)
Definition: assert.h:17
void cbdataInternalUnlock(const void *p)
Definition: cbdata.cc:238
void cbdataInternalLock(const void *p)
Definition: cbdata.cc:221
void setLocalhost()
Definition: Address.cc:255
Definition: File.h:39
LOGLINEEND * f_lineend
Definition: File.h:59
LOGFLUSH * f_flush
Definition: File.h:60
void * data
Definition: File.h:55
LOGCLOSE * f_close
Definition: File.h:62
char path[MAXPATHLEN]
Definition: File.h:46
LOGLINESTART * f_linestart
Definition: File.h:57
LOGWRITE * f_linewrite
Definition: File.h:58
LOGROTATE * f_rotate
Definition: File.h:61
struct SquidConfig::@106 onoff
int buffered_logs
Definition: SquidConfig.h:284
struct SquidConfig::@98 Log
int rotateNumber
Definition: SquidConfig.h:191
int written_len
Definition: File.h:25
dlink_node node
Definition: File.h:26
char * buf
Definition: File.h:22
int commSetNonBlocking(int fd)
Definition: comm.cc:1066
int ignoreErrno(int ierrno)
Definition: comm.cc:1440
int commUnsetNonBlocking(int fd)
Definition: comm.cc:1099
#define comm_close(x)
Definition: comm.h:27
A const & min(A const &lhs, A const &rhs)
#define DBG_IMPORTANT
Definition: Stream.h:38
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:194
#define IPC_STREAM
Definition: defines.h:106
#define COMM_SELECT_WRITE
Definition: defines.h:25
#define BUFSIZ
Definition: defines.h:20
void eventDelete(EVH *func, void *arg)
Definition: event.cc:127
void eventAdd(const char *name, EVH *func, void *arg, double when, int weight, bool cbdata)
Definition: event.cc:107
void fatal(const char *message)
Definition: fatal.cc:28
void fatalf(const char *fmt,...)
Definition: fatal.cc:68
int FD_WRITE_METHOD(int fd, const char *buf, int len)
Definition: fde.h:200
pid_t ipcCreate(int type, const char *prog, const char *const args[], const char *name, Ip::Address &local_addr, int *rfd, int *wfd, void **hIpc)
Definition: ipc.cc:65
void logfileFlush(Logfile *lf)
Definition: File.cc:139
void LOGROTATE(Logfile *, const int16_t)
Definition: File.h:35
void LOGLINESTART(Logfile *)
Definition: File.h:31
void LOGCLOSE(Logfile *)
Definition: File.h:36
void LOGLINEEND(Logfile *)
Definition: File.h:33
void LOGWRITE(Logfile *, const char *, size_t len)
Definition: File.h:32
void LOGFLUSH(Logfile *)
Definition: File.h:34
void SetSelect(int, unsigned int, PF *, void *, time_t)
Mark an FD to be watched for its IO status.
Definition: ModDevPoll.cc:223
LogConfig TheConfig
Definition: Config.cc:15
#define xfree
#define xmalloc
char eol
Definition: ModDaemon.cc:48
int last_warned
Definition: ModDaemon.cc:53
int nbufs
Definition: ModDaemon.cc:52
pid_t pid
Definition: ModDaemon.cc:49
dlink_list bufs
Definition: ModDaemon.cc:51
int flush_pending
Definition: ModDaemon.cc:50
void * xcalloc(size_t n, size_t sz)
Definition: xalloc.cc:71
const char * xstrerr(int error)
Definition: xstrerror.cc:83

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors