summaryrefslogtreecommitdiff
path: root/src/fdlog_maint.c
blob: fdf8f7fe85a865ae261fbbc6348d904172901170 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#include "first.h"

#include "fdlog.h"

#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include "sys-unistd.h" /* <unistd.h> */
#ifdef _WIN32
#include <windows.h>
#endif

#include "fdevent.h"
#include "ck.h"
#include "log.h"

/*
 * Notes:
 * - Use of "/dev/stdin" and "/dev/stdout" is not recommended
 *   since those are manipulated at startup in server_main_setup()
 *   "/dev/stderr" might similarly be manipulated at startup.
 * - Use of "/proc/self/fd/..." is permitted, and admin may use a shell script
 *   to dup standard fds to higher numbers before starting lighttpd if admin
 *   wants to direct logs to the standard fds on which lighttpd was started.
 *   Future: might detect and use the open fd rather than open() a new fd to the
 *   already-open path.  If so, should stat() to check that fd actually exists,
 *   and must check for and not close() those paths when closing fdlog_st fds.
 */

struct fdlog_files_t {
    fdlog_st **ptr;
    uint32_t used;
};

static struct fdlog_files_t fdlog_files;

typedef struct fdlog_pipe {
    /* ((fdlog_st *) is ptr rather than inlined struct since multiple callers
     *  might have reference to (fdlog_st *), and if fdlog_pipes.ptr is
     *  reallocated those ptrs could be invalided if inlined struct) */
    fdlog_st *fdlog; /*(contains write-side of pipe)*/
    pid_t pid;
    int fd;         /*(contains read-side of pipe)*/
    unix_time64_t start;
} fdlog_pipe;


struct fdlog_pipes_t {
    fdlog_pipe *ptr;
    uint32_t used;
};

static struct fdlog_pipes_t fdlog_pipes;


static pid_t
fdlog_pipe_spawn (const char * const fn, const int rfd)
{
    char *args[4];
    int devnull = fdevent_open_devnull();
    pid_t pid;

    if (-1 == devnull) {
        return -1;
    }

    *(const char **)&args[0] = "/bin/sh";
    *(const char **)&args[1] = "-c";
    *(const char **)&args[2] = fn;
    args[3] = NULL;

    pid = fdevent_fork_execve(args[0], args, NULL, rfd, devnull, devnull, -1);

    if (pid > 0) {
        close(devnull);
    }
    else {
        int errnum = errno;
        close(devnull);
        errno = errnum;
    }
    return pid;
}


__attribute_noinline__
static int
fdlog_pipe_restart (fdlog_pipe * const fdp, const unix_time64_t ts)
{
    if (fdp->start + 5 < ts) { /* limit restart to once every 5 sec */
        /* restart child process using existing pipe fds */
        fdp->start = ts;
        fdp->pid = fdlog_pipe_spawn(fdp->fdlog->fn, fdp->fd);
    }
    return (fdp->pid > 0) ? 1 : -1;
}


void
fdlog_pipes_restart (const unix_time64_t ts)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
        if (fdp->pid > 0) continue;
        fdlog_pipe_restart(fdp, ts);
    }
}


int
fdlog_pipes_waitpid_cb (const pid_t pid)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
        if (fdp->pid != pid) continue;

        fdp->pid = -1;
        return fdlog_pipe_restart(fdp, log_monotonic_secs);
    }
    return 0;
}


static void
fdlog_pipes_close (fdlog_st * const retain)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
        fdlog_st * const fdlog = fdp->fdlog;
        close(fdp->fd);
        fdp->fd = -1;
        if (fdlog == retain) continue; /*(free'd later)*/
        fdlog_free(fdlog);
    }
    free(fdlog_pipes.ptr);
    fdlog_pipes.ptr = NULL;
    fdlog_pipes.used = 0;
}


void
fdlog_pipes_abandon_pids (void)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_pipe * const fdp = fdlog_pipes.ptr+i;
        fdp->pid = -1;
    }
}


void
fdlog_pipe_serrh (const int fd)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_st * const fdlog = fdlog_pipes.ptr[i].fdlog;
        if (fdlog->fd != fd) continue;

        fdlog->fd = STDERR_FILENO;
      #ifdef _WIN32
        SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(STDERR_FILENO));
      #endif
        break;
    }
}


static fdlog_st *
fdlog_pipe_init (const char * const fn, const int fds[2], const pid_t pid)
{
    if (!(fdlog_pipes.used & (4-1)))
        ck_realloc_u32((void **)&fdlog_pipes.ptr, fdlog_pipes.used,
                       4, sizeof(*fdlog_pipes.ptr));
    fdlog_pipe * const fdp = fdlog_pipes.ptr + fdlog_pipes.used++;
    fdp->fd = fds[0];
    fdp->pid = pid;
    fdp->start = log_monotonic_secs;
    return (fdp->fdlog = fdlog_init(fn, fds[1], FDLOG_PIPE));
}


static fdlog_st *
fdlog_pipe_open (const char * const fn)
{
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        fdlog_st * const fdlog = fdlog_pipes.ptr[i].fdlog;
        if (0 != strcmp(fdlog->fn, fn)) continue;
        return fdlog;
    }

    int fds[2];
    if (fdevent_pipe_cloexec(fds, 65536))
        return NULL;

    pid_t pid = fdlog_pipe_spawn(fn, fds[0]);
    if (pid > 0) {
        /*(nonblocking write() from lighttpd)*/
        if (0 != fdevent_fcntl_set_nb(fds[1])) { /*(ignore)*/ }
        return fdlog_pipe_init(fn, fds, pid);
    }
    else {
        int errnum = errno;
        close(fds[0]);
        close(fds[1]);
        errno = errnum;
        return NULL;
    }
}


static fdlog_st *
fdlog_file_init (const char * const fn, const int fd)
{
    if (!(fdlog_files.used & (4-1)))
        ck_realloc_u32((void **)&fdlog_files.ptr, fdlog_files.used,
                       4, sizeof(*fdlog_files.ptr));
    return (fdlog_files.ptr[fdlog_files.used++] = fdlog_init(fn,fd,FDLOG_FILE));
}


static int
fdlog_file_open_fd (const char * const fn)
{
    int flags = O_APPEND | O_WRONLY | O_CREAT;
    return fdevent_open_cloexec(fn, 1, flags, 0644); /*(permit symlinks)*/
}


static fdlog_st *
fdlog_file_open (const char * const fn)
{
    for (uint32_t i = 0; i < fdlog_files.used; ++i) {
        fdlog_st * const fdlog = fdlog_files.ptr[i];
        if (0 != strcmp(fdlog->fn, fn)) continue;
        return fdlog;
    }

    int fd = fdlog_file_open_fd(fn);
    return (-1 != fd) ? fdlog_file_init(fn, fd) : NULL;
}


fdlog_st *
fdlog_open (const char * const fn)
{
    return (fn[0] != '|')
      ? fdlog_file_open(fn)
      : fdlog_pipe_open(fn+1); /*(skip the '|')*/
}


void
fdlog_files_flush (fdlog_st * const errh, const int memrel)
{
    for (uint32_t i = 0; i < fdlog_files.used; ++i) {
        fdlog_st * const fdlog = fdlog_files.ptr[i];
        buffer * const b = &fdlog->b;
        if (!buffer_is_blank(b)) {
            const ssize_t wr = write_all(fdlog->fd, BUF_PTR_LEN(b));
            buffer_clear(b); /*(clear buffer, even on error)*/
            if (-1 == wr)
                log_perror(errh, __FILE__, __LINE__,
                  "error flushing log %s", fdlog->fn);
        }
        if (memrel && b->ptr) buffer_free_ptr(b);
    }
}


void
fdlog_files_cycle (fdlog_st * const errh)
{
    fdlog_files_flush(errh, 0);
    for (uint32_t i = 0; i < fdlog_files.used; ++i) {
        fdlog_st * const fdlog = fdlog_files.ptr[i];
        int fd = fdlog_file_open_fd(fdlog->fn);
        if (-1 != fd) {
            if (fdlog->fd > STDERR_FILENO) {
                close(fdlog->fd);
                fdlog->fd = fd;
            }
            else {
                if (fdlog->fd != dup2(fd, fdlog->fd))
                    log_perror(errh, __FILE__, __LINE__,
                      "dup2() %s to %d", fdlog->fn, fdlog->fd);
                close(fd);
              #ifdef _WIN32
                SetStdHandle(STD_ERROR_HANDLE,
                             (HANDLE)_get_osfhandle(STDERR_FILENO));
              #endif
            }
        }
        else {
            log_perror(errh, __FILE__, __LINE__,
              "error cycling log %s", fdlog->fn);
            /*(leave prior log file open)*/
        }
    }
}


static void
fdlog_files_close (fdlog_st * const retain)
{
    fdlog_files_flush(retain, 0);
    for (uint32_t i = 0; i < fdlog_files.used; ++i) {
        fdlog_st * const fdlog = fdlog_files.ptr[i];
        if (fdlog == retain) continue; /*(free'd later)*/
        fdlog_free(fdlog);
    }
    free(fdlog_files.ptr);
    fdlog_files.ptr = NULL;
    fdlog_files.used = 0;
}


void
fdlog_closeall (fdlog_st * const errh)
{
    fdlog_files_close(errh);
    fdlog_pipes_close(errh);
}


void
fdlog_flushall (fdlog_st * const errh)
{
    fdlog_files_flush(errh, 1); /*(flush, then release buffer memory)*/
    /*(at the moment, pipe loggers clear buffer after each write attempt,
     * so there is nothing to flush, though there are buffers to be freed)*/
    for (uint32_t i = 0; i < fdlog_pipes.used; ++i) {
        buffer * const b = &fdlog_pipes.ptr[i].fdlog->b;
        if (b->ptr) buffer_free_ptr(b);
    }
    if (errh->b.ptr) buffer_free_ptr(&errh->b);
}