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 const char * 293 getbuildenv(const char *label) 294 { 295 buildenv_t **envp; 296 buildenv_t *env; 297 298 envp = &BuildEnv; 299 while ((env = *envp) != NULL) { 300 if (strcmp(env->label, label) == 0) 301 return env->data; 302 envp = &env->next; 303 } 304 return NULL; 305 } 306 307 void 308 freestrp(char **strp) 309 { 310 if (*strp) { 311 free(*strp); 312 *strp = NULL; 313 } 314 } 315 316 void 317 dupstrp(char **strp) 318 { 319 if (*strp) 320 *strp = strdup(*strp); 321 } 322 323 int 324 ipcreadmsg(int fd, wmsg_t *msg) 325 { 326 size_t res; 327 ssize_t r; 328 char *ptr; 329 330 res = sizeof(*msg); 331 ptr = (char *)(void *)msg; 332 while (res) { 333 r = read(fd, ptr, res); 334 if (r <= 0) { 335 if (errno == EINTR) 336 continue; 337 return -1; 338 } 339 res -= (size_t)r; 340 ptr += r; 341 } 342 return 0; 343 } 344 345 int 346 ipcwritemsg(int fd, wmsg_t *msg) 347 { 348 size_t res; 349 ssize_t r; 350 char *ptr; 351 352 res = sizeof(*msg); 353 ptr = (char *)(void *)msg; 354 while (res) { 355 r = write(fd, ptr, res); 356 if (r < 0) { 357 if (errno == EINTR) 358 continue; 359 return -1; 360 } 361 res -= (size_t)r; 362 ptr += r; 363 } 364 return 0; 365 } 366 367 int 368 askyn(const char *ctl, ...) 369 { 370 va_list va; 371 char buf[256]; 372 int res = 0; 373 374 if (YesOpt) 375 return 1; 376 377 for (;;) { 378 va_start(va, ctl); 379 vprintf(ctl, va); 380 va_end(va); 381 fflush(stdout); 382 if (fgets(buf, sizeof(buf), stdin) == NULL) 383 break; 384 if (buf[0] == 'y' || buf[0] == 'Y') { 385 res = 1; 386 break; 387 } 388 if (buf[0] == 'n' || buf[0] == 'N') { 389 res = 0; 390 break; 391 } 392 printf("Please type y/n\n"); 393 } 394 return res; 395 } 396 397 /* 398 * Get swap% used 0.0-1.0. 399 * 400 * NOTE: This routine is intended to return quickly. 401 * 402 * NOTE: swap_cache (caching for hard drives) is persistent and should 403 * not be counted for our purposes. 404 */ 405 double 406 getswappct(int *noswapp) 407 { 408 long swap_size = 0; 409 long swap_anon = 0; 410 long swap_cache __unused = 0; 411 size_t len; 412 double dswap; 413 414 len = sizeof(swap_size); 415 sysctlbyname("vm.swap_size", &swap_size, &len, NULL, 0); 416 len = sizeof(swap_size); 417 sysctlbyname("vm.swap_anon_use", &swap_anon, &len, NULL, 0); 418 len = sizeof(swap_size); 419 sysctlbyname("vm.swap_cache_use", &swap_cache, &len, NULL, 0); 420 if (swap_size) { 421 dswap = (double)(swap_anon /*+swap_cache*/) / (double)swap_size; 422 *noswapp = 0; 423 } else { 424 dswap = 0.0; 425 *noswapp = 1; 426 } 427 return dswap; 428 } 429 430 /* 431 * dexec_open()/fgets/dexec_close() 432 * 433 * Similar to popen() but directly exec()s the argument list (cav[0] must 434 * be an absolute path). 435 * 436 * If xenv is non-NULL its an array of local buildenv_t's to be used. 437 * The array is terminated with a NULL xenv->label. 438 * 439 * If with_env is non-zero the configured environment is included. 440 * 441 * If with_mvars is non-zero the make environment is passed as VAR=DATA 442 * elements on the command line. 443 */ 444 FILE * 445 dexec_open(const char *logid, const char **cav, int cac, 446 pid_t *pidp, buildenv_t *xenv, int with_env, int with_mvars) 447 { 448 buildenv_t *benv; 449 const char **cenv; 450 char *allocary[MAXCAC*2]; 451 int env_basei; 452 int envi; 453 int alloci; 454 int nullfd; 455 int fds[2]; /* stdout */ 456 pid_t pid; 457 FILE *fp; 458 struct logerrinfo *einfo; 459 static int warned; 460 461 /* 462 * Error logging thread setup 463 */ 464 if (ETid == 0) { 465 pthread_mutex_lock(&DLogFdMutex); 466 if (ETid == 0) { 467 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, EFds) < 0) 468 dfatal_errno("socketpair"); 469 #ifdef SO_PASSCRED 470 int optval = 1; 471 if (setsockopt(EFds[0], SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) < 0) { 472 if (warned == 0) { 473 warned = 1; 474 fprintf(stderr, "SO_PASSCRED not supported\n"); 475 } 476 } 477 #endif 478 479 pthread_cond_init(&ECond, NULL); 480 pthread_create(&ETid, NULL, dexec_logerr_thread, NULL); 481 } 482 pthread_mutex_unlock(&DLogFdMutex); 483 } 484 485 env_basei = 0; 486 while (environ[env_basei]) 487 ++env_basei; 488 cenv = calloc(env_basei + MAXCAC, sizeof(char *)); 489 env_basei = 0; 490 for (envi = 0; envi < env_basei; ++envi) 491 cenv[envi] = environ[envi]; 492 493 alloci = 0; 494 for (benv = BuildEnv; benv; benv = benv->next) { 495 if (with_mvars && 496 (benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 497 asprintf(&allocary[alloci], "%s=%s", 498 benv->label, benv->data); 499 cav[cac++] = allocary[alloci]; 500 ++alloci; 501 } 502 if (with_env && 503 (benv->type & BENV_PKGLIST) && 504 (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) { 505 asprintf(&allocary[alloci], "%s=%s", 506 benv->label, benv->data); 507 cenv[envi++] = allocary[alloci]; 508 ++alloci; 509 } 510 ddassert(cac < MAXCAC && envi - env_basei < MAXCAC); 511 } 512 513 /* 514 * Extra environment specific to this particular dexec 515 */ 516 while (xenv && xenv->label) { 517 asprintf(&allocary[alloci], "%s=%s", 518 xenv->label, xenv->data); 519 cenv[envi++] = allocary[alloci]; 520 ++alloci; 521 ++xenv; 522 } 523 524 cav[cac] = NULL; 525 cenv[envi] = NULL; 526 527 if (pipe2(fds, O_CLOEXEC) < 0) 528 dfatal_errno("pipe"); 529 nullfd = open("/dev/null", O_RDWR | O_CLOEXEC); 530 if (nullfd < 0) 531 dfatal_errno("open(\"/dev/null\")"); 532 533 /* 534 * We have to be very careful using vfork(), do only the bare 535 * minimum necessary in the child to set it up and exec it. 536 */ 537 pid = vfork(); 538 if (pid == 0) { 539 #if 0 540 int i; 541 printf("%s", cav[0]); 542 for (i = 0; cav[i]; ++i) 543 printf(" %s", cav[i]); 544 printf("\n"); 545 printf("ENV: "); 546 for (i = 0; cenv[i]; ++i) 547 printf(" %s", cenv[i]); 548 #endif 549 550 if (fds[1] != 1) { 551 dup2(fds[1], 1); 552 close(fds[1]); 553 } 554 if (EFds[1] != 2) { 555 dup2(EFds[1], 2); 556 close(EFds[1]); 557 } 558 close(fds[0]); /* safety */ 559 close(EFds[0]); /* safety */ 560 dup2(nullfd, 0); /* no questions! */ 561 closefrom(3); /* be nice */ 562 563 /* 564 * Self-nice to be nice (ignore any error) 565 */ 566 if (NiceOpt) 567 setpriority(PRIO_PROCESS, 0, NiceOpt); 568 569 execve(cav[0], (void *)cav, (void *)cenv); 570 write(2, "EXEC FAILURE\n", 13); 571 _exit(1); 572 } 573 close(nullfd); 574 close(fds[1]); 575 if (pid < 0) { 576 close(fds[0]); 577 dfatal_errno("vfork failed"); 578 } 579 fp = fdopen(fds[0], "r"); 580 *pidp = pid; 581 582 /* 583 * einfo setup 584 */ 585 einfo = calloc(1, sizeof(*einfo)); 586 if (logid) 587 einfo->logid = strdup(logid); 588 589 pthread_mutex_lock(&DLogFdMutex); 590 einfo->pid = pid; 591 einfo->next = *einfohash(pid); 592 *einfohash(pid) = einfo; 593 pthread_cond_signal(&ECond); 594 pthread_mutex_unlock(&DLogFdMutex); 595 596 while (--alloci >= 0) 597 free(allocary[alloci]); 598 free(cenv); 599 600 return fp; 601 } 602 603 int 604 dexec_close(FILE *fp, pid_t pid) 605 { 606 struct logerrinfo **einfop; 607 struct logerrinfo *einfo; 608 pid_t rpid; 609 int status; 610 611 fclose(fp); 612 while ((rpid = waitpid(pid, &status, 0)) != pid) { 613 if (rpid < 0) { 614 if (errno == EINTR) 615 continue; 616 return 1; 617 } 618 } 619 620 pthread_mutex_lock(&DLogFdMutex); 621 einfop = einfohash(pid); 622 while ((einfo = *einfop) != NULL) { 623 if (einfo->pid == pid) { 624 einfo->exited = 1; 625 break; 626 } 627 einfop = &einfo->next; 628 } 629 pthread_mutex_unlock(&DLogFdMutex); 630 631 return (WEXITSTATUS(status)); 632 } 633 634 static void * 635 dexec_logerr_thread(void *dummy __unused) 636 { 637 char buf[4096]; 638 union { 639 char cbuf[CMSG_SPACE(sizeof(struct ucred))]; 640 struct cmsghdr cbuf_align; 641 } cmsg_buf; 642 struct cmsghdr *cmsg; 643 struct logerrinfo **einfop; 644 struct logerrinfo *einfo; 645 struct msghdr msg; 646 struct iovec iov; 647 ssize_t len; 648 int mflags = MSG_DONTWAIT; 649 int fd; 650 651 pthread_detach(pthread_self()); 652 653 msg.msg_name = NULL; 654 msg.msg_namelen = 0; 655 msg.msg_iov = &iov; 656 657 msg.msg_control = cmsg_buf.cbuf; 658 msg.msg_controllen = sizeof(cmsg_buf.cbuf); 659 660 fd = EFds[0]; 661 for (;;) { 662 int i; 663 664 msg.msg_iovlen = 1; 665 iov.iov_base = buf; 666 iov.iov_len = sizeof(buf) - 1; 667 668 /* 669 * Wait for message, if none are pending then clean-up 670 * exited einfos (this ensures that we have flushed all 671 * error output before freeing the structure). 672 * 673 * Don't obtain the mutex until we really need it. The 674 * parent thread can only 'add' einfo entries to the hash 675 * table so we are basically scan-safe. 676 */ 677 len = recvmsg(fd, &msg, mflags); 678 if (len < 0) { 679 if (errno != EAGAIN) { /* something messed up */ 680 fprintf(stderr, "ERRNO %d\n", errno); 681 break; 682 } 683 684 for (i = 0; i < EINFO_HSIZE; ++i) { 685 einfo = EInfo[i]; 686 while (einfo) { 687 if (einfo->exited) 688 break; 689 einfo = einfo->next; 690 } 691 if (einfo == NULL) 692 continue; 693 pthread_mutex_lock(&DLogFdMutex); 694 einfop = &EInfo[i]; 695 while ((einfo = *einfop) != NULL) { 696 if (einfo->exited) { 697 *einfop = einfo->next; 698 if (einfo->logid) 699 free(einfo->logid); 700 free(einfo); 701 } else { 702 einfop = &einfo->next; 703 } 704 } 705 pthread_mutex_unlock(&DLogFdMutex); 706 } 707 mflags = 0; 708 continue; 709 } 710 711 /* 712 * Process SCM_CREDS, if present. Throw away SCM_RIGHTS 713 * if some sub-process stupidly sent it. 714 */ 715 einfo = NULL; 716 mflags = MSG_DONTWAIT; 717 718 if (len && buf[len-1] == '\n') 719 --len; 720 buf[len] = 0; 721 722 for (cmsg = CMSG_FIRSTHDR(&msg); 723 cmsg; 724 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 725 struct cmsgcred *cred; 726 int *fds; 727 int n; 728 729 if (cmsg->cmsg_level != SOL_SOCKET) 730 continue; 731 732 switch(cmsg->cmsg_type) { 733 case SCM_CREDS: 734 735 cred = (void *)CMSG_DATA(cmsg); 736 737 einfo = *einfohash(cred->cmcred_pid); 738 while (einfo && einfo->pid != cred->cmcred_pid) 739 einfo = einfo->next; 740 break; 741 case SCM_RIGHTS: 742 fds = (void *)CMSG_DATA(cmsg); 743 n = (cmsg->cmsg_len - sizeof(cmsg)) / 744 sizeof(int); 745 for (i = 0; i < n; ++i) 746 close(fds[i]); 747 break; 748 } 749 } 750 751 if (einfo && einfo->logid) { 752 dlog(DLOG_ALL | DLOG_STDOUT, 753 "%s: %s\n", 754 einfo->logid, buf); 755 } else { 756 dlog(DLOG_ALL | DLOG_STDOUT, "%s", buf); 757 } 758 } 759 return NULL; 760 } 761 762 const char * 763 getphasestr(worker_phase_t phaseid) 764 { 765 const char *phase; 766 767 switch(phaseid) { 768 case PHASE_PENDING: 769 phase = "pending"; 770 break; 771 case PHASE_INSTALL_PKGS: 772 phase = "install-pkgs"; 773 break; 774 case PHASE_CHECK_SANITY: 775 phase = "check-sanity"; 776 break; 777 case PHASE_PKG_DEPENDS: 778 phase = "pkg-depends"; 779 break; 780 case PHASE_FETCH_DEPENDS: 781 phase = "fetch-depends"; 782 break; 783 case PHASE_FETCH: 784 phase = "fetch"; 785 break; 786 case PHASE_CHECKSUM: 787 phase = "checksum"; 788 break; 789 case PHASE_EXTRACT_DEPENDS: 790 phase = "extract-depends"; 791 break; 792 case PHASE_EXTRACT: 793 phase = "extract"; 794 break; 795 case PHASE_PATCH_DEPENDS: 796 phase = "patch-depends"; 797 break; 798 case PHASE_PATCH: 799 phase = "patch"; 800 break; 801 case PHASE_BUILD_DEPENDS: 802 phase = "build-depends"; 803 break; 804 case PHASE_LIB_DEPENDS: 805 phase = "lib-depends"; 806 break; 807 case PHASE_CONFIGURE: 808 phase = "configure"; 809 break; 810 case PHASE_BUILD: 811 phase = "build"; 812 break; 813 case PHASE_RUN_DEPENDS: 814 phase = "run-depends"; 815 break; 816 case PHASE_STAGE: 817 phase = "stage"; 818 break; 819 case PHASE_TEST: 820 phase = "test"; 821 break; 822 case PHASE_CHECK_PLIST: 823 phase = "check-plist"; 824 break; 825 case PHASE_PACKAGE: 826 phase = "package"; 827 break; 828 case PHASE_INSTALL: 829 phase = "install"; 830 break; 831 case PHASE_DEINSTALL: 832 phase = "deinstall"; 833 break; 834 case PHASE_DUMP_ENV: 835 phase = "dump-env"; 836 break; 837 case PHASE_DUMP_VAR: 838 phase = "dump-var"; 839 break; 840 case PHASE_SHOW_CONFIG: 841 phase = "show-config"; 842 break; 843 case PHASE_DUMP_MAKECONF: 844 phase = "make-conf"; 845 break; 846 default: 847 phase = "Run-Unknown"; 848 break; 849 } 850 return phase; 851 } 852 853 int 854 readlogline(monitorlog_t *log, char **bufp) 855 { 856 int r; 857 int n; 858 859 /* 860 * Reset buffer as an optimization to avoid unnecessary 861 * shifts. 862 */ 863 *bufp = NULL; 864 if (log->buf_beg == log->buf_end) { 865 log->buf_beg = 0; 866 log->buf_end = 0; 867 log->buf_scan = 0; 868 } 869 870 /* 871 * Look for newline, handle discard mode 872 */ 873 again: 874 for (n = log->buf_scan; n < log->buf_end; ++n) { 875 if (log->buf[n] == '\n') { 876 *bufp = log->buf + log->buf_beg; 877 r = n - log->buf_beg; 878 log->buf_beg = n + 1; 879 log->buf_scan = n + 1; 880 881 if (log->buf_discard_mode == 0) 882 return r; 883 log->buf_discard_mode = 0; 884 goto again; 885 } 886 } 887 888 /* 889 * Handle overflow 890 */ 891 if (n == sizeof(log->buf)) { 892 if (log->buf_beg) { 893 /* 894 * Shift the buffer to make room and read more data. 895 */ 896 bcopy(log->buf + log->buf_beg, 897 log->buf, 898 n - log->buf_beg); 899 log->buf_end -= log->buf_beg; 900 log->buf_scan -= log->buf_beg; 901 n -= log->buf_beg; 902 log->buf_beg = 0; 903 } else if (log->buf_discard_mode) { 904 /* 905 * Overflow. If in discard mode just throw it all 906 * away. Stay in discard mode. 907 */ 908 log->buf_beg = 0; 909 log->buf_end = 0; 910 log->buf_scan = 0; 911 } else { 912 /* 913 * Overflow. If not in discard mode return a truncated 914 * line and enter discard mode. 915 * 916 * The caller will temporarily set ptr[r] = 0 so make 917 * sure that does not overflow our buffer as we are not 918 * at a newline. 919 * 920 * (log->buf_beg is 0); 921 */ 922 *bufp = log->buf + log->buf_beg; 923 r = n - 1; 924 log->buf_beg = n; 925 log->buf_scan = n; 926 log->buf_discard_mode = 1; 927 928 return r; 929 } 930 } 931 932 /* 933 * Read more data. If there is no data pending then return -1, 934 * otherwise loop up to see if more complete line(s) are available. 935 */ 936 r = pread(log->fd, 937 log->buf + log->buf_end, 938 sizeof(log->buf) - log->buf_end, 939 log->offset); 940 if (r <= 0) 941 return -1; 942 log->offset += r; 943 log->buf_end += r; 944 goto again; 945 } 946 947 uint32_t 948 crcDirTree(const char *path) 949 { 950 FTS *fts; 951 FTSENT *fen; 952 struct stat *st; 953 char *pav[2]; 954 uint32_t crc; 955 uint32_t val; 956 957 crc = 0; 958 pav[0] = strdup(path); 959 pav[1] = NULL; 960 961 fts = fts_open(pav, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 962 if (fts == NULL) 963 goto failed; 964 while ((fen = fts_read(fts)) != NULL) { 965 if (fen->fts_info != FTS_F && fen->fts_info != FTS_SL) 966 continue; 967 /* 968 * Ignore hidden dot files or ones ending with .core from the 969 * calculated CRC sum to prevent unnecessary rebuilds. 970 */ 971 if (fen->fts_name[0] == '.') 972 continue; 973 if (fen->fts_namelen >= 5 && 974 !strcmp(fen->fts_name + fen->fts_namelen - 5, ".core")) { 975 continue; 976 } 977 978 st = fen->fts_statp; 979 980 val = iscsi_crc32(&st->st_mtime, sizeof(st->st_mtime)); 981 val = iscsi_crc32_ext(&st->st_size, sizeof(st->st_size), val); 982 val = iscsi_crc32_ext(fen->fts_path, fen->fts_pathlen, val); 983 crc ^= val; 984 } 985 fts_close(fts); 986 failed: 987 free(pav[0]); 988 989 return crc; 990 } 991