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