1 /* 2 * Copyright (c) 2019-2020 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "dsynth.h" 39 40 struct logerrinfo { 41 struct logerrinfo *next; 42 char *logid; 43 pid_t pid; 44 long seq; 45 int exited; 46 }; 47 48 buildenv_t *BuildEnv; 49 static buildenv_t **BuildEnvTail = &BuildEnv; 50 51 extern char **environ; 52 53 static void *dexec_logerr_thread(void *info); 54 55 #define EINFO_HSIZE 1024 56 #define EINFO_HMASK (EINFO_HSIZE - 1) 57 58 static struct logerrinfo *EInfo[EINFO_HSIZE]; 59 static pthread_t ETid; 60 static int EFds[2]; 61 static pthread_cond_t ECond; 62 63 static __inline 64 struct logerrinfo ** 65 einfohash(pid_t pid) 66 { 67 return(&EInfo[pid & EINFO_HMASK]); 68 } 69 70 __dead2 void 71 _dfatal(const char *file __unused, int line __unused, const char *func, 72 int do_errno, const char *ctl, ...) 73 { 74 va_list va; 75 76 fprintf(stderr, "%s: ", func); 77 va_start(va, ctl); 78 vfprintf(stderr, ctl, va); 79 va_end(va); 80 if (do_errno & 1) 81 fprintf(stderr, ": %s", strerror(errno)); 82 fprintf(stderr, "\n"); 83 fflush(stderr); 84 85 if (do_errno & 2) 86 kill(getpid(), SIGQUIT); 87 exit(1); 88 } 89 90 void 91 _ddprintf(int tab, const char *ctl, ...) 92 { 93 va_list va; 94 95 if (tab) 96 printf("%*.*s", tab, tab, ""); 97 va_start(va, ctl); 98 vfprintf(stdout, ctl, va); 99 va_end(va); 100 } 101 102 char * 103 strdup_or_null(char *str) 104 { 105 if (str && str[0]) 106 return(strdup(str)); 107 return NULL; 108 } 109 110 static const char *DLogNames[] = { 111 "00_last_results.log", 112 "01_success_list.log", 113 "02_failure_list.log", 114 "03_ignored_list.log", 115 "04_skipped_list.log", 116 "05_abnormal_command_output.log", 117 "06_obsolete_packages.log", 118 "07_debug.log", 119 }; 120 121 static int DLogFd[DLOG_COUNT]; 122 static pthread_mutex_t DLogFdMutex; 123 124 #define arysize(ary) (sizeof((ary)) / sizeof((ary)[0])) 125 126 static int 127 dlogfd(int which, int modes) 128 { 129 char *path; 130 int fd; 131 132 which &= DLOG_MASK; 133 if ((fd = DLogFd[which]) > 0) 134 return fd; 135 pthread_mutex_lock(&DLogFdMutex); 136 if ((fd = DLogFd[which]) <= 0) { 137 asprintf(&path, "%s/%s", LogsPath, DLogNames[which]); 138 fd = open(path, modes, 0666); 139 DLogFd[which] = fd; 140 free(path); 141 } 142 pthread_mutex_unlock(&DLogFdMutex); 143 144 return fd; 145 } 146 147 148 void 149 dlogreset(void) 150 { 151 int i; 152 153 ddassert(DLOG_COUNT == arysize(DLogNames)); 154 for (i = 0; i < DLOG_COUNT; ++i) { 155 if (DLogFd[i] > 0) { 156 close(DLogFd[i]); 157 DLogFd[i] = -1; 158 } 159 (void)dlogfd(i, O_RDWR|O_CREAT|O_TRUNC|O_APPEND); 160 } 161 } 162 163 void 164 _dlog(int which, const char *ctl, ...) 165 { 166 va_list va; 167 char *buf; 168 char *ptr; 169 size_t len; 170 int fd; 171 int filter; 172 173 filter = which; 174 which &= DLOG_MASK; 175 176 ddassert((uint)which < DLOG_COUNT); 177 va_start(va, ctl); 178 vasprintf(&buf, ctl, va); 179 va_end(va); 180 len = strlen(buf); 181 182 /* 183 * The special sequence ## right-justfies the text after the ##. 184 * 185 * NOTE: Alignment test includes \n so use 80 instead of 79 to 186 * leave one char unused on a 80-column terminal. 187 */ 188 if ((ptr = strstr(buf, "##")) != NULL) { 189 size_t l2; 190 size_t l1; 191 char *b2; 192 int spc; 193 194 l1 = (int)(ptr - buf); 195 l2 = len - l1 - 2; 196 if (l1 <= 80 - l2) { 197 spc = 80 - l1 - l2; 198 buf[l1] = 0; 199 asprintf(&b2, "%s%*.*s%s", 200 buf, spc, spc, "", ptr + 2); 201 } else { 202 buf[l1] = 0; 203 asprintf(&b2, "%s%s", buf, ptr + 2); 204 } 205 len = strlen(b2); 206 free(buf); 207 buf = b2; 208 } 209 210 /* 211 * All logs also go to log 00. 212 */ 213 if (which != DLOG_ALL) { 214 fd = dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND); 215 if (fd > 0) 216 write(fd, buf, len); 217 } 218 219 /* 220 * Nominal log target 221 */ 222 fd = dlogfd(which, O_RDWR|O_CREAT|O_APPEND); 223 write(fd, buf, len); 224 225 /* 226 * If ncurses is not being used, all log output also goes 227 * to stdout, unless filtered. 228 */ 229 if ((UseNCurses == 0 || (filter & DLOG_STDOUT)) && 230 (filter & DLOG_FILTER) == 0) { 231 if (ColorOpt) { 232 if (filter & DLOG_GRN) 233 write(1, "\x1b[0;32m", 7); 234 if (filter & DLOG_RED) 235 write(1, "\x1b[0;31m", 7); 236 } 237 write(1, buf, len); 238 if (ColorOpt && (filter & (DLOG_GRN|DLOG_RED))) { 239 write(1, "\x1b[0;39m", 7); 240 } 241 } 242 free(buf); 243 } 244 245 int 246 dlog00_fd(void) 247 { 248 return(dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND)); 249 } 250 251 /* 252 * Bulk and Build environment control. These routines are only called 253 * unthreaded or when dsynth threads are idle. 254 */ 255 void 256 addbuildenv(const char *label, const char *data, int type) 257 { 258 buildenv_t *env; 259 260 env = calloc(1, sizeof(*env)); 261 env->a1 = strdup(label); 262 env->a2 = strdup(data); 263 env->label = env->a1; 264 env->data = env->a2; 265 env->type = type; 266 *BuildEnvTail = env; 267 BuildEnvTail = &env->next; 268 } 269 270 void 271 delbuildenv(const char *label) 272 { 273 buildenv_t **envp; 274 buildenv_t *env; 275 276 envp = &BuildEnv; 277 while ((env = *envp) != NULL) { 278 if (strcmp(env->label, label) == 0) { 279 *envp = env->next; 280 if (env->a1) 281 free(env->a1); 282 if (env->a2) 283 free(env->a2); 284 free(env); 285 } else { 286 envp = &env->next; 287 } 288 } 289 BuildEnvTail = envp; 290 } 291 292 void 293 freestrp(char **strp) 294 { 295 if (*strp) { 296 free(*strp); 297 *strp = NULL; 298 } 299 } 300 301 void 302 dupstrp(char **strp) 303 { 304 if (*strp) 305 *strp = strdup(*strp); 306 } 307 308 int 309 ipcreadmsg(int fd, wmsg_t *msg) 310 { 311 size_t res; 312 ssize_t r; 313 char *ptr; 314 315 res = sizeof(*msg); 316 ptr = (char *)(void *)msg; 317 while (res) { 318 r = read(fd, ptr, res); 319 if (r <= 0) { 320 if (errno == EINTR) 321 continue; 322 return -1; 323 } 324 res -= (size_t)r; 325 ptr += r; 326 } 327 return 0; 328 } 329 330 int 331 ipcwritemsg(int fd, wmsg_t *msg) 332 { 333 size_t res; 334 ssize_t r; 335 char *ptr; 336 337 res = sizeof(*msg); 338 ptr = (char *)(void *)msg; 339 while (res) { 340 r = write(fd, ptr, res); 341 if (r < 0) { 342 if (errno == EINTR) 343 continue; 344 return -1; 345 } 346 res -= (size_t)r; 347 ptr += r; 348 } 349 return 0; 350 } 351 352 int 353 askyn(const char *ctl, ...) 354 { 355 va_list va; 356 char buf[256]; 357 int res = 0; 358 359 if (YesOpt) 360 return 1; 361 362 for (;;) { 363 va_start(va, ctl); 364 vprintf(ctl, va); 365 va_end(va); 366 fflush(stdout); 367 if (fgets(buf, sizeof(buf), stdin) == NULL) 368 break; 369 if (buf[0] == 'y' || buf[0] == 'Y') { 370 res = 1; 371 break; 372 } 373 if (buf[0] == 'n' || buf[0] == 'N') { 374 res = 0; 375 break; 376 } 377 printf("Please type y/n\n"); 378 } 379 return res; 380 } 381 382 /* 383 * Get swap% used 0.0-1.0. 384 * 385 * NOTE: This routine is intended to return quickly. 386 * 387 * NOTE: swap_cache (caching for hard drives) is persistent and should 388 * not be counted for our purposes. 389 */ 390 double 391 getswappct(int *noswapp) 392 { 393 long swap_size = 0; 394 long swap_anon = 0; 395 long swap_cache __unused = 0; 396 size_t len; 397 double dswap; 398 399 len = sizeof(swap_size); 400 sysctlbyname("vm.swap_size", &swap_size, &len, NULL, 0); 401 len = sizeof(swap_size); 402 sysctlbyname("vm.swap_anon_use", &swap_anon, &len, NULL, 0); 403 len = sizeof(swap_size); 404 sysctlbyname("vm.swap_cache_use", &swap_cache, &len, NULL, 0); 405 if (swap_size) { 406 dswap = (double)(swap_anon /*+swap_cache*/) / (double)swap_size; 407 *noswapp = 0; 408 } else { 409 dswap = 0.0; 410 *noswapp = 1; 411 } 412 return dswap; 413 } 414 415 /* 416 * dexec_open()/fgets/dexec_close() 417 * 418 * Similar to popen() but directly exec()s the argument list (cav[0] must 419 * be an absolute path). 420 * 421 * If xenv is non-NULL its an array of local buildenv_t's to be used. 422 * The array is terminated with a NULL xenv->label. 423 * 424 * If with_env is non-zero the configured environment is included. 425 * 426 * If with_mvars is non-zero the make environment is passed as VAR=DATA 427 * elements on the command line. 428 */ 429 FILE * 430 dexec_open(const char *logid, const char **cav, int cac, 431 pid_t *pidp, buildenv_t *xenv, int with_env, int with_mvars) 432 { 433 buildenv_t *benv; 434 const char **cenv; 435 char *allocary[MAXCAC*2]; 436 int env_basei; 437 int envi; 438 int alloci; 439 int nullfd; 440 int fds[2]; /* stdout */ 441 pid_t pid; 442 FILE *fp; 443 struct logerrinfo *einfo; 444 static int warned; 445 446 /* 447 * Error logging thread setup 448 */ 449 if (ETid == 0) { 450 pthread_mutex_lock(&DLogFdMutex); 451 if (ETid == 0) { 452 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, EFds) < 0) 453 dfatal_errno("socketpair"); 454 #ifdef SO_PASSCRED 455 int optval = 1; 456 if (setsockopt(EFds[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) < 0) { 457 if (warned == 0) { 458 warned = 1; 459 fprintf(stderr, "SO_PASSCRED not supported\n"); 460 } 461 } 462 #endif 463 464 pthread_cond_init(&ECond, NULL); 465 pthread_create(&ETid, NULL, dexec_logerr_thread, NULL); 466 } 467 pthread_mutex_unlock(&DLogFdMutex); 468 } 469 470 env_basei = 0; 471 while (environ[env_basei]) 472 ++env_basei; 473 cenv = calloc(env_basei + MAXCAC, sizeof(char *)); 474 env_basei = 0; 475 for (envi = 0; envi < env_basei; ++envi) 476 cenv[envi] = environ[envi]; 477 478 alloci = 0; 479 for (benv = BuildEnv; benv; benv = benv->next) { 480 if (with_mvars && 481 (benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 482 asprintf(&allocary[alloci], "%s=%s", 483 benv->label, benv->data); 484 cav[cac++] = allocary[alloci]; 485 ++alloci; 486 } 487 if (with_env && 488 (benv->type & BENV_PKGLIST) && 489 (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) { 490 asprintf(&allocary[alloci], "%s=%s", 491 benv->label, benv->data); 492 cenv[envi++] = allocary[alloci]; 493 ++alloci; 494 } 495 ddassert(cac < MAXCAC && envi - env_basei < MAXCAC); 496 } 497 498 /* 499 * Extra environment specific to this particular dexec 500 */ 501 while (xenv && xenv->label) { 502 asprintf(&allocary[alloci], "%s=%s", 503 xenv->label, xenv->data); 504 cenv[envi++] = allocary[alloci]; 505 ++alloci; 506 ++xenv; 507 } 508 509 cav[cac] = NULL; 510 cenv[envi] = NULL; 511 512 if (pipe2(fds, O_CLOEXEC) < 0) 513 dfatal_errno("pipe"); 514 nullfd = open("/dev/null", O_RDWR | O_CLOEXEC); 515 if (nullfd < 0) 516 dfatal_errno("open(\"/dev/null\")"); 517 518 /* 519 * We have to be very careful using vfork(), do only the bare 520 * minimum necessary in the child to set it up and exec it. 521 */ 522 pid = vfork(); 523 if (pid == 0) { 524 #if 0 525 int i; 526 printf("%s", cav[0]); 527 for (i = 0; cav[i]; ++i) 528 printf(" %s", cav[i]); 529 printf("\n"); 530 printf("ENV: "); 531 for (i = 0; cenv[i]; ++i) 532 printf(" %s", cenv[i]); 533 #endif 534 535 if (fds[1] != 1) { 536 dup2(fds[1], 1); 537 close(fds[1]); 538 } 539 if (EFds[1] != 2) { 540 dup2(EFds[1], 2); 541 close(EFds[1]); 542 } 543 close(fds[0]); /* safety */ 544 close(EFds[0]); /* safety */ 545 dup2(nullfd, 0); /* no questions! */ 546 closefrom(3); /* be nice */ 547 548 /* 549 * Self-nice to be nice (ignore any error) 550 */ 551 if (NiceOpt) 552 setpriority(PRIO_PROCESS, 0, NiceOpt); 553 554 execve(cav[0], (void *)cav, (void *)cenv); 555 write(2, "EXEC FAILURE\n", 13); 556 _exit(1); 557 } 558 close(nullfd); 559 close(fds[1]); 560 if (pid < 0) { 561 close(fds[0]); 562 dfatal_errno("vfork failed"); 563 } 564 fp = fdopen(fds[0], "r"); 565 *pidp = pid; 566 567 /* 568 * einfo setup 569 */ 570 einfo = calloc(1, sizeof(*einfo)); 571 if (logid) 572 einfo->logid = strdup(logid); 573 574 pthread_mutex_lock(&DLogFdMutex); 575 einfo->pid = pid; 576 einfo->next = *einfohash(pid); 577 *einfohash(pid) = einfo; 578 pthread_cond_signal(&ECond); 579 pthread_mutex_unlock(&DLogFdMutex); 580 581 while (--alloci >= 0) 582 free(allocary[alloci]); 583 free(cenv); 584 585 return fp; 586 } 587 588 int 589 dexec_close(FILE *fp, pid_t pid) 590 { 591 struct logerrinfo **einfop; 592 struct logerrinfo *einfo; 593 pid_t rpid; 594 int status; 595 596 fclose(fp); 597 while ((rpid = waitpid(pid, &status, 0)) != pid) { 598 if (rpid < 0) { 599 if (errno == EINTR) 600 continue; 601 return 1; 602 } 603 } 604 605 pthread_mutex_lock(&DLogFdMutex); 606 einfop = einfohash(pid); 607 while ((einfo = *einfop) != NULL) { 608 if (einfo->pid == pid) { 609 einfo->exited = 1; 610 break; 611 } 612 einfop = &einfo->next; 613 } 614 pthread_mutex_unlock(&DLogFdMutex); 615 616 return (WEXITSTATUS(status)); 617 } 618 619 static void * 620 dexec_logerr_thread(void *dummy __unused) 621 { 622 char buf[4096]; 623 union { 624 char cbuf[CMSG_SPACE(sizeof(struct ucred))]; 625 struct cmsghdr cbuf_align; 626 } cmsg_buf; 627 struct cmsghdr *cmsg; 628 struct logerrinfo **einfop; 629 struct logerrinfo *einfo; 630 struct msghdr msg; 631 struct iovec iov; 632 ssize_t len; 633 int mflags = MSG_DONTWAIT; 634 int fd; 635 636 pthread_detach(pthread_self()); 637 638 msg.msg_name = NULL; 639 msg.msg_namelen = 0; 640 msg.msg_iov = &iov; 641 642 msg.msg_control = cmsg_buf.cbuf; 643 msg.msg_controllen = sizeof(cmsg_buf.cbuf); 644 645 fd = EFds[0]; 646 for (;;) { 647 int i; 648 649 msg.msg_iovlen = 1; 650 iov.iov_base = buf; 651 iov.iov_len = sizeof(buf) - 1; 652 653 /* 654 * Wait for message, if none are pending then clean-up 655 * exited einfos (this ensures that we have flushed all 656 * error output before freeing the structure). 657 * 658 * Don't obtain the mutex until we really need it. The 659 * parent thread can only 'add' einfo entries to the hash 660 * table so we are basically scan-safe. 661 */ 662 len = recvmsg(fd, &msg, mflags); 663 if (len < 0) { 664 if (errno != EAGAIN) { /* something messed up */ 665 fprintf(stderr, "ERRNO %d\n", errno); 666 break; 667 } 668 669 for (i = 0; i < EINFO_HSIZE; ++i) { 670 einfo = EInfo[i]; 671 while (einfo) { 672 if (einfo->exited) 673 break; 674 einfo = einfo->next; 675 } 676 if (einfo == NULL) 677 continue; 678 pthread_mutex_lock(&DLogFdMutex); 679 einfop = &EInfo[i]; 680 while ((einfo = *einfop) != NULL) { 681 if (einfo->exited) { 682 *einfop = einfo->next; 683 if (einfo->logid) 684 free(einfo->logid); 685 free(einfo); 686 } else { 687 einfop = &einfo->next; 688 } 689 } 690 pthread_mutex_unlock(&DLogFdMutex); 691 } 692 mflags = 0; 693 continue; 694 } 695 696 /* 697 * Process SCM_CREDS, if present. Throw away SCM_RIGHTS 698 * if some sub-process stupidly sent it. 699 */ 700 einfo = NULL; 701 mflags = MSG_DONTWAIT; 702 703 if (len && buf[len-1] == '\n') 704 --len; 705 buf[len] = 0; 706 707 for (cmsg = CMSG_FIRSTHDR(&msg); 708 cmsg; 709 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 710 struct cmsgcred *cred; 711 int *fds; 712 int n; 713 714 if (cmsg->cmsg_level != SOL_SOCKET) 715 continue; 716 717 switch(cmsg->cmsg_type) { 718 case SCM_CREDS: 719 720 cred = (void *)CMSG_DATA(cmsg); 721 722 einfo = *einfohash(cred->cmcred_pid); 723 while (einfo && einfo->pid != cred->cmcred_pid) 724 einfo = einfo->next; 725 break; 726 case SCM_RIGHTS: 727 fds = (void *)CMSG_DATA(cmsg); 728 n = (cmsg->cmsg_len - sizeof(cmsg)) / 729 sizeof(int); 730 for (i = 0; i < n; ++i) 731 close(fds[i]); 732 break; 733 } 734 } 735 736 if (einfo && einfo->logid) { 737 dlog(DLOG_ALL | DLOG_STDOUT, 738 "%s: %s\n", 739 einfo->logid, buf); 740 } else { 741 dlog(DLOG_ALL | DLOG_STDOUT, "%s", buf); 742 } 743 } 744 return NULL; 745 } 746 747 const char * 748 getphasestr(worker_phase_t phaseid) 749 { 750 const char *phase; 751 752 switch(phaseid) { 753 case PHASE_PENDING: 754 phase = "pending"; 755 break; 756 case PHASE_INSTALL_PKGS: 757 phase = "install-pkgs"; 758 break; 759 case PHASE_CHECK_SANITY: 760 phase = "check-sanity"; 761 break; 762 case PHASE_PKG_DEPENDS: 763 phase = "pkg-depends"; 764 break; 765 case PHASE_FETCH_DEPENDS: 766 phase = "fetch-depends"; 767 break; 768 case PHASE_FETCH: 769 phase = "fetch"; 770 break; 771 case PHASE_CHECKSUM: 772 phase = "checksum"; 773 break; 774 case PHASE_EXTRACT_DEPENDS: 775 phase = "extract-depends"; 776 break; 777 case PHASE_EXTRACT: 778 phase = "extract"; 779 break; 780 case PHASE_PATCH_DEPENDS: 781 phase = "patch-depends"; 782 break; 783 case PHASE_PATCH: 784 phase = "patch"; 785 break; 786 case PHASE_BUILD_DEPENDS: 787 phase = "build-depends"; 788 break; 789 case PHASE_LIB_DEPENDS: 790 phase = "lib-depends"; 791 break; 792 case PHASE_CONFIGURE: 793 phase = "configure"; 794 break; 795 case PHASE_BUILD: 796 phase = "build"; 797 break; 798 case PHASE_RUN_DEPENDS: 799 phase = "run-depends"; 800 break; 801 case PHASE_STAGE: 802 phase = "stage"; 803 break; 804 case PHASE_TEST: 805 phase = "test"; 806 break; 807 case PHASE_CHECK_PLIST: 808 phase = "check-plist"; 809 break; 810 case PHASE_PACKAGE: 811 phase = "package"; 812 break; 813 case PHASE_INSTALL: 814 phase = "install"; 815 break; 816 case PHASE_DEINSTALL: 817 phase = "deinstall"; 818 break; 819 case PHASE_DUMP_ENV: 820 phase = "dump-env"; 821 break; 822 case PHASE_DUMP_VAR: 823 phase = "dump-var"; 824 break; 825 case PHASE_SHOW_CONFIG: 826 phase = "show-config"; 827 break; 828 case PHASE_DUMP_MAKECONF: 829 phase = "make-conf"; 830 break; 831 default: 832 phase = "Run-Unknown"; 833 break; 834 } 835 return phase; 836 } 837 838 int 839 readlogline(monitorlog_t *log, char **bufp) 840 { 841 int r; 842 int n; 843 844 /* 845 * Reset buffer as an optimization to avoid unnecessary 846 * shifts. 847 */ 848 *bufp = NULL; 849 if (log->buf_beg == log->buf_end) { 850 log->buf_beg = 0; 851 log->buf_end = 0; 852 log->buf_scan = 0; 853 } 854 855 /* 856 * Look for newline, handle discard mode 857 */ 858 again: 859 for (n = log->buf_scan; n < log->buf_end; ++n) { 860 if (log->buf[n] == '\n') { 861 *bufp = log->buf + log->buf_beg; 862 r = n - log->buf_beg; 863 log->buf_beg = n + 1; 864 log->buf_scan = n + 1; 865 866 if (log->buf_discard_mode == 0) 867 return r; 868 log->buf_discard_mode = 0; 869 goto again; 870 } 871 } 872 873 /* 874 * Handle overflow 875 */ 876 if (n == sizeof(log->buf)) { 877 if (log->buf_beg) { 878 /* 879 * Shift the buffer to make room and read more data. 880 */ 881 bcopy(log->buf + log->buf_beg, 882 log->buf, 883 n - log->buf_beg); 884 log->buf_end -= log->buf_beg; 885 log->buf_scan -= log->buf_beg; 886 n -= log->buf_beg; 887 log->buf_beg = 0; 888 } else if (log->buf_discard_mode) { 889 /* 890 * Overflow. If in discard mode just throw it all 891 * away. Stay in discard mode. 892 */ 893 log->buf_beg = 0; 894 log->buf_end = 0; 895 log->buf_scan = 0; 896 } else { 897 /* 898 * Overflow. If not in discard mode return a truncated 899 * line and enter discard mode. 900 * 901 * The caller will temporarily set ptr[r] = 0 so make 902 * sure that does not overflow our buffer as we are not 903 * at a newline. 904 * 905 * (log->buf_beg is 0); 906 */ 907 *bufp = log->buf + log->buf_beg; 908 r = n - 1; 909 log->buf_beg = n; 910 log->buf_scan = n; 911 log->buf_discard_mode = 1; 912 913 return r; 914 } 915 } 916 917 /* 918 * Read more data. If there is no data pending then return -1, 919 * otherwise loop up to see if more complete line(s) are available. 920 */ 921 r = pread(log->fd, 922 log->buf + log->buf_end, 923 sizeof(log->buf) - log->buf_end, 924 log->offset); 925 if (r <= 0) 926 return -1; 927 log->offset += r; 928 log->buf_end += r; 929 goto again; 930 } 931 932 uint32_t 933 crcDirTree(const char *path) 934 { 935 FTS *fts; 936 FTSENT *fen; 937 struct stat *st; 938 char *pav[2]; 939 uint32_t crc; 940 uint32_t val; 941 942 crc = 0; 943 pav[0] = strdup(path); 944 pav[1] = NULL; 945 946 fts = fts_open(pav, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 947 if (fts == NULL) 948 goto failed; 949 while ((fen = fts_read(fts)) != NULL) { 950 if (fen->fts_info != FTS_F && fen->fts_info != FTS_SL) 951 continue; 952 /* 953 * Ignore hidden dot files or ones ending with .core from the 954 * calculated CRC sum to prevent unnecessary rebuilds. 955 */ 956 if (fen->fts_name[0] == '.') 957 continue; 958 if (fen->fts_namelen >= 5 && 959 !strcmp(fen->fts_name + fen->fts_namelen - 5, ".core")) { 960 continue; 961 } 962 963 st = fen->fts_statp; 964 965 val = iscsi_crc32(&st->st_mtime, sizeof(st->st_mtime)); 966 val = iscsi_crc32_ext(&st->st_size, sizeof(st->st_size), val); 967 val = iscsi_crc32_ext(fen->fts_path, fen->fts_pathlen, val); 968 crc ^= val; 969 } 970 fts_close(fts); 971 failed: 972 free(pav[0]); 973 974 return crc; 975 } 976