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 #if 0 487 /* 488 * For now we are ignoring the passed-in environment, so don't 489 * copy the existing environment into the exec environment. 490 */ 491 while (environ[env_basei]) 492 ++env_basei; 493 #endif 494 cenv = calloc(env_basei + MAXCAC, sizeof(char *)); 495 for (envi = 0; envi < env_basei; ++envi) 496 cenv[envi] = environ[envi]; 497 498 alloci = 0; 499 for (benv = BuildEnv; benv; benv = benv->next) { 500 if (with_mvars && 501 (benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 502 asprintf(&allocary[alloci], "%s=%s", 503 benv->label, benv->data); 504 cav[cac++] = allocary[alloci]; 505 ++alloci; 506 } 507 if (with_env && 508 (benv->type & BENV_PKGLIST) && 509 (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) { 510 asprintf(&allocary[alloci], "%s=%s", 511 benv->label, benv->data); 512 cenv[envi++] = allocary[alloci]; 513 ++alloci; 514 } 515 ddassert(cac < MAXCAC && envi - env_basei < MAXCAC); 516 } 517 518 /* 519 * Extra environment specific to this particular dexec 520 */ 521 while (xenv && xenv->label) { 522 asprintf(&allocary[alloci], "%s=%s", 523 xenv->label, xenv->data); 524 cenv[envi++] = allocary[alloci]; 525 ++alloci; 526 ++xenv; 527 } 528 529 cav[cac] = NULL; 530 cenv[envi] = NULL; 531 532 if (pipe2(fds, O_CLOEXEC) < 0) 533 dfatal_errno("pipe"); 534 nullfd = open("/dev/null", O_RDWR | O_CLOEXEC); 535 if (nullfd < 0) 536 dfatal_errno("open(\"/dev/null\")"); 537 538 /* 539 * We have to be very careful using vfork(), do only the bare 540 * minimum necessary in the child to set it up and exec it. 541 */ 542 pid = vfork(); 543 if (pid == 0) { 544 #if 0 545 int i; 546 printf("%s", cav[0]); 547 for (i = 0; cav[i]; ++i) 548 printf(" %s", cav[i]); 549 printf("\n"); 550 printf("ENV: "); 551 for (i = 0; cenv[i]; ++i) 552 printf(" %s", cenv[i]); 553 #endif 554 555 if (fds[1] != 1) { 556 dup2(fds[1], 1); 557 close(fds[1]); 558 } 559 if (EFds[1] != 2) { 560 dup2(EFds[1], 2); 561 close(EFds[1]); 562 } 563 close(fds[0]); /* safety */ 564 close(EFds[0]); /* safety */ 565 dup2(nullfd, 0); /* no questions! */ 566 closefrom(3); /* be nice */ 567 568 /* 569 * Self-nice to be nice (ignore any error) 570 */ 571 if (NiceOpt) 572 setpriority(PRIO_PROCESS, 0, NiceOpt); 573 574 execve(cav[0], (void *)cav, (void *)cenv); 575 write(2, "EXEC FAILURE\n", 13); 576 _exit(1); 577 } 578 close(nullfd); 579 close(fds[1]); 580 if (pid < 0) { 581 close(fds[0]); 582 dfatal_errno("vfork failed"); 583 } 584 fp = fdopen(fds[0], "r"); 585 *pidp = pid; 586 587 /* 588 * einfo setup 589 */ 590 einfo = calloc(1, sizeof(*einfo)); 591 if (logid) 592 einfo->logid = strdup(logid); 593 594 pthread_mutex_lock(&DLogFdMutex); 595 einfo->pid = pid; 596 einfo->next = *einfohash(pid); 597 *einfohash(pid) = einfo; 598 pthread_cond_signal(&ECond); 599 pthread_mutex_unlock(&DLogFdMutex); 600 601 while (--alloci >= 0) 602 free(allocary[alloci]); 603 free(cenv); 604 605 return fp; 606 } 607 608 int 609 dexec_close(FILE *fp, pid_t pid) 610 { 611 struct logerrinfo **einfop; 612 struct logerrinfo *einfo; 613 pid_t rpid; 614 int status; 615 616 fclose(fp); 617 while ((rpid = waitpid(pid, &status, 0)) != pid) { 618 if (rpid < 0) { 619 if (errno == EINTR) 620 continue; 621 return 1; 622 } 623 } 624 625 pthread_mutex_lock(&DLogFdMutex); 626 einfop = einfohash(pid); 627 while ((einfo = *einfop) != NULL) { 628 if (einfo->pid == pid) { 629 einfo->exited = 1; 630 break; 631 } 632 einfop = &einfo->next; 633 } 634 pthread_mutex_unlock(&DLogFdMutex); 635 636 return (WEXITSTATUS(status)); 637 } 638 639 static void * 640 dexec_logerr_thread(void *dummy __unused) 641 { 642 char buf[4096]; 643 union { 644 char cbuf[CMSG_SPACE(sizeof(struct ucred))]; 645 struct cmsghdr cbuf_align; 646 } cmsg_buf; 647 struct cmsghdr *cmsg; 648 struct logerrinfo **einfop; 649 struct logerrinfo *einfo; 650 struct msghdr msg; 651 struct iovec iov; 652 ssize_t len; 653 int mflags = MSG_DONTWAIT; 654 int fd; 655 656 pthread_detach(pthread_self()); 657 658 msg.msg_name = NULL; 659 msg.msg_namelen = 0; 660 msg.msg_iov = &iov; 661 662 msg.msg_control = cmsg_buf.cbuf; 663 msg.msg_controllen = sizeof(cmsg_buf.cbuf); 664 665 fd = EFds[0]; 666 for (;;) { 667 int i; 668 669 msg.msg_iovlen = 1; 670 iov.iov_base = buf; 671 iov.iov_len = sizeof(buf) - 1; 672 673 /* 674 * Wait for message, if none are pending then clean-up 675 * exited einfos (this ensures that we have flushed all 676 * error output before freeing the structure). 677 * 678 * Don't obtain the mutex until we really need it. The 679 * parent thread can only 'add' einfo entries to the hash 680 * table so we are basically scan-safe. 681 */ 682 len = recvmsg(fd, &msg, mflags); 683 if (len < 0) { 684 if (errno != EAGAIN) { /* something messed up */ 685 fprintf(stderr, "ERRNO %d\n", errno); 686 break; 687 } 688 689 for (i = 0; i < EINFO_HSIZE; ++i) { 690 einfo = EInfo[i]; 691 while (einfo) { 692 if (einfo->exited) 693 break; 694 einfo = einfo->next; 695 } 696 if (einfo == NULL) 697 continue; 698 pthread_mutex_lock(&DLogFdMutex); 699 einfop = &EInfo[i]; 700 while ((einfo = *einfop) != NULL) { 701 if (einfo->exited) { 702 *einfop = einfo->next; 703 if (einfo->logid) 704 free(einfo->logid); 705 free(einfo); 706 } else { 707 einfop = &einfo->next; 708 } 709 } 710 pthread_mutex_unlock(&DLogFdMutex); 711 } 712 mflags = 0; 713 continue; 714 } 715 716 /* 717 * Process SCM_CREDS, if present. Throw away SCM_RIGHTS 718 * if some sub-process stupidly sent it. 719 */ 720 einfo = NULL; 721 mflags = MSG_DONTWAIT; 722 723 if (len && buf[len-1] == '\n') 724 --len; 725 buf[len] = 0; 726 727 for (cmsg = CMSG_FIRSTHDR(&msg); 728 cmsg; 729 cmsg = CMSG_NXTHDR(&msg, cmsg)) { 730 struct cmsgcred *cred; 731 int *fds; 732 int n; 733 734 if (cmsg->cmsg_level != SOL_SOCKET) 735 continue; 736 737 switch(cmsg->cmsg_type) { 738 case SCM_CREDS: 739 740 cred = (void *)CMSG_DATA(cmsg); 741 742 einfo = *einfohash(cred->cmcred_pid); 743 while (einfo && einfo->pid != cred->cmcred_pid) 744 einfo = einfo->next; 745 break; 746 case SCM_RIGHTS: 747 fds = (void *)CMSG_DATA(cmsg); 748 n = (cmsg->cmsg_len - sizeof(cmsg)) / 749 sizeof(int); 750 for (i = 0; i < n; ++i) 751 close(fds[i]); 752 break; 753 } 754 } 755 756 if (einfo && einfo->logid) { 757 dlog(DLOG_ALL | DLOG_STDOUT, 758 "%s: %s\n", 759 einfo->logid, buf); 760 } else { 761 dlog(DLOG_ALL | DLOG_STDOUT, "%s", buf); 762 } 763 } 764 return NULL; 765 } 766 767 const char * 768 getphasestr(worker_phase_t phaseid) 769 { 770 const char *phase; 771 772 switch(phaseid) { 773 case PHASE_PENDING: 774 phase = "pending"; 775 break; 776 case PHASE_INSTALL_PKGS: 777 phase = "install-pkgs"; 778 break; 779 case PHASE_CHECK_SANITY: 780 phase = "check-sanity"; 781 break; 782 case PHASE_PKG_DEPENDS: 783 phase = "pkg-depends"; 784 break; 785 case PHASE_FETCH_DEPENDS: 786 phase = "fetch-depends"; 787 break; 788 case PHASE_FETCH: 789 phase = "fetch"; 790 break; 791 case PHASE_CHECKSUM: 792 phase = "checksum"; 793 break; 794 case PHASE_EXTRACT_DEPENDS: 795 phase = "extract-depends"; 796 break; 797 case PHASE_EXTRACT: 798 phase = "extract"; 799 break; 800 case PHASE_PATCH_DEPENDS: 801 phase = "patch-depends"; 802 break; 803 case PHASE_PATCH: 804 phase = "patch"; 805 break; 806 case PHASE_BUILD_DEPENDS: 807 phase = "build-depends"; 808 break; 809 case PHASE_LIB_DEPENDS: 810 phase = "lib-depends"; 811 break; 812 case PHASE_CONFIGURE: 813 phase = "configure"; 814 break; 815 case PHASE_BUILD: 816 phase = "build"; 817 break; 818 case PHASE_RUN_DEPENDS: 819 phase = "run-depends"; 820 break; 821 case PHASE_STAGE: 822 phase = "stage"; 823 break; 824 case PHASE_TEST: 825 phase = "test"; 826 break; 827 case PHASE_CHECK_PLIST: 828 phase = "check-plist"; 829 break; 830 case PHASE_PACKAGE: 831 phase = "package"; 832 break; 833 case PHASE_INSTALL: 834 phase = "install"; 835 break; 836 case PHASE_DEINSTALL: 837 phase = "deinstall"; 838 break; 839 case PHASE_DUMP_ENV: 840 phase = "dump-env"; 841 break; 842 case PHASE_DUMP_VAR: 843 phase = "dump-var"; 844 break; 845 case PHASE_SHOW_CONFIG: 846 phase = "show-config"; 847 break; 848 case PHASE_DUMP_MAKECONF: 849 phase = "make-conf"; 850 break; 851 default: 852 phase = "Run-Unknown"; 853 break; 854 } 855 return phase; 856 } 857 858 int 859 readlogline(monitorlog_t *log, char **bufp) 860 { 861 int r; 862 int n; 863 864 /* 865 * Reset buffer as an optimization to avoid unnecessary 866 * shifts. 867 */ 868 *bufp = NULL; 869 if (log->buf_beg == log->buf_end) { 870 log->buf_beg = 0; 871 log->buf_end = 0; 872 log->buf_scan = 0; 873 } 874 875 /* 876 * Look for newline, handle discard mode 877 */ 878 again: 879 for (n = log->buf_scan; n < log->buf_end; ++n) { 880 if (log->buf[n] == '\n') { 881 *bufp = log->buf + log->buf_beg; 882 r = n - log->buf_beg; 883 log->buf_beg = n + 1; 884 log->buf_scan = n + 1; 885 886 if (log->buf_discard_mode == 0) 887 return r; 888 log->buf_discard_mode = 0; 889 goto again; 890 } 891 } 892 893 /* 894 * Handle overflow 895 */ 896 if (n == sizeof(log->buf)) { 897 if (log->buf_beg) { 898 /* 899 * Shift the buffer to make room and read more data. 900 */ 901 bcopy(log->buf + log->buf_beg, 902 log->buf, 903 n - log->buf_beg); 904 log->buf_end -= log->buf_beg; 905 log->buf_scan -= log->buf_beg; 906 n -= log->buf_beg; 907 log->buf_beg = 0; 908 } else if (log->buf_discard_mode) { 909 /* 910 * Overflow. If in discard mode just throw it all 911 * away. Stay in discard mode. 912 */ 913 log->buf_beg = 0; 914 log->buf_end = 0; 915 log->buf_scan = 0; 916 } else { 917 /* 918 * Overflow. If not in discard mode return a truncated 919 * line and enter discard mode. 920 * 921 * The caller will temporarily set ptr[r] = 0 so make 922 * sure that does not overflow our buffer as we are not 923 * at a newline. 924 * 925 * (log->buf_beg is 0); 926 */ 927 *bufp = log->buf + log->buf_beg; 928 r = n - 1; 929 log->buf_beg = n; 930 log->buf_scan = n; 931 log->buf_discard_mode = 1; 932 933 return r; 934 } 935 } 936 937 /* 938 * Read more data. If there is no data pending then return -1, 939 * otherwise loop up to see if more complete line(s) are available. 940 */ 941 r = pread(log->fd, 942 log->buf + log->buf_end, 943 sizeof(log->buf) - log->buf_end, 944 log->offset); 945 if (r <= 0) 946 return -1; 947 log->offset += r; 948 log->buf_end += r; 949 goto again; 950 } 951 952 uint32_t 953 crcDirTree(const char *path) 954 { 955 FTS *fts; 956 FTSENT *fen; 957 struct stat *st; 958 char *pav[2]; 959 uint32_t crc; 960 uint32_t val; 961 962 crc = 0; 963 pav[0] = strdup(path); 964 pav[1] = NULL; 965 966 fts = fts_open(pav, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 967 if (fts == NULL) 968 goto failed; 969 while ((fen = fts_read(fts)) != NULL) { 970 if (fen->fts_info != FTS_F && fen->fts_info != FTS_SL) 971 continue; 972 /* 973 * Ignore hidden dot files or ones ending with .core from the 974 * calculated CRC sum to prevent unnecessary rebuilds. 975 */ 976 if (fen->fts_name[0] == '.') 977 continue; 978 if (fen->fts_namelen >= 5 && 979 !strcmp(fen->fts_name + fen->fts_namelen - 5, ".core")) { 980 continue; 981 } 982 983 st = fen->fts_statp; 984 985 val = iscsi_crc32(&st->st_mtime, sizeof(st->st_mtime)); 986 val = iscsi_crc32_ext(&st->st_size, sizeof(st->st_size), val); 987 val = iscsi_crc32_ext(fen->fts_path, fen->fts_pathlen, val); 988 crc ^= val; 989 } 990 fts_close(fts); 991 failed: 992 free(pav[0]); 993 994 return crc; 995 } 996