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 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 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 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 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 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 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 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 * 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 * 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 * 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 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 * 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 * 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 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 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 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 309 fdlog_closeall (fdlog_st * const errh) 310 { 311 fdlog_files_close(errh); 312 fdlog_pipes_close(errh); 313 } 314 315 316 void 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