s6-log.c (36269B)
1 /* ISC license. */ 2 3 #include <fcntl.h> 4 #include <sys/stat.h> 5 #include <sys/wait.h> 6 #include <stdint.h> 7 #include <string.h> 8 #include <errno.h> 9 #include <signal.h> 10 #include <unistd.h> 11 #include <stdio.h> 12 #include <stdlib.h> 13 #include <regex.h> 14 15 #include <skalibs/posixplz.h> 16 #include <skalibs/uint64.h> 17 #include <skalibs/types.h> 18 #include <skalibs/allreadwrite.h> 19 #include <skalibs/buffer.h> 20 #include <skalibs/bytestr.h> 21 #include <skalibs/error.h> 22 #include <skalibs/sgetopt.h> 23 #include <skalibs/strerr.h> 24 #include <skalibs/bufalloc.h> 25 #include <skalibs/stralloc.h> 26 #include <skalibs/tai.h> 27 #include <skalibs/djbtime.h> 28 #include <skalibs/iopause.h> 29 #include <skalibs/djbunix.h> 30 #include <skalibs/direntry.h> 31 #include <skalibs/sig.h> 32 #include <skalibs/selfpipe.h> 33 #include <skalibs/siovec.h> 34 #include <skalibs/cspawn.h> 35 36 #include <s6/config.h> 37 38 #ifdef S6_USE_EXECLINE 39 #include <execline/config.h> 40 #endif 41 42 #define USAGE "s6-log [ -d notif ] [ -q | -v ] [ -b ] [ -p ] [ -l linelimit ] [ -t lastlinetimeout ] [ -- ] logging_script" 43 #define dieusage() strerr_dieusage(100, USAGE) 44 #define dienomem() strerr_diefu1sys(111, "stralloc_catb") 45 46 #define LINELIMIT_MIN 48 47 48 static mode_t mask ; 49 static int flagprotect = 0 ; 50 static int flagexiting = 0 ; 51 static unsigned int verbosity = 1 ; 52 static tain lastlinetto = TAIN_INFINITE_RELATIVE ; 53 static tain exit_deadline = TAIN_INFINITE ; 54 55 static stralloc indata = STRALLOC_ZERO ; 56 57 /* Data types */ 58 59 typedef int qcmp_func (void const *, void const *) ; 60 typedef qcmp_func *qcmp_func_ref ; 61 62 typedef enum rotstate_e rotstate_t, *rotstate_t_ref ; 63 enum rotstate_e 64 { 65 ROTSTATE_WRITABLE, 66 ROTSTATE_START, 67 ROTSTATE_RENAME, 68 ROTSTATE_NEWCURRENT, 69 ROTSTATE_CHMODPREVIOUS, 70 ROTSTATE_FINISHPREVIOUS, 71 ROTSTATE_RUNPROCESSOR, 72 ROTSTATE_WAITPROCESSOR, 73 ROTSTATE_SYNCPROCESSED, 74 ROTSTATE_SYNCNEWSTATE, 75 ROTSTATE_UNLINKPREVIOUS, 76 ROTSTATE_RENAMESTATE, 77 ROTSTATE_FINISHPROCESSED, 78 ROTSTATE_ENDFCHMOD, 79 ROTSTATE_END 80 } ; 81 82 typedef enum seltype_e seltype_t, *seltype_t_ref ; 83 enum seltype_e 84 { 85 SELTYPE_DEFAULT, 86 SELTYPE_PLUS, 87 SELTYPE_MINUS, 88 SELTYPE_PHAIL 89 } ; 90 91 typedef struct sel_s sel_t, *sel_t_ref ; 92 struct sel_s 93 { 94 seltype_t type ; 95 regex_t re ; 96 } ; 97 98 #define SEL_ZERO { .type = SELTYPE_PHAIL } 99 100 typedef enum acttype_e acttype_t, *acttype_t_ref ; 101 enum acttype_e 102 { 103 ACTTYPE_NOTHING, 104 ACTTYPE_FD1, 105 ACTTYPE_FD2, 106 ACTTYPE_STATUS, 107 ACTTYPE_DIR, 108 ACTTYPE_PHAIL 109 } ; 110 111 typedef struct as_status_s as_status_t, *as_status_t_ref ; 112 struct as_status_s 113 { 114 char const *file ; 115 size_t filelen ; 116 } ; 117 118 typedef union actstuff_u actstuff_t, *actstuff_t_ref ; 119 union actstuff_u 120 { 121 size_t fd2_size ; 122 as_status_t status ; 123 unsigned int ld ; 124 } ; 125 126 typedef struct act_s act_t, *act_t_ref ; 127 struct act_s 128 { 129 acttype_t type ; 130 actstuff_t data ; 131 unsigned int flags ; 132 char const *prefix ; 133 size_t prefixlen ; 134 } ; 135 136 typedef struct scriptelem_s scriptelem_t, *scriptelem_t_ref ; 137 struct scriptelem_s 138 { 139 sel_t const *sels ; 140 unsigned int sellen ; 141 act_t const *acts ; 142 unsigned int actlen ; 143 } ; 144 145 typedef void inputproc_func (scriptelem_t const *, unsigned int, size_t, unsigned int) ; 146 typedef inputproc_func *inputproc_func_ref ; 147 148 typedef struct logdir_s logdir_t, *logdir_t_ref ; 149 struct logdir_s 150 { 151 bufalloc out ; 152 unsigned int xindex ; 153 tain retrytto ; 154 tain deadline ; 155 uint64_t maxdirsize ; 156 uint32_t b ; 157 uint32_t n ; 158 uint32_t s ; 159 uint32_t tolerance ; 160 pid_t pid ; 161 char const *dir ; 162 char const *processor ; 163 unsigned int flags ; 164 int fd ; 165 int fdlock ; 166 rotstate_t rstate ; 167 } ; 168 169 #define LOGDIR_ZERO { \ 170 .out = BUFALLOC_ZERO, \ 171 .xindex = 0, \ 172 .retrytto = TAIN_ZERO, \ 173 .deadline = TAIN_ZERO, \ 174 .maxdirsize = 0, \ 175 .b = 0, \ 176 .n = 0, \ 177 .s = 0, \ 178 .tolerance = 0, \ 179 .pid = 0, \ 180 .dir = 0, \ 181 .processor = 0, \ 182 .fd = -1, \ 183 .fdlock = -1, \ 184 .rstate = ROTSTATE_WRITABLE \ 185 } 186 187 struct filedesc_s 188 { 189 off_t size ; 190 char name[28] ; 191 } ; 192 193 194 /* Logdirs */ 195 196 static logdir_t *logdirs ; 197 static unsigned int llen = 0 ; 198 199 static int filedesc_cmp (struct filedesc_s const *a, struct filedesc_s const *b) 200 { 201 return memcmp(a->name+1, b->name+1, 26) ; 202 } 203 204 static int name_is_relevant (char const *name) 205 { 206 tain dummy ; 207 if (strlen(name) != 27) return 0 ; 208 if (!timestamp_scan(name, &dummy)) return 0 ; 209 if (name[25] != '.') return 0 ; 210 if ((name[26] != 's') && (name[26] != 'u')) return 0 ; 211 return 1 ; 212 } 213 214 static inline int logdir_trim (logdir_t *ldp) 215 { 216 unsigned int n = 0 ; 217 DIR *dir = opendir(ldp->dir) ; 218 if (!dir) return -1 ; 219 for (;;) 220 { 221 direntry *d ; 222 errno = 0 ; 223 d = readdir(dir) ; 224 if (!d) break ; 225 if (name_is_relevant(d->d_name)) n++ ; 226 } 227 if (errno) 228 { 229 dir_close(dir) ; 230 return -1 ; 231 } 232 if (!n) 233 { 234 dir_close(dir) ; 235 return 0 ; 236 } 237 238 rewinddir(dir) ; 239 240 { 241 uint64_t totalsize = 0 ; 242 size_t dirlen = strlen(ldp->dir) ; 243 unsigned int i = 0 ; 244 struct filedesc_s archive[n] ; 245 char fullname[dirlen + 29] ; 246 memcpy(fullname, ldp->dir, dirlen) ; 247 fullname[dirlen] = '/' ; 248 for (;;) 249 { 250 struct stat st ; 251 direntry *d ; 252 errno = 0 ; 253 d = readdir(dir) ; 254 if (!d) break ; 255 if (!name_is_relevant(d->d_name)) continue ; 256 if (i >= n) { errno = EBUSY ; break ; } 257 memcpy(fullname + dirlen + 1, d->d_name, 28) ; 258 if (stat(fullname, &st) < 0) 259 { 260 if (verbosity) strerr_warnwu2sys("stat ", fullname) ; 261 continue ; 262 } 263 memcpy(archive[i].name, d->d_name, 28) ; 264 archive[i].size = st.st_size ; 265 totalsize += st.st_size ; 266 i++ ; 267 } 268 if (errno) 269 { 270 dir_close(dir) ; 271 return -1 ; 272 } 273 dir_close(dir) ; 274 if ((i <= ldp->n) && (!ldp->maxdirsize || (totalsize <= ldp->maxdirsize))) 275 return 0 ; 276 qsort(archive, i, sizeof(struct filedesc_s), (qcmp_func_ref)&filedesc_cmp) ; 277 n = 0 ; 278 while ((i > ldp->n + n) || (ldp->maxdirsize && (totalsize > ldp->maxdirsize))) 279 { 280 memcpy(fullname + dirlen + 1, archive[n].name, 28) ; 281 if (unlink(fullname) < 0) 282 { 283 if (errno == ENOENT) totalsize -= archive[n].size ; 284 if (verbosity) strerr_warnwu2sys("unlink ", fullname) ; 285 } 286 else totalsize -= archive[n].size ; 287 n++ ; 288 } 289 } 290 return n ; 291 } 292 293 static int finish (logdir_t *ldp, char const *name, char suffix) 294 { 295 struct stat st ; 296 size_t dirlen = strlen(ldp->dir) ; 297 size_t namelen = strlen(name) ; 298 char x[dirlen + namelen + 2] ; 299 memcpy(x, ldp->dir, dirlen) ; 300 x[dirlen] = '/' ; 301 memcpy(x + dirlen + 1, name, namelen + 1) ; 302 if (stat(x, &st) < 0) return errno == ENOENT ? 0 : -1 ; 303 if (st.st_nlink == 1) 304 { 305 char y[dirlen + 29] ; 306 memcpy(y, ldp->dir, dirlen) ; 307 y[dirlen] = '/' ; 308 timestamp_g(y + dirlen + 1) ; 309 y[dirlen + 26] = '.' ; 310 y[dirlen + 27] = suffix ; 311 y[dirlen + 28] = 0 ; 312 if (link(x, y) < 0) return -1 ; 313 } 314 if (unlink(x) < 0) return -1 ; 315 return logdir_trim(ldp) ; 316 } 317 318 static int rotator (logdir_t *ldp) 319 { 320 size_t dirlen = strlen(ldp->dir) ; 321 switch (ldp->rstate) 322 { 323 case ROTSTATE_START : 324 if (fd_sync(ldp->fd) < 0) 325 { 326 if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; 327 goto fail ; 328 } 329 tain_now_g() ; 330 ldp->rstate = ROTSTATE_RENAME ; 331 case ROTSTATE_RENAME : 332 { 333 char current[dirlen + 9] ; 334 char previous[dirlen + 10] ; 335 memcpy(current, ldp->dir, dirlen) ; 336 memcpy(current + dirlen, "/current", 9) ; 337 memcpy(previous, ldp->dir, dirlen) ; 338 memcpy(previous + dirlen, "/previous", 10) ; 339 if (rename(current, previous) < 0) 340 { 341 if (verbosity) strerr_warnwu4sys("rename ", current, " to ", previous) ; 342 goto fail ; 343 } 344 ldp->rstate = ROTSTATE_NEWCURRENT ; 345 } 346 case ROTSTATE_NEWCURRENT : 347 { 348 int fd ; 349 char x[dirlen + 9] ; 350 memcpy(x, ldp->dir, dirlen) ; 351 memcpy(x + dirlen, "/current", 9) ; 352 fd = openc_append(x) ; 353 if (fd < 0) 354 { 355 if (verbosity) strerr_warnwu2sys("open_append ", x) ; 356 goto fail ; 357 } 358 fd_close(ldp->fd) ; 359 ldp->fd = fd ; 360 ldp->b = 0 ; 361 ldp->rstate = ROTSTATE_CHMODPREVIOUS ; 362 } 363 case ROTSTATE_CHMODPREVIOUS : 364 { 365 char x[dirlen + 10] ; 366 memcpy(x, ldp->dir, dirlen) ; 367 memcpy(x + dirlen, "/previous", 10) ; 368 if (chmod(x, mask | S_IXUSR) < 0) 369 { 370 if (verbosity) strerr_warnwu2sys("chmod ", x) ; 371 goto fail ; 372 } 373 if (ldp->processor) goto runprocessor ; 374 ldp->rstate = ROTSTATE_FINISHPREVIOUS ; 375 } 376 case ROTSTATE_FINISHPREVIOUS : 377 if (finish(ldp, "previous", 's') < 0) 378 { 379 if (verbosity) strerr_warnwu2sys("finish previous .s to logdir ", ldp->dir) ; 380 goto fail ; 381 } 382 tain_copynow(&ldp->deadline) ; 383 ldp->rstate = ROTSTATE_WRITABLE ; 384 break ; 385 runprocessor: 386 ldp->rstate = ROTSTATE_RUNPROCESSOR ; 387 case ROTSTATE_RUNPROCESSOR : 388 { 389 #ifdef S6_USE_EXECLINE 390 char const *cargv[4] = { ldp->flags & 4 ? "/bin/sh" : EXECLINE_EXTBINPREFIX "execlineb", ldp->flags & 4 ? "-c" : "-Pc", ldp->processor, 0 } ; 391 #else 392 char const *cargv[4] = { "/bin/sh", "-c", ldp->processor, 0 } ; 393 #endif 394 cspawn_fileaction fa[5] = 395 { 396 [0] = { .type = CSPAWN_FA_CHDIR, .x = { .path = ldp->dir } }, 397 [1] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 0, .file = "previous", .oflag = O_RDONLY, .mode = 0644 } } }, 398 [2] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 1, .file = "processed", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } }, 399 [3] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 4, .file = "state", .oflag = O_RDONLY, .mode = 0644 } } }, 400 [4] = { .type = CSPAWN_FA_OPEN, .x = { .openinfo = { .fd = 5, .file = "newstate", .oflag = O_WRONLY | O_CREAT | O_TRUNC, .mode = 0666 } } } 401 } ; 402 pid_t pid = cspawn(cargv[0], cargv, (char const *const *)environ, CSPAWN_FLAGS_SELFPIPE_FINISH, fa, 5) ; 403 if (!pid) 404 { 405 if (verbosity) strerr_warnwu2sys("spawn processor for logdir ", ldp->dir) ; 406 goto fail ; 407 } 408 ldp->pid = pid ; 409 tain_add_g(&ldp->deadline, &tain_infinite_relative) ; 410 ldp->rstate = ROTSTATE_WAITPROCESSOR ; 411 } 412 case ROTSTATE_WAITPROCESSOR : 413 return (errno = EAGAIN, 0) ; 414 case ROTSTATE_SYNCPROCESSED : 415 { 416 int fd ; 417 char x[dirlen + 11] ; 418 memcpy(x, ldp->dir, dirlen) ; 419 memcpy(x + dirlen, "/processed", 11) ; 420 fd = open_append(x) ; 421 if (fd < 0) 422 { 423 if (verbosity) strerr_warnwu2sys("open_append ", x) ; 424 goto fail ; 425 } 426 if (fd_sync(fd) < 0) 427 { 428 fd_close(fd) ; 429 if (verbosity) strerr_warnwu2sys("fd_sync ", x) ; 430 goto fail ; 431 } 432 tain_now_g() ; 433 if (fd_chmod(fd, mask | S_IXUSR) < 0) 434 { 435 fd_close(fd) ; 436 if (verbosity) strerr_warnwu2sys("fd_chmod ", x) ; 437 goto fail ; 438 } 439 fd_close(fd) ; 440 ldp->rstate = ROTSTATE_SYNCNEWSTATE ; 441 } 442 case ROTSTATE_SYNCNEWSTATE : 443 { 444 int fd ; 445 char x[dirlen + 10] ; 446 memcpy(x, ldp->dir, dirlen) ; 447 memcpy(x + dirlen, "/newstate", 10) ; 448 fd = open_append(x) ; 449 if (fd < 0) 450 { 451 if (verbosity) strerr_warnwu2sys("open_append ", x) ; 452 goto fail ; 453 } 454 if (fd_sync(fd) < 0) 455 { 456 if (verbosity) strerr_warnwu2sys("fd_sync ", x) ; 457 goto fail ; 458 } 459 tain_now_g() ; 460 fd_close(fd) ; 461 ldp->rstate = ROTSTATE_UNLINKPREVIOUS ; 462 } 463 case ROTSTATE_UNLINKPREVIOUS : 464 { 465 char x[dirlen + 10] ; 466 memcpy(x, ldp->dir, dirlen) ; 467 memcpy(x + dirlen, "/previous", 10) ; 468 if ((unlink(x) < 0) && (errno != ENOENT)) 469 { 470 if (verbosity) strerr_warnwu2sys("open_append ", x) ; 471 goto fail ; 472 } 473 ldp->rstate = ROTSTATE_RENAMESTATE ; 474 } 475 case ROTSTATE_RENAMESTATE : 476 { 477 char newstate[dirlen + 10] ; 478 char state[dirlen + 7] ; 479 memcpy(newstate, ldp->dir, dirlen) ; 480 memcpy(state, ldp->dir, dirlen) ; 481 memcpy(newstate + dirlen, "/newstate", 10) ; 482 memcpy(state + dirlen, "/state", 7) ; 483 if (rename(newstate, state) < 0) 484 { 485 if (verbosity) strerr_warnwu4sys("rename ", newstate, " to ", state) ; 486 goto fail ; 487 } 488 ldp->rstate = ROTSTATE_FINISHPROCESSED ; 489 } 490 case ROTSTATE_FINISHPROCESSED : 491 if (finish(ldp, "processed", 's') < 0) 492 { 493 if (verbosity) strerr_warnwu2sys("finish processed .s to logdir ", ldp->dir) ; 494 goto fail ; 495 } 496 tain_copynow(&ldp->deadline) ; 497 ldp->rstate = ROTSTATE_WRITABLE ; 498 break ; 499 default : strerr_dief1x(101, "inconsistent state in rotator()") ; 500 } 501 return 1 ; 502 fail: 503 tain_add_g(&ldp->deadline, &ldp->retrytto) ; 504 return 0 ; 505 } 506 507 static ssize_t logdir_write (int i, char const *s, size_t len) 508 { 509 logdir_t *ldp = logdirs + i ; 510 ssize_t r ; 511 size_t n = len ; 512 { 513 size_t m = byte_rchr(s, n, '\n') ; 514 if (m < n) n = m+1 ; 515 } 516 r = fd_write(ldp->fd, s, n) ; 517 if (r < 0) 518 { 519 if (!error_isagain(errno)) 520 { 521 tain_add_g(&ldp->deadline, &ldp->retrytto) ; 522 if (verbosity) strerr_warnwu3sys("write to ", ldp->dir, "/current") ; 523 } 524 return r ; 525 } 526 ldp->b += r ; 527 if ((ldp->b + ldp->tolerance >= ldp->s) && (s[r-1] == '\n')) 528 { 529 ldp->rstate = ROTSTATE_START ; 530 rotator(ldp) ; 531 } 532 return r ; 533 } 534 535 static inline void rotate_or_flush (logdir_t *ldp) 536 { 537 if ((ldp->rstate != ROTSTATE_WRITABLE) && !rotator(ldp)) return ; 538 if (ldp->b >= ldp->s) 539 { 540 ldp->rstate = ROTSTATE_START ; 541 if (!rotator(ldp)) return ; 542 } 543 bufalloc_flush(&ldp->out) ; 544 } 545 546 static inline void logdir_init (unsigned int index, uint32_t s, uint32_t n, uint32_t tolerance, uint64_t maxdirsize, tain const *retrytto, char const *processor, char const *name, unsigned int flags) 547 { 548 logdir_t *ldp = logdirs + index ; 549 struct stat st ; 550 size_t dirlen = strlen(name) ; 551 int r ; 552 char x[dirlen + 11] ; 553 ldp->s = s ; 554 ldp->n = n ; 555 ldp->pid = 0 ; 556 ldp->tolerance = tolerance ; 557 ldp->maxdirsize = maxdirsize ; 558 ldp->retrytto = *retrytto ; 559 ldp->processor = processor ; 560 ldp->flags = flags ; 561 ldp->dir = name ; 562 ldp->fd = -1 ; 563 ldp->rstate = ROTSTATE_WRITABLE ; 564 r = mkdir(ldp->dir, S_IRWXU | S_ISGID) ; 565 if (r < 0 && errno != EEXIST) strerr_diefu2sys(111, "mkdir ", name) ; 566 memcpy(x, name, dirlen) ; 567 memcpy(x + dirlen, "/lock", 6) ; 568 ldp->fdlock = openc_create(x) ; 569 if (ldp->fdlock < 0) strerr_diefu2sys(111, "open ", x) ; 570 r = fd_lock(ldp->fdlock, 1, 1) ; 571 if (!r) errno = EBUSY ; 572 if (r < 1) strerr_diefu2sys(111, "lock ", x) ; 573 memcpy(x + dirlen + 1, "current", 8) ; 574 if (stat(x, &st) < 0) 575 { 576 if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ; 577 } 578 else if (st.st_mode & S_IXUSR) goto opencurrent ; 579 memcpy(x + dirlen + 1, "state", 6) ; 580 unlink_void(x) ; 581 memcpy(x + dirlen + 1, "newstate", 9) ; 582 unlink_void(x) ; 583 { 584 int flagprocessed = 0 ; 585 memcpy(x + dirlen + 1, "processed", 10) ; 586 if (stat(x, &st) < 0) 587 { 588 if (errno != ENOENT) strerr_diefu2sys(111, "stat ", x) ; 589 } 590 else if (st.st_mode & S_IXUSR) flagprocessed = 1 ; 591 if (flagprocessed) 592 { 593 memcpy(x + dirlen + 1, "previous", 9) ; 594 unlink_void(x) ; 595 if (finish(ldp, "processed", 's') < 0) 596 strerr_diefu2sys(111, "finish processed .s for logdir ", ldp->dir) ; 597 } 598 else 599 { 600 unlink_void(x) ; 601 if (finish(ldp, "previous", 'u') < 0) 602 strerr_diefu2sys(111, "finish previous .u for logdir ", ldp->dir) ; 603 } 604 } 605 if (finish(ldp, "current", 'u') < 0) 606 strerr_diefu2sys(111, "finish current .u for logdir ", ldp->dir) ; 607 memcpy(x + dirlen + 1, "state", 6) ; 608 r = open_trunc(x) ; 609 if (r == -1) strerr_diefu2sys(111, "open_trunc ", x) ; 610 fd_close(r) ; 611 st.st_size = 0 ; 612 memcpy(x + dirlen + 1, "current", 8) ; 613 opencurrent: 614 ldp->fd = openc_append(x) ; 615 if (ldp->fd < 0) strerr_diefu2sys(111, "open_append ", x) ; 616 if (fd_chmod(ldp->fd, mask) == -1) 617 strerr_diefu2sys(111, "fd_chmod ", x) ; 618 ldp->b = st.st_size ; 619 tain_copynow(&ldp->deadline) ; 620 bufalloc_init(&ldp->out, &logdir_write, index) ; 621 } 622 623 static inline int logdir_finalize (logdir_t *ldp) 624 { 625 switch (ldp->rstate) 626 { 627 case ROTSTATE_WRITABLE : 628 { 629 if (fd_sync(ldp->fd) < 0) 630 { 631 if (verbosity) strerr_warnwu3sys("fd_sync ", ldp->dir, "/current") ; 632 goto fail ; 633 } 634 tain_now_g() ; 635 ldp->rstate = ROTSTATE_ENDFCHMOD ; 636 } 637 case ROTSTATE_ENDFCHMOD : 638 { 639 if (fd_chmod(ldp->fd, mask | S_IXUSR) < 0) 640 { 641 if (verbosity) strerr_warnwu3sys("fd_chmod ", ldp->dir, "/current") ; 642 goto fail ; 643 } 644 ldp->rstate = ROTSTATE_END ; 645 break ; 646 } 647 default : strerr_dief1x(101, "inconsistent state in logdir_finalize()") ; 648 } 649 return 1 ; 650 fail: 651 tain_add_g(&ldp->deadline, &ldp->retrytto) ; 652 return 0 ; 653 } 654 655 static inline void finalize (void) 656 { 657 unsigned int n = llen ; 658 for (;;) 659 { 660 unsigned int i = 0 ; 661 tain deadline ; 662 tain_addsec_g(&deadline, 2) ; 663 for (; i < llen ; i++) 664 if (logdirs[i].rstate != ROTSTATE_END) 665 { 666 if (logdir_finalize(logdirs + i)) n-- ; 667 else if (tain_less(&logdirs[i].deadline, &deadline)) 668 deadline = logdirs[i].deadline ; 669 } 670 if (!n) break ; 671 { 672 iopause_fd x ; 673 iopause_g(&x, 0, &deadline) ; 674 } 675 } 676 } 677 678 679 /* Script */ 680 681 static inline void script_firstpass (char const *const *argv, unsigned int *sellen, unsigned int *actlen, unsigned int *scriptlen, unsigned int *gflags) 682 { 683 unsigned int se = 0, ac = 0, sc = 0, gf = *gflags ; 684 int flagacted = 0 ; 685 for (; *argv ; argv++) 686 { 687 switch ((*argv)[0]) 688 { 689 case 'f' : 690 if ((*argv)[1]) goto fail ; 691 case '+' : 692 case '-' : 693 if (flagacted) 694 { 695 sc++ ; 696 flagacted = 0 ; 697 } 698 se++ ; 699 case 'n' : 700 case 's' : 701 case 'S' : 702 case 'l' : 703 case 'r' : 704 case 'E' : 705 case '^' : 706 case 'p' : 707 #ifdef S6_USE_EXECLINE 708 case '!' : 709 #endif 710 case '?' : 711 break ; 712 case 't' : 713 if ((*argv)[1]) goto fail ; 714 gf |= 1 ; 715 break ; 716 case 'T' : 717 if ((*argv)[1]) goto fail ; 718 gf |= 2 ; 719 break ; 720 case '1' : 721 case '2' : 722 if ((*argv)[1]) goto fail ; 723 flagacted = 1 ; 724 ac++ ; 725 break ; 726 case '.' : 727 case '/' : 728 llen++ ; 729 flagacted = 1 ; 730 ac++ ; 731 break ; 732 case '=' : 733 if (!(*argv)[1]) goto fail ; 734 flagacted = 1 ; 735 ac++ ; 736 break ; 737 default : strerr_dief2x(100, "unrecognized directive: ", *argv) ; 738 } 739 } 740 if (flagacted) sc++ ; 741 else if (sc) 742 { 743 if (verbosity) 744 strerr_warnw1x("ignoring extraneous non-action directives") ; 745 } 746 else strerr_dief1x(100, "no action directive specified") ; 747 *sellen = se ; 748 *actlen = ac ; 749 *scriptlen = sc ; 750 *gflags = gf ; 751 return ; 752 fail : 753 strerr_dief2x(100, "syntax error at directive: ", *argv) ; 754 } 755 756 static inline void script_secondpass (char const *const *argv, scriptelem_t *script, sel_t *selections, act_t *actions) 757 { 758 tain retrytto ; 759 unsigned int fd2_size = 200 ; 760 unsigned int status_size = 1001 ; 761 uint32_t s = 99999 ; 762 uint32_t n = 10 ; 763 uint32_t tolerance = 2000 ; 764 uint64_t maxdirsize = 0 ; 765 char const *processor = 0 ; 766 char const *prefix = 0 ; 767 size_t prefixlen = 0 ; 768 unsigned int sel = 0, act = 0, lidx = 0, flags = 0 ; 769 int flagacted = 0 ; 770 tain_uint(&retrytto, 2) ; 771 772 for (; *argv ; argv++) 773 { 774 switch (**argv) 775 { 776 case 'f' : 777 case '+' : 778 case '-' : 779 { 780 sel_t selitem = { .type = (*argv)[0] != 'f' ? (*argv)[0] == '+' ? SELTYPE_PLUS : SELTYPE_MINUS : SELTYPE_DEFAULT } ; 781 if ((*argv)[0] != 'f') 782 { 783 int r = skalibs_regcomp(&selitem.re, *argv + 1, REG_EXTENDED | REG_NOSUB | REG_NEWLINE) ; 784 if (r == REG_ESPACE) 785 { 786 errno = ENOMEM ; 787 strerr_diefu1sys(111, "initialize script") ; 788 } 789 if (r) goto fail ; 790 } 791 if (flagacted) 792 { 793 flagacted = 0 ; 794 script->sels = selections ; 795 script->sellen = sel ; 796 script->acts = actions ; 797 script->actlen = act ; 798 selections += sel ; sel = 0 ; 799 actions += act ; act = 0 ; 800 script++ ; 801 } 802 selections[sel++] = selitem ; 803 break ; 804 } 805 case 'n' : 806 if (!uint320_scan(*argv + 1, &n)) goto fail ; 807 break ; 808 case 's' : 809 if (!uint320_scan(*argv + 1, &s)) goto fail ; 810 if (s < 4096) s = 4096 ; 811 if (s > 268435455) s = 268435455 ; 812 break ; 813 case 'S' : 814 if (!uint640_scan(*argv + 1, &maxdirsize)) goto fail ; 815 break ; 816 case 'l' : 817 if (!uint320_scan(*argv + 1, &tolerance)) goto fail ; 818 if (tolerance > (s >> 1)) 819 strerr_dief3x(100, "directive ", *argv, " conflicts with previous s directive") ; 820 break ; 821 case 'r' : 822 { 823 uint32_t t ; 824 if (!uint320_scan(*argv + 1, &t)) goto fail ; 825 if (!tain_from_millisecs(&retrytto, t)) goto fail ; 826 break ; 827 } 828 case 'E' : 829 if (!uint0_scan(*argv + 1, &fd2_size)) goto fail ; 830 break ; 831 case '^' : 832 if (!uint0_scan(*argv + 1, &status_size)) goto fail ; 833 break ; 834 case 'p' : 835 if ((*argv)[1]) { prefix = *argv + 1 ; prefixlen = strlen(prefix) ; } 836 else { prefix = 0 ; prefixlen = 0 ; } 837 break ; 838 #ifdef S6_USE_EXECLINE 839 case '!' : 840 processor = (*argv)[1] ? *argv + 1 : 0 ; 841 flags &= ~4 ; 842 break ; 843 #endif 844 case '?' : 845 processor = (*argv)[1] ? *argv + 1 : 0 ; 846 flags |= 4 ; 847 break ; 848 case 't' : 849 flags |= 1 ; 850 break ; 851 case 'T' : 852 flags |= 2 ; 853 break ; 854 case '1' : 855 { 856 act_t a = { .type = ACTTYPE_FD1, .flags = flags, .prefix = prefix, .prefixlen = prefixlen } ; 857 actions[act++] = a ; flagacted = 1 ; flags = 0 ; 858 break ; 859 } 860 case '2' : 861 { 862 act_t a = { .type = ACTTYPE_FD2, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .fd2_size = fd2_size } } ; 863 actions[act++] = a ; flagacted = 1 ; flags = 0 ; 864 break ; 865 } 866 case '=' : 867 { 868 act_t a = { .type = ACTTYPE_STATUS, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .status = { .file = *argv + 1, .filelen = status_size } } } ; 869 actions[act++] = a ; flagacted = 1 ; flags = 0 ; 870 break ; 871 } 872 case '.' : 873 case '/' : 874 { 875 act_t a = { .type = ACTTYPE_DIR, .flags = flags, .prefix = prefix, .prefixlen = prefixlen, .data = { .ld = lidx } } ; 876 logdir_init(lidx, s, n, tolerance, maxdirsize, &retrytto, processor, *argv, flags) ; 877 lidx++ ; 878 actions[act++] = a ; flagacted = 1 ; flags = 0 ; 879 break ; 880 } 881 default : goto fail ; 882 } 883 } 884 if (flagacted) 885 { 886 script->sels = selections ; 887 script->sellen = sel ; 888 script->acts = actions ; 889 script->actlen = act ; 890 } 891 return ; 892 fail: 893 strerr_dief2x(100, "unrecognized directive: ", *argv) ; 894 } 895 896 static void script_run (scriptelem_t const *script, unsigned int scriptlen, char const *s, size_t len, unsigned int gflags) 897 { 898 int flagselected = 1, flagacted = 0 ; 899 unsigned int i = 0 ; 900 size_t hlen = 0 ; 901 char hstamp[32] ; 902 char tstamp[TIMESTAMP+1] ; 903 if (gflags & 3) 904 { 905 tain now ; 906 tain_wallclock_read(&now) ; 907 if (gflags & 1) 908 { 909 timestamp_fmt(tstamp, &now) ; 910 tstamp[TIMESTAMP] = ' ' ; 911 } 912 if (gflags & 2) 913 { 914 localtmn l ; 915 localtmn_from_tain(&l, &now, 1) ; 916 hlen = localtmn_fmt(hstamp, &l) ; 917 hstamp[hlen++] = ' ' ; 918 hstamp[hlen++] = ' ' ; 919 } 920 } 921 922 for (; i < scriptlen ; i++) 923 { 924 unsigned int j = 0 ; 925 for (; j < script[i].sellen ; j++) 926 { 927 switch (script[i].sels[j].type) 928 { 929 case SELTYPE_DEFAULT : 930 flagselected = !flagacted ; 931 break ; 932 case SELTYPE_PLUS : 933 if (!flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 1 ; 934 break ; 935 case SELTYPE_MINUS : 936 if (flagselected && !regexec(&script[i].sels[j].re, s, 0, 0, 0)) flagselected = 0 ; 937 break ; 938 default : 939 strerr_dief2x(101, "internal consistency error in ", "selection type") ; 940 } 941 } 942 if (flagselected) 943 { 944 flagacted = 1 ; 945 for (j = 0 ; j < script[i].actlen ; j++) 946 { 947 act_t const *act = script[i].acts + j ; 948 unsigned int m = 0 ; 949 struct iovec v[6] ; 950 if (act->flags & 1) 951 { 952 v[m].iov_base = tstamp ; 953 v[m++].iov_len = TIMESTAMP+1 ; 954 } 955 if (act->flags & 2) 956 { 957 v[m].iov_base = hstamp ; 958 v[m++].iov_len = hlen ; 959 } 960 if (act->prefix) 961 { 962 v[m].iov_base = (char *)act->prefix ; 963 v[m++].iov_len = act->prefixlen ; 964 v[m].iov_base = " " ; 965 v[m++].iov_len = 1 ; 966 } 967 v[m].iov_base = (char *)s ; 968 v[m++].iov_len = len ; 969 v[m].iov_base = "\n" ; 970 v[m++].iov_len = 1 ; 971 switch (act->type) 972 { 973 case ACTTYPE_FD1 : 974 if (!bufalloc_putv(bufalloc_1, v, m)) dienomem() ; 975 case ACTTYPE_NOTHING : 976 break ; 977 978 case ACTTYPE_FD2 : 979 buffer_puts(buffer_2, PROG) ; 980 buffer_puts(buffer_2, ": alert: ") ; 981 if (act->data.fd2_size && act->data.fd2_size + 3 < len) 982 { 983 v[m-2].iov_len = act->data.fd2_size ; 984 v[m-1].iov_base = "...\n" ; 985 v[m-1].iov_len = 4 ; 986 } 987 buffer_putv(buffer_2, v, m) ; 988 buffer_flush(buffer_2) ; /* if it blocks, too bad */ 989 break ; 990 991 case ACTTYPE_STATUS : 992 if (act->data.status.filelen) 993 { 994 size_t reallen = siovec_len(v, m) ; 995 if (reallen > act->data.status.filelen) 996 siovec_trunc(v, m, act->data.status.filelen) ; 997 else 998 { 999 size_t k = act->data.status.filelen - reallen + 1 ; 1000 char pad[k] ; 1001 v[m-1].iov_base = pad ; 1002 v[m-1].iov_len = k ; 1003 while (k--) pad[k] = '\n' ; 1004 if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity) 1005 strerr_warnwu2sys("write status file ", act->data.status.file) ; 1006 break ; 1007 } 1008 } 1009 if (!openwritevnclose_suffix(act->data.status.file, v, m, ".new") && verbosity) 1010 strerr_warnwu2sys("write status file ", act->data.status.file) ; 1011 break ; 1012 1013 case ACTTYPE_DIR : 1014 if (!bufalloc_putv(&logdirs[act->data.ld].out, v, m)) dienomem() ; 1015 break ; 1016 1017 default : 1018 strerr_dief2x(101, "internal consistency error in ", "action type") ; 1019 } 1020 } 1021 } 1022 } 1023 if (gflags & 3) tain_now_g() ; 1024 } 1025 1026 1027 /* Input */ 1028 1029 static void prepare_to_exit (void) 1030 { 1031 fd_close(0) ; 1032 flagexiting = 1 ; 1033 } 1034 1035 static inline int getchunk (buffer *b, stralloc *sa, size_t linelimit) 1036 { 1037 struct iovec v[2] ; 1038 size_t pos ; 1039 int r ; 1040 buffer_rpeek(b, v) ; 1041 pos = siovec_bytein(v, 2, "\n", 2) ; 1042 if (linelimit && sa->len + pos > linelimit) 1043 { 1044 r = 2 ; 1045 pos = linelimit - sa->len ; 1046 } 1047 else 1048 { 1049 r = pos < buffer_len(b) ; 1050 pos += r ; 1051 } 1052 if (!stralloc_readyplus(sa, pos + (r == 2))) return -1 ; 1053 buffer_getnofill(b, sa->s + sa->len, pos) ; sa->len += pos ; 1054 if (r == 2) sa->s[sa->len++] = 0 ; 1055 return r ; 1056 } 1057 1058 static void normal_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags) 1059 { 1060 ssize_t r = sanitize_read(buffer_fill(buffer_0)) ; 1061 if (r < 0) 1062 { 1063 if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ; 1064 prepare_to_exit() ; 1065 } 1066 else if (r) for (;;) 1067 { 1068 r = getchunk(buffer_0, &indata, linelimit) ; 1069 if (r < 0) dienomem() ; 1070 else if (!r) break ; 1071 indata.s[indata.len - 1] = 0 ; 1072 script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; 1073 indata.len = 0 ; 1074 } 1075 } 1076 1077 static void process_partial_line (scriptelem_t const *script, unsigned int scriptlen, unsigned int gflags) 1078 { 1079 if (!stralloc_0(&indata)) dienomem() ; 1080 script_run(script, scriptlen, indata.s, indata.len - 1, gflags) ; 1081 indata.len = 0 ; 1082 } 1083 1084 static void last_stdin (scriptelem_t const *script, unsigned int scriptlen, size_t linelimit, unsigned int gflags) 1085 { 1086 for (;;) 1087 { 1088 char c ; 1089 switch (sanitize_read(fd_read(0, &c, 1))) 1090 { 1091 case 0 : return ; 1092 case -1 : 1093 if ((errno != EPIPE) && verbosity) strerr_warnwu1sys("read from stdin") ; 1094 if (indata.len) goto lastline ; 1095 goto end ; 1096 case 1 : 1097 if (c == '\n' || !c) goto lastline ; 1098 if (!stralloc_catb(&indata, &c, 1)) dienomem() ; 1099 if (linelimit && indata.len >= linelimit) 1100 { 1101 if (verbosity) strerr_warnw2x("input line too long, ", "stopping before the end") ; 1102 goto lastline ; 1103 } 1104 else break ; 1105 } 1106 } 1107 lastline: 1108 process_partial_line(script, scriptlen, gflags) ; 1109 end: 1110 prepare_to_exit() ; 1111 } 1112 1113 static inputproc_func_ref handle_stdin = &normal_stdin ; 1114 1115 1116 /* Signals */ 1117 1118 static inline void processor_died (logdir_t *ldp, int wstat) 1119 { 1120 ldp->pid = 0 ; 1121 if (WIFSIGNALED(wstat)) 1122 { 1123 if (verbosity) strerr_warnw2x("processor crashed in ", ldp->dir) ; 1124 tain_add_g(&ldp->deadline, &ldp->retrytto) ; 1125 ldp->rstate = ROTSTATE_RUNPROCESSOR ; 1126 } 1127 else if (WEXITSTATUS(wstat)) 1128 { 1129 if (verbosity) strerr_warnw2x("processor failed in ", ldp->dir) ; 1130 tain_add_g(&ldp->deadline, &ldp->retrytto) ; 1131 ldp->rstate = ROTSTATE_RUNPROCESSOR ; 1132 } 1133 else 1134 { 1135 ldp->rstate = ROTSTATE_SYNCPROCESSED ; 1136 rotator(ldp) ; 1137 } 1138 } 1139 1140 static inline int handle_signals (void) 1141 { 1142 int e = 0 ; 1143 for (;;) 1144 { 1145 switch (selfpipe_read()) 1146 { 1147 case -1 : strerr_diefu1sys(111, "selfpipe_read") ; 1148 case 0 : return e ; 1149 case SIGALRM : 1150 { 1151 unsigned int i = 0 ; 1152 for (; i < llen ; i++) 1153 if ((logdirs[i].rstate == ROTSTATE_WRITABLE) && logdirs[i].b) 1154 { 1155 logdirs[i].rstate = ROTSTATE_START ; 1156 rotator(logdirs + i) ; 1157 } 1158 break ; 1159 } 1160 case SIGTERM : 1161 if (flagprotect) break ; 1162 case SIGHUP : 1163 handle_stdin = &last_stdin ; 1164 tain_add_g(&exit_deadline, &lastlinetto) ; 1165 if (!indata.len) { prepare_to_exit() ; e = 1 ; } 1166 break ; 1167 case SIGCHLD : 1168 { 1169 for (;;) 1170 { 1171 int wstat ; 1172 unsigned int i = 0 ; 1173 pid_t r = wait_nohang(&wstat) ; 1174 if (r <= 0) break ; 1175 for (; i < llen ; i++) if (r == logdirs[i].pid) break ; 1176 if (i < llen) processor_died(logdirs + i, wstat) ; 1177 } 1178 break ; 1179 } 1180 default : strerr_dief1x(101, "internal consistency error with signal handling") ; 1181 } 1182 } 1183 } 1184 1185 1186 /* Main */ 1187 1188 int main (int argc, char const *const *argv) 1189 { 1190 unsigned int sellen, actlen, scriptlen ; 1191 unsigned int linelimit = 8192 ; 1192 unsigned int notif = 0 ; 1193 unsigned int gflags = 0 ; 1194 int flagblock = 0 ; 1195 PROG = "s6-log" ; 1196 { 1197 subgetopt l = SUBGETOPT_ZERO ; 1198 unsigned int t = 2000 ; 1199 for (;;) 1200 { 1201 int opt = subgetopt_r(argc, argv, "qvbpl:d:t:", &l) ; 1202 if (opt == -1) break ; 1203 switch (opt) 1204 { 1205 case 'q' : if (verbosity) verbosity-- ; break ; 1206 case 'v' : verbosity++ ; break ; 1207 case 'b' : flagblock = 1 ; break ; 1208 case 'p' : flagprotect = 1 ; break ; 1209 case 'l' : if (!uint0_scan(l.arg, &linelimit)) dieusage() ; break ; 1210 case 'd' : 1211 if (!uint0_scan(l.arg, ¬if)) dieusage() ; 1212 if (notif < 3) strerr_dief1x(100, "notification fd must be 3 or more") ; 1213 if (fcntl(notif, F_GETFD) < 0) strerr_dief1sys(100, "invalid notification fd") ; 1214 break ; 1215 case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ; 1216 default : dieusage() ; 1217 } 1218 } 1219 argc -= l.ind ; argv += l.ind ; 1220 if (t) tain_from_millisecs(&lastlinetto, t) ; 1221 else lastlinetto = tain_infinite_relative ; 1222 } 1223 if (!argc) dieusage() ; 1224 if (linelimit && linelimit < LINELIMIT_MIN) linelimit = LINELIMIT_MIN ; 1225 if (!fd_sanitize()) strerr_diefu1sys(111, "ensure stdin/stdout/stderr are open") ; 1226 if (!tain_now_set_stopwatch_g() && verbosity) 1227 strerr_warnwu1sys("set monotonic clock and read current time - timestamps may be wrong for a while") ; 1228 if (ndelay_on(0) < 0) strerr_diefu3sys(111, "set std", "in", " non-blocking") ; 1229 if (ndelay_on(1) < 0) strerr_diefu3sys(111, "set std", "out", " non-blocking") ; 1230 mask = umask(0) ; 1231 umask(mask) ; 1232 mask = ~mask & 0666 ; 1233 script_firstpass(argv, &sellen, &actlen, &scriptlen, &gflags) ; 1234 { 1235 sel_t selections[sellen ? sellen : 1] ; 1236 act_t actions[actlen] ; 1237 scriptelem_t script[scriptlen] ; 1238 logdir_t logdirblob[llen] ; 1239 iopause_fd x[3 + llen] ; 1240 logdirs = logdirblob ; 1241 script_secondpass(argv, script, selections, actions) ; 1242 x[0].fd = selfpipe_init() ; 1243 if (x[0].fd < 0) strerr_diefu1sys(111, "selfpipe_init") ; 1244 if (!sig_altignore(SIGPIPE)) strerr_diefu1sys(111, "sig_ignore(SIGPIPE)") ; 1245 { 1246 sigset_t set ; 1247 sigemptyset(&set) ; 1248 sigaddset(&set, SIGTERM) ; 1249 sigaddset(&set, SIGHUP) ; 1250 sigaddset(&set, SIGALRM) ; 1251 sigaddset(&set, SIGCHLD) ; 1252 if (!selfpipe_trapset(&set)) 1253 strerr_diefu1sys(111, "selfpipe_trapset") ; 1254 } 1255 x[0].events = IOPAUSE_READ ; 1256 if (notif) 1257 { 1258 fd_write(notif, "\n", 1) ; 1259 fd_close(notif) ; 1260 } 1261 1262 for (;;) 1263 { 1264 tain deadline = exit_deadline ; 1265 int r = 0 ; 1266 unsigned int xindex0, xindex1 ; 1267 unsigned int i = 0, j = 1 ; 1268 if (bufalloc_1->fd == 1 && bufalloc_len(bufalloc_1)) 1269 { 1270 r = 1 ; 1271 x[j].fd = 1 ; 1272 x[j].events = IOPAUSE_EXCEPT | (bufalloc_len(bufalloc_1) ? IOPAUSE_WRITE : 0) ; 1273 xindex1 = j++ ; 1274 } 1275 else xindex1 = 0 ; 1276 1277 for (; i < llen ; i++) 1278 { 1279 logdirs[i].xindex = 0 ; 1280 if (bufalloc_len(&logdirs[i].out) || (logdirs[i].rstate != ROTSTATE_WRITABLE)) 1281 { 1282 r = 1 ; 1283 if (tain_less(&logdirs[i].deadline, &deadline)) 1284 deadline = logdirs[i].deadline ; 1285 } 1286 } 1287 if (!flagexiting && !(flagblock && r)) 1288 { 1289 x[j].fd = 0 ; 1290 x[j].events = IOPAUSE_READ ; 1291 xindex0 = j++ ; 1292 } 1293 else xindex0 = 0 ; 1294 1295 if (flagexiting && !r) break ; 1296 1297 r = iopause_g(x, j, &deadline) ; 1298 if (r < 0) strerr_diefu1sys(111, "iopause") ; 1299 else if (!r) 1300 { 1301 if (!tain_future(&exit_deadline)) 1302 { 1303 if (indata.len) process_partial_line(script, scriptlen, gflags) ; 1304 prepare_to_exit() ; 1305 } 1306 for (i = 0 ; i < llen ; i++) 1307 if (!tain_future(&logdirs[i].deadline)) 1308 rotate_or_flush(logdirs + i) ; 1309 continue ; 1310 } 1311 1312 if (x[0].revents & (IOPAUSE_READ | IOPAUSE_EXCEPT) && handle_signals()) continue ; 1313 1314 if (xindex1 && x[xindex1].revents) 1315 { 1316 if (!bufalloc_flush(bufalloc_1) && !error_isagain(errno)) 1317 { 1318 unsigned int i = actlen ; 1319 strerr_warnwu1sys("write to stdout, closing the stream - error was") ; 1320 fd_close(1) ; 1321 bufalloc_1->fd = -1 ; 1322 bufalloc_free(bufalloc_1) ; 1323 while (i--) 1324 if (actions[i].type == ACTTYPE_FD1) 1325 actions[i].type = ACTTYPE_NOTHING ; 1326 } 1327 } 1328 1329 for (i = 0 ; i < llen ; i++) 1330 if (logdirs[i].xindex && x[logdirs[i].xindex].revents & IOPAUSE_WRITE) 1331 rotate_or_flush(logdirs + i) ; 1332 1333 if (xindex0 && x[xindex0].revents) 1334 { 1335 if (x[xindex0].revents & IOPAUSE_READ) 1336 (*handle_stdin)(script, scriptlen, linelimit, gflags) ; 1337 else 1338 { 1339 if (indata.len) process_partial_line(script, scriptlen, gflags) ; 1340 prepare_to_exit() ; 1341 } 1342 } 1343 } 1344 finalize() ; 1345 } 1346 return 0 ; 1347 }