1 /* 2 * Copyright (c) 2019 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 buildenv_t *BuildEnv; 41 static buildenv_t **BuildEnvTail = &BuildEnv; 42 43 extern char **environ; 44 45 __dead2 void 46 _dfatal(const char *file __unused, int line __unused, const char *func, 47 int do_errno, const char *ctl, ...) 48 { 49 va_list va; 50 51 fprintf(stderr, "%s: ", func); 52 va_start(va, ctl); 53 vfprintf(stderr, ctl, va); 54 va_end(va); 55 if (do_errno & 1) 56 fprintf(stderr, ": %s", strerror(errno)); 57 fprintf(stderr, "\n"); 58 fflush(stderr); 59 60 if (do_errno & 2) 61 kill(getpid(), SIGQUIT); 62 exit(1); 63 } 64 65 void 66 _ddprintf(int tab, const char *ctl, ...) 67 { 68 va_list va; 69 70 if (tab) 71 printf("%*.*s", tab, tab, ""); 72 va_start(va, ctl); 73 vfprintf(stdout, ctl, va); 74 va_end(va); 75 } 76 77 char * 78 strdup_or_null(char *str) 79 { 80 if (str && str[0]) 81 return(strdup(str)); 82 return NULL; 83 } 84 85 static const char *DLogNames[] = { 86 "00_last_results.log", 87 "01_success_list.log", 88 "02_failure_list.log", 89 "03_ignored_list.log", 90 "04_skipped_list.log", 91 "05_abnormal_command_output.log", 92 "06_obsolete_packages.log", 93 "07_debug.log", 94 }; 95 96 static int DLogFd[DLOG_COUNT]; 97 static pthread_mutex_t DLogFdMutex; 98 99 #define arysize(ary) (sizeof((ary)) / sizeof((ary)[0])) 100 101 static int 102 dlogfd(int which, int modes) 103 { 104 char *path; 105 int fd; 106 107 which &= DLOG_MASK; 108 if ((fd = DLogFd[which]) > 0) 109 return fd; 110 pthread_mutex_lock(&DLogFdMutex); 111 if ((fd = DLogFd[which]) <= 0) { 112 asprintf(&path, "%s/%s", LogsPath, DLogNames[which]); 113 fd = open(path, modes, 0666); 114 DLogFd[which] = fd; 115 free(path); 116 } 117 pthread_mutex_unlock(&DLogFdMutex); 118 119 return fd; 120 } 121 122 123 void 124 dlogreset(void) 125 { 126 int i; 127 128 ddassert(DLOG_COUNT == arysize(DLogNames)); 129 for (i = 0; i < DLOG_COUNT; ++i) { 130 if (DLogFd[i] > 0) { 131 close(DLogFd[i]); 132 DLogFd[i] = -1; 133 } 134 (void)dlogfd(i, O_RDWR|O_CREAT|O_TRUNC|O_APPEND); 135 } 136 } 137 138 void 139 _dlog(int which, const char *ctl, ...) 140 { 141 va_list va; 142 char *buf; 143 char *ptr; 144 size_t len; 145 int fd; 146 int filter; 147 148 filter = which; 149 which &= DLOG_MASK; 150 151 ddassert((uint)which < DLOG_COUNT); 152 va_start(va, ctl); 153 vasprintf(&buf, ctl, va); 154 va_end(va); 155 len = strlen(buf); 156 157 /* 158 * The special sequence ## right-justfies the text after the ##. 159 * 160 * NOTE: Alignment test includes \n so use 80 instead of 79 to 161 * leave one char unused on a 80-column terminal. 162 */ 163 if ((ptr = strstr(buf, "##")) != NULL) { 164 size_t l2; 165 size_t l1; 166 char *b2; 167 int spc; 168 169 l1 = (int)(ptr - buf); 170 l2 = len - l1 - 2; 171 if (l1 <= 80 - l2) { 172 spc = 80 - l1 - l2; 173 buf[l1] = 0; 174 asprintf(&b2, "%s%*.*s%s", 175 buf, spc, spc, "", ptr + 2); 176 } else { 177 buf[l1] = 0; 178 asprintf(&b2, "%s%s", buf, ptr + 2); 179 } 180 len = strlen(b2); 181 free(buf); 182 buf = b2; 183 } 184 185 /* 186 * All logs also go to log 00. 187 */ 188 if (which != DLOG_ALL) { 189 fd = dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND); 190 if (fd > 0) 191 write(fd, buf, len); 192 } 193 194 /* 195 * Nominal log target 196 */ 197 fd = dlogfd(which, O_RDWR|O_CREAT|O_APPEND); 198 write(fd, buf, len); 199 200 /* 201 * If ncurses is not being used, all log output also goes 202 * to stdout, unless filtered. 203 */ 204 if ((UseNCurses == 0 || (filter & DLOG_STDOUT)) && 205 (filter & DLOG_FILTER) == 0) { 206 if (ColorOpt) { 207 if (filter & DLOG_GRN) 208 write(1, "\x1b[0;32m", 7); 209 if (filter & DLOG_RED) 210 write(1, "\x1b[0;31m", 7); 211 } 212 write(1, buf, len); 213 if (ColorOpt && (filter & (DLOG_GRN|DLOG_RED))) { 214 write(1, "\x1b[0;39m", 7); 215 } 216 } 217 free(buf); 218 } 219 220 int 221 dlog00_fd(void) 222 { 223 return(dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND)); 224 } 225 226 /* 227 * Bulk and Build environment control. These routines are only called 228 * unthreaded or when dsynth threads are idle. 229 */ 230 void 231 addbuildenv(const char *label, const char *data, int type) 232 { 233 buildenv_t *env; 234 235 env = calloc(1, sizeof(*env)); 236 env->a1 = strdup(label); 237 env->a2 = strdup(data); 238 env->label = env->a1; 239 env->data = env->a2; 240 env->type = type; 241 *BuildEnvTail = env; 242 BuildEnvTail = &env->next; 243 } 244 245 void 246 delbuildenv(const char *label) 247 { 248 buildenv_t **envp; 249 buildenv_t *env; 250 251 envp = &BuildEnv; 252 while ((env = *envp) != NULL) { 253 if (strcmp(env->label, label) == 0) { 254 *envp = env->next; 255 if (env->a1) 256 free(env->a1); 257 if (env->a2) 258 free(env->a2); 259 free(env); 260 } else { 261 envp = &env->next; 262 } 263 } 264 BuildEnvTail = envp; 265 } 266 267 void 268 freestrp(char **strp) 269 { 270 if (*strp) { 271 free(*strp); 272 *strp = NULL; 273 } 274 } 275 276 void 277 dupstrp(char **strp) 278 { 279 if (*strp) 280 *strp = strdup(*strp); 281 } 282 283 int 284 ipcreadmsg(int fd, wmsg_t *msg) 285 { 286 size_t res; 287 ssize_t r; 288 char *ptr; 289 290 res = sizeof(*msg); 291 ptr = (char *)(void *)msg; 292 while (res) { 293 r = read(fd, ptr, res); 294 if (r <= 0) { 295 if (errno == EINTR) 296 continue; 297 return -1; 298 } 299 res -= (size_t)r; 300 ptr += r; 301 } 302 return 0; 303 } 304 305 int 306 ipcwritemsg(int fd, wmsg_t *msg) 307 { 308 size_t res; 309 ssize_t r; 310 char *ptr; 311 312 res = sizeof(*msg); 313 ptr = (char *)(void *)msg; 314 while (res) { 315 r = write(fd, ptr, res); 316 if (r < 0) { 317 if (errno == EINTR) 318 continue; 319 return -1; 320 } 321 res -= (size_t)r; 322 ptr += r; 323 } 324 return 0; 325 } 326 327 int 328 askyn(const char *ctl, ...) 329 { 330 va_list va; 331 char buf[256]; 332 int res = 0; 333 334 if (YesOpt) 335 return 1; 336 337 for (;;) { 338 va_start(va, ctl); 339 vprintf(ctl, va); 340 va_end(va); 341 fflush(stdout); 342 if (fgets(buf, sizeof(buf), stdin) == NULL) 343 break; 344 if (buf[0] == 'y' || buf[0] == 'Y') { 345 res = 1; 346 break; 347 } 348 if (buf[0] == 'n' || buf[0] == 'N') { 349 res = 0; 350 break; 351 } 352 printf("Please type y/n\n"); 353 } 354 return res; 355 } 356 357 /* 358 * Get swap% used 0.0-1.0. 359 * 360 * NOTE: This routine is intended to return quickly. 361 * 362 * NOTE: swap_cache (caching for hard drives) is persistent and should 363 * not be counted for our purposes. 364 */ 365 double 366 getswappct(int *noswapp) 367 { 368 long swap_size = 0; 369 long swap_anon = 0; 370 long swap_cache __unused = 0; 371 size_t len; 372 double dswap; 373 374 len = sizeof(swap_size); 375 sysctlbyname("vm.swap_size", &swap_size, &len, NULL, 0); 376 len = sizeof(swap_size); 377 sysctlbyname("vm.swap_anon_use", &swap_anon, &len, NULL, 0); 378 len = sizeof(swap_size); 379 sysctlbyname("vm.swap_cache_use", &swap_cache, &len, NULL, 0); 380 if (swap_size) { 381 dswap = (double)(swap_anon /*+swap_cache*/) / (double)swap_size; 382 *noswapp = 0; 383 } else { 384 dswap = 0.0; 385 *noswapp = 1; 386 } 387 return dswap; 388 } 389 390 /* 391 * dexec_open()/fgets/dexec_close() 392 * 393 * Similar to popen() but directly exec()s the argument list (cav[0] must 394 * be an absolute path). 395 * 396 * If xenv is non-NULL its an array of local buildenv_t's to be used. 397 * The array is terminated with a NULL xenv->label. 398 * 399 * If with_env is non-zero the configured environment is included. 400 * 401 * If with_mvars is non-zero the make environment is passed as VAR=DATA 402 * elements on the command line. 403 */ 404 FILE * 405 dexec_open(const char **cav, int cac, pid_t *pidp, buildenv_t *xenv, 406 int with_env, int with_mvars) 407 { 408 buildenv_t *benv; 409 const char **cenv; 410 char *allocary[MAXCAC*2]; 411 int env_basei; 412 int envi; 413 int alloci; 414 int nullfd; 415 int fds[2]; 416 pid_t pid; 417 FILE *fp; 418 419 env_basei = 0; 420 while (environ[env_basei]) 421 ++env_basei; 422 cenv = calloc(env_basei + MAXCAC, sizeof(char *)); 423 env_basei = 0; 424 for (envi = 0; envi < env_basei; ++envi) 425 cenv[envi] = environ[envi]; 426 427 alloci = 0; 428 for (benv = BuildEnv; benv; benv = benv->next) { 429 if (with_mvars && 430 (benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 431 asprintf(&allocary[alloci], "%s=%s", 432 benv->label, benv->data); 433 cav[cac++] = allocary[alloci]; 434 ++alloci; 435 } 436 if (with_env && 437 (benv->type & BENV_PKGLIST) && 438 (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) { 439 asprintf(&allocary[alloci], "%s=%s", 440 benv->label, benv->data); 441 cenv[envi++] = allocary[alloci]; 442 ++alloci; 443 } 444 ddassert(cac < MAXCAC && envi - env_basei < MAXCAC); 445 } 446 447 /* 448 * Extra environment specific to this particular dexec 449 */ 450 while (xenv && xenv->label) { 451 asprintf(&allocary[alloci], "%s=%s", 452 xenv->label, xenv->data); 453 cenv[envi++] = allocary[alloci]; 454 ++alloci; 455 ++xenv; 456 } 457 458 cav[cac] = NULL; 459 cenv[envi] = NULL; 460 461 if (pipe(fds) < 0) 462 dfatal_errno("pipe"); 463 nullfd = open("/dev/null", O_RDWR); 464 if (nullfd < 0) 465 dfatal_errno("open(\"/dev/null\")"); 466 467 /* 468 * We have to be very careful using vfork(), do only the bare 469 * minimum necessary in the child to set it up and exec it. 470 */ 471 pid = vfork(); 472 if (pid == 0) { 473 #if 0 474 int i; 475 printf("%s", cav[0]); 476 for (i = 0; cav[i]; ++i) 477 printf(" %s", cav[i]); 478 printf("\n"); 479 printf("ENV: "); 480 for (i = 0; cenv[i]; ++i) 481 printf(" %s", cenv[i]); 482 #endif 483 484 if (fds[1] != 1) { 485 dup2(fds[1], 1); 486 close(fds[1]); 487 } 488 close(fds[0]); /* safety */ 489 dup2(nullfd, 0); /* no questions! */ 490 closefrom(3); /* be nice */ 491 492 /* 493 * Self-nice to be nice (ignore any error) 494 */ 495 if (NiceOpt) 496 setpriority(PRIO_PROCESS, 0, NiceOpt); 497 498 execve(cav[0], (void *)cav, (void *)cenv); 499 write(2, "EXEC FAILURE\n", 13); 500 _exit(1); 501 } 502 close(nullfd); 503 close(fds[1]); 504 if (pid < 0) { 505 close(fds[0]); 506 dfatal_errno("vfork failed"); 507 } 508 fp = fdopen(fds[0], "r"); 509 *pidp = pid; 510 511 while (--alloci >= 0) 512 free(allocary[alloci]); 513 free(cenv); 514 515 return fp; 516 } 517 518 int 519 dexec_close(FILE *fp, pid_t pid) 520 { 521 pid_t rpid; 522 int status; 523 524 fclose(fp); 525 while ((rpid = waitpid(pid, &status, 0)) != pid) { 526 if (rpid < 0) { 527 if (errno == EINTR) 528 continue; 529 return 1; 530 } 531 } 532 return (WEXITSTATUS(status)); 533 } 534 535 const char * 536 getphasestr(worker_phase_t phaseid) 537 { 538 const char *phase; 539 540 switch(phaseid) { 541 case PHASE_PENDING: 542 phase = "pending"; 543 break; 544 case PHASE_INSTALL_PKGS: 545 phase = "install-pkgs"; 546 break; 547 case PHASE_CHECK_SANITY: 548 phase = "check-sanity"; 549 break; 550 case PHASE_PKG_DEPENDS: 551 phase = "pkg-depends"; 552 break; 553 case PHASE_FETCH_DEPENDS: 554 phase = "fetch-depends"; 555 break; 556 case PHASE_FETCH: 557 phase = "fetch"; 558 break; 559 case PHASE_CHECKSUM: 560 phase = "checksum"; 561 break; 562 case PHASE_EXTRACT_DEPENDS: 563 phase = "extract-depends"; 564 break; 565 case PHASE_EXTRACT: 566 phase = "extract"; 567 break; 568 case PHASE_PATCH_DEPENDS: 569 phase = "patch-depends"; 570 break; 571 case PHASE_PATCH: 572 phase = "patch"; 573 break; 574 case PHASE_BUILD_DEPENDS: 575 phase = "build-depends"; 576 break; 577 case PHASE_LIB_DEPENDS: 578 phase = "lib-depends"; 579 break; 580 case PHASE_CONFIGURE: 581 phase = "configure"; 582 break; 583 case PHASE_BUILD: 584 phase = "build"; 585 break; 586 case PHASE_RUN_DEPENDS: 587 phase = "run-depends"; 588 break; 589 case PHASE_STAGE: 590 phase = "stage"; 591 break; 592 case PHASE_TEST: 593 phase = "test"; 594 break; 595 case PHASE_CHECK_PLIST: 596 phase = "check-plist"; 597 break; 598 case PHASE_PACKAGE: 599 phase = "package"; 600 break; 601 case PHASE_INSTALL_MTREE: 602 phase = "install-mtree"; 603 break; 604 case PHASE_INSTALL: 605 phase = "install"; 606 break; 607 case PHASE_DEINSTALL: 608 phase = "deinstall"; 609 break; 610 default: 611 phase = "Run-Unknown"; 612 break; 613 } 614 return phase; 615 } 616 617 int 618 readlogline(monitorlog_t *log, char **bufp) 619 { 620 int r; 621 int n; 622 623 /* 624 * Reset buffer as an optimization to avoid unnecessary 625 * shifts. 626 */ 627 *bufp = NULL; 628 if (log->buf_beg == log->buf_end) { 629 log->buf_beg = 0; 630 log->buf_end = 0; 631 log->buf_scan = 0; 632 } 633 634 /* 635 * Look for newline, handle discard mode 636 */ 637 again: 638 for (n = log->buf_scan; n < log->buf_end; ++n) { 639 if (log->buf[n] == '\n') { 640 *bufp = log->buf + log->buf_beg; 641 r = n - log->buf_beg; 642 log->buf_beg = n + 1; 643 log->buf_scan = n + 1; 644 645 if (log->buf_discard_mode == 0) 646 return r; 647 log->buf_discard_mode = 0; 648 goto again; 649 } 650 } 651 652 /* 653 * Handle overflow 654 */ 655 if (n == sizeof(log->buf)) { 656 if (log->buf_beg) { 657 /* 658 * Shift the buffer to make room and read more data. 659 */ 660 bcopy(log->buf + log->buf_beg, 661 log->buf, 662 n - log->buf_beg); 663 log->buf_end -= log->buf_beg; 664 log->buf_scan -= log->buf_beg; 665 n -= log->buf_beg; 666 log->buf_beg = 0; 667 } else if (log->buf_discard_mode) { 668 /* 669 * Overflow. If in discard mode just throw it all 670 * away. Stay in discard mode. 671 */ 672 log->buf_beg = 0; 673 log->buf_end = 0; 674 log->buf_scan = 0; 675 } else { 676 /* 677 * Overflow. If not in discard mode return a truncated 678 * line and enter discard mode. 679 * 680 * The caller will temporarily set ptr[r] = 0 so make 681 * sure that does not overflow our buffer as we are not 682 * at a newline. 683 * 684 * (log->buf_beg is 0); 685 */ 686 *bufp = log->buf + log->buf_beg; 687 r = n - 1; 688 log->buf_beg = n; 689 log->buf_scan = n; 690 log->buf_discard_mode = 1; 691 692 return r; 693 } 694 } 695 696 /* 697 * Read more data. If there is no data pending then return -1, 698 * otherwise loop up to see if more complete line(s) are available. 699 */ 700 r = pread(log->fd, 701 log->buf + log->buf_end, 702 sizeof(log->buf) - log->buf_end, 703 log->offset); 704 if (r <= 0) 705 return -1; 706 log->offset += r; 707 log->buf_end += r; 708 goto again; 709 } 710 711 uint32_t 712 crcDirTree(const char *path) 713 { 714 FTS *fts; 715 FTSENT *fen; 716 struct stat *st; 717 char *pav[2]; 718 uint32_t crc; 719 uint32_t val; 720 721 crc = 0; 722 pav[0] = strdup(path); 723 pav[1] = NULL; 724 725 fts = fts_open(pav, FTS_PHYSICAL | FTS_NOCHDIR, NULL); 726 if (fts == NULL) 727 goto failed; 728 while ((fen = fts_read(fts)) != NULL) { 729 if (fen->fts_info != FTS_F && fen->fts_info != FTS_SL) 730 continue; 731 if (fen->fts_namelen >= 5 && 732 !strcmp(fen->fts_name + fen->fts_namelen - 5, ".core")) { 733 continue; 734 } 735 736 st = fen->fts_statp; 737 738 val = iscsi_crc32(&st->st_mtime, sizeof(st->st_mtime)); 739 val = iscsi_crc32_ext(&st->st_size, sizeof(st->st_size), val); 740 val = iscsi_crc32_ext(fen->fts_path, fen->fts_pathlen, val); 741 crc ^= val; 742 } 743 fts_close(fts); 744 failed: 745 free(pav[0]); 746 747 return crc; 748 } 749