xref: /lighttpd1.4/src/fdlog_maint.c (revision c412bb59)
1 #include "first.h"
2 
3 #include "fdlog.h"
4 
5 #include <sys/types.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include "fdevent.h"
13 #include "ck.h"
14 #include "log.h"
15 
16 /*
17  * Notes:
18  * - Use of "/dev/stdin" and "/dev/stdout" is not recommended
19  *   since those are manipulated at startup in server_main_setup()
20  *   "/dev/stderr" might similarly be manipulated at startup.
21  * - Use of "/proc/self/fd/..." is permitted, and admin may use a shell script
22  *   to dup standard fds to higher numbers before starting lighttpd if admin
23  *   wants to direct logs to the standard fds on which lighttpd was started.
24  *   Future: might detect and use the open fd rather than open() a new fd to the
25  *   already-open path.  If so, should stat() to check that fd actually exists,
26  *   and must check for and not close() those paths when closing fdlog_st fds.
27  */
28 
29 struct fdlog_files_t {
30     fdlog_st **ptr;
31     uint32_t used;
32 };
33 
34 static struct fdlog_files_t fdlog_files;
35 
36 typedef struct fdlog_pipe {
37     /* ((fdlog_st *) is ptr rather than inlined struct since multiple callers
38      *  might have reference to (fdlog_st *), and if fdlog_pipes.ptr is
39      *  reallocated those ptrs could be invalided if inlined struct) */
40     fdlog_st *fdlog; /*(contains write-side of pipe)*/
41     pid_t pid;
42     int fd;         /*(contains read-side of pipe)*/
43     unix_time64_t start;
44 } fdlog_pipe;
45 
46 
47 struct fdlog_pipes_t {
48     fdlog_pipe *ptr;
49     uint32_t used;
50 };
51 
52 static struct fdlog_pipes_t fdlog_pipes;
53 
54 
55 static pid_t
fdlog_pipe_spawn(const char * const fn,const int rfd)56 fdlog_pipe_spawn (const char * const fn, const int rfd)
57 {
58     char *args[4];
59     int devnull = fdevent_open_devnull();
60     pid_t pid;
61 
62     if (-1 == devnull) {
63         return -1;
64     }
65 
66     *(const char **)&args[0] = "/bin/sh";
67     *(const char **)&args[1] = "-c";
68     *(const char **)&args[2] = fn;
69     args[3] = NULL;
70 
71     pid = fdevent_fork_execve(args[0], args, NULL, rfd, devnull, devnull, -1);
72 
73     if (pid > 0) {
74         close(devnull);
75     }
76     else {
77         int errnum = errno;
78         close(devnull);
79         errno = errnum;
80     }
81     return pid;
82 }
83 
84 
85 __attribute_noinline__
86 static int
fdlog_pipe_restart(fdlog_pipe * const fdp,const unix_time64_t ts)87 fdlog_pipe_restart (fdlog_pipe * const fdp, const unix_time64_t ts)
88 {
89     if (fdp->start + 5 < ts) { /* limit restart to once every 5 sec */
90         /* restart child process using existing pipe fds */
91         fdp->start = ts;
92         fdp->pid = fdlog_pipe_spawn(fdp->fdlog->fn, fdp->fd);
93     }
94     return (fdp->pid > 0) ? 1 : -1;
95 }
96 
97 
98 void
fdlog_pipes_restart(const unix_time64_t ts)99 fdlog_pipes_restart (const unix_time64_t ts)
100 {
101     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
102         fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
103         if (fdp->pid > 0) continue;
104         fdlog_pipe_restart(fdp, ts);
105     }
106 }
107 
108 
109 int
fdlog_pipes_waitpid_cb(const pid_t pid)110 fdlog_pipes_waitpid_cb (const pid_t pid)
111 {
112     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
113         fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
114         if (fdp->pid != pid) continue;
115 
116         fdp->pid = -1;
117         return fdlog_pipe_restart(fdp, log_monotonic_secs);
118     }
119     return 0;
120 }
121 
122 
123 static void
fdlog_pipes_close(fdlog_st * const retain)124 fdlog_pipes_close (fdlog_st * const retain)
125 {
126     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
127         fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
128         fdlog_st * const fdlog = fdp->fdlog;
129         close(fdp->fd);
130         fdp->fd = -1;
131         if (fdlog == retain) continue; /*(free'd later)*/
132         fdlog_free(fdlog);
133     }
134     free(fdlog_pipes.ptr);
135     fdlog_pipes.ptr = NULL;
136     fdlog_pipes.used = 0;
137 }
138 
139 
140 void
fdlog_pipes_abandon_pids(void)141 fdlog_pipes_abandon_pids (void)
142 {
143     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
144         fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
145         fdp->pid = -1;
146     }
147 }
148 
149 
150 void
fdlog_pipe_serrh(const int fd)151 fdlog_pipe_serrh (const int fd)
152 {
153     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
154         fdlog_st * const fdlog = fdlog_pipes.ptr[i].fdlog;
155         if (fdlog->fd != fd) continue;
156 
157         fdlog->fd = STDERR_FILENO;
158         break;
159     }
160 }
161 
162 
163 static fdlog_st *
fdlog_pipe_init(const char * const fn,const int fds[2],const pid_t pid)164 fdlog_pipe_init (const char * const fn, const int fds[2], const pid_t pid)
165 {
166     if (!(fdlog_pipes.used & (4-1)))
167         ck_realloc_u32((void **)&fdlog_pipes.ptr, fdlog_pipes.used,
168                        4, sizeof(*fdlog_pipes.ptr));
169     fdlog_pipe * const fdp = fdlog_pipes.ptr + fdlog_pipes.used++;
170     fdp->fd = fds[0];
171     fdp->pid = pid;
172     fdp->start = log_monotonic_secs;
173     return (fdp->fdlog = fdlog_init(fn, fds[1], FDLOG_PIPE));
174 }
175 
176 
177 static fdlog_st *
fdlog_pipe_open(const char * const fn)178 fdlog_pipe_open (const char * const fn)
179 {
180     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
181         fdlog_st * const fdlog = fdlog_pipes.ptr[i].fdlog;
182         if (0 != strcmp(fdlog->fn, fn)) continue;
183         return fdlog;
184     }
185 
186     int fds[2];
187     if (fdevent_pipe_cloexec(fds, 65536))
188         return NULL;
189 
190     pid_t pid = fdlog_pipe_spawn(fn, fds[0]);
191     if (pid > 0) {
192         /*(nonblocking write() from lighttpd)*/
193         if (0 != fdevent_fcntl_set_nb(fds[1])) { /*(ignore)*/ }
194         return fdlog_pipe_init(fn, fds, pid);
195     }
196     else {
197         int errnum = errno;
198         close(fds[0]);
199         close(fds[1]);
200         errno = errnum;
201         return NULL;
202     }
203 }
204 
205 
206 static fdlog_st *
fdlog_file_init(const char * const fn,const int fd)207 fdlog_file_init (const char * const fn, const int fd)
208 {
209     if (!(fdlog_files.used & (4-1)))
210         ck_realloc_u32((void **)&fdlog_files.ptr, fdlog_files.used,
211                        4, sizeof(*fdlog_files.ptr));
212     return (fdlog_files.ptr[fdlog_files.used++] = fdlog_init(fn,fd,FDLOG_FILE));
213 }
214 
215 
216 static int
fdlog_file_open_fd(const char * const fn)217 fdlog_file_open_fd (const char * const fn)
218 {
219     int flags = O_APPEND | O_WRONLY | O_CREAT;
220     return fdevent_open_cloexec(fn, 1, flags, 0644); /*(permit symlinks)*/
221 }
222 
223 
224 static fdlog_st *
fdlog_file_open(const char * const fn)225 fdlog_file_open (const char * const fn)
226 {
227     for (uint32_t i = 0; i < fdlog_files.used; ++i) {
228         fdlog_st * const fdlog = fdlog_files.ptr[i];
229         if (0 != strcmp(fdlog->fn, fn)) continue;
230         return fdlog;
231     }
232 
233     int fd = fdlog_file_open_fd(fn);
234     return (-1 != fd) ? fdlog_file_init(fn, fd) : NULL;
235 }
236 
237 
238 fdlog_st *
fdlog_open(const char * const fn)239 fdlog_open (const char * const fn)
240 {
241     return (fn[0] != '|')
242       ? fdlog_file_open(fn)
243       : fdlog_pipe_open(fn+1); /*(skip the '|')*/
244 }
245 
246 
247 void
fdlog_files_flush(fdlog_st * const errh,const int memrel)248 fdlog_files_flush (fdlog_st * const errh, const int memrel)
249 {
250     for (uint32_t i = 0; i < fdlog_files.used; ++i) {
251         fdlog_st * const fdlog = fdlog_files.ptr[i];
252         buffer * const b = &fdlog->b;
253         if (!buffer_is_blank(b)) {
254             const ssize_t wr = write_all(fdlog->fd, BUF_PTR_LEN(b));
255             buffer_clear(b); /*(clear buffer, even on error)*/
256             if (-1 == wr)
257                 log_perror(errh, __FILE__, __LINE__,
258                   "error flushing log %s", fdlog->fn);
259         }
260         if (memrel && b->ptr) buffer_free_ptr(b);
261     }
262 }
263 
264 
265 void
fdlog_files_cycle(fdlog_st * const errh)266 fdlog_files_cycle (fdlog_st * const errh)
267 {
268     fdlog_files_flush(errh, 0);
269     for (uint32_t i = 0; i < fdlog_files.used; ++i) {
270         fdlog_st * const fdlog = fdlog_files.ptr[i];
271         int fd = fdlog_file_open_fd(fdlog->fn);
272         if (-1 != fd) {
273             if (fdlog->fd > STDERR_FILENO) {
274                 close(fdlog->fd);
275                 fdlog->fd = fd;
276             }
277             else {
278                 if (fdlog->fd != dup2(fd, fdlog->fd))
279                     log_perror(errh, __FILE__, __LINE__,
280                       "dup2() %s to %d", fdlog->fn, fdlog->fd);
281                 close(fd);
282             }
283         }
284         else {
285             log_perror(errh, __FILE__, __LINE__,
286               "error cycling log %s", fdlog->fn);
287             /*(leave prior log file open)*/
288         }
289     }
290 }
291 
292 
293 static void
fdlog_files_close(fdlog_st * const retain)294 fdlog_files_close (fdlog_st * const retain)
295 {
296     fdlog_files_flush(retain, 0);
297     for (uint32_t i = 0; i < fdlog_files.used; ++i) {
298         fdlog_st * const fdlog = fdlog_files.ptr[i];
299         if (fdlog == retain) continue; /*(free'd later)*/
300         fdlog_free(fdlog);
301     }
302     free(fdlog_files.ptr);
303     fdlog_files.ptr = NULL;
304     fdlog_files.used = 0;
305 }
306 
307 
308 void
fdlog_closeall(fdlog_st * const errh)309 fdlog_closeall (fdlog_st * const errh)
310 {
311     fdlog_files_close(errh);
312     fdlog_pipes_close(errh);
313 }
314 
315 
316 void
fdlog_flushall(fdlog_st * const errh)317 fdlog_flushall (fdlog_st * const errh)
318 {
319     fdlog_files_flush(errh, 1); /*(flush, then release buffer memory)*/
320     /*(at the moment, pipe loggers clear buffer after each write attempt,
321      * so there is nothing to flush, though there are buffers to be freed)*/
322     for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
323         buffer * const b = &fdlog_pipes.ptr[i].fdlog->b;
324         if (b->ptr) buffer_free_ptr(b);
325     }
326     if (errh->b.ptr) buffer_free_ptr(&errh->b);
327 }
328