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 }; 94 95 static int DLogFd[DLOG_COUNT]; 96 static pthread_mutex_t DLogFdMutex; 97 98 #define arysize(ary) (sizeof((ary)) / sizeof((ary)[0])) 99 100 static int 101 dlogfd(int which, int modes) 102 { 103 char *path; 104 int fd; 105 106 which &= DLOG_MASK; 107 if ((fd = DLogFd[which]) > 0) 108 return fd; 109 pthread_mutex_lock(&DLogFdMutex); 110 if ((fd = DLogFd[which]) <= 0) { 111 asprintf(&path, "%s/%s", LogsPath, DLogNames[which]); 112 fd = open(path, modes, 0666); 113 DLogFd[which] = fd; 114 free(path); 115 } 116 pthread_mutex_unlock(&DLogFdMutex); 117 118 return fd; 119 } 120 121 122 void 123 dlogreset(void) 124 { 125 int i; 126 127 ddassert(DLOG_COUNT == arysize(DLogNames)); 128 for (i = 0; i < DLOG_COUNT; ++i) { 129 if (DLogFd[i] > 0) { 130 close(DLogFd[i]); 131 DLogFd[i] = -1; 132 } 133 (void)dlogfd(i, O_RDWR|O_CREAT|O_TRUNC|O_APPEND); 134 } 135 } 136 137 void 138 _dlog(int which, const char *ctl, ...) 139 { 140 va_list va; 141 char *buf; 142 char *ptr; 143 size_t len; 144 int fd; 145 int filter; 146 147 filter = which; 148 which &= DLOG_MASK; 149 150 ddassert((uint)which < DLOG_COUNT); 151 va_start(va, ctl); 152 vasprintf(&buf, ctl, va); 153 va_end(va); 154 len = strlen(buf); 155 156 /* 157 * The special sequence ## right-justfies the text after the ##. 158 * 159 * NOTE: Alignment test includes \n so use 80 instead of 79 to 160 * leave one char unused on a 80-column terminal. 161 */ 162 if ((ptr = strstr(buf, "##")) != NULL) { 163 size_t l2; 164 size_t l1; 165 char *b2; 166 int spc; 167 168 l1 = (int)(ptr - buf); 169 l2 = len - l1 - 2; 170 if (l1 <= 80 - l2) { 171 spc = 80 - l1 - l2; 172 buf[l1] = 0; 173 asprintf(&b2, "%s%*.*s%s", 174 buf, spc, spc, "", ptr + 2); 175 } else { 176 buf[l1] = 0; 177 asprintf(&b2, "%s%s", buf, ptr + 2); 178 } 179 len = strlen(b2); 180 free(buf); 181 buf = b2; 182 } 183 184 /* 185 * All logs also go to log 00. 186 */ 187 if (which != DLOG_ALL) { 188 fd = dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND); 189 if (fd > 0) 190 write(fd, buf, len); 191 } 192 193 /* 194 * Nominal log target 195 */ 196 fd = dlogfd(which, O_RDWR|O_CREAT|O_APPEND); 197 write(fd, buf, len); 198 199 /* 200 * If ncurses is not being used, all log output also goes 201 * to stdout, unless filtered. 202 */ 203 if (UseNCurses == 0 && (filter & DLOG_FILTER) == 0) { 204 if (ColorOpt) { 205 if (filter & DLOG_GRN) 206 write(1, "\x1b[0;32m", 7); 207 if (filter & DLOG_RED) 208 write(1, "\x1b[0;31m", 7); 209 } 210 write(1, buf, len); 211 if (ColorOpt && (filter & (DLOG_GRN|DLOG_RED))) { 212 write(1, "\x1b[0;39m", 7); 213 } 214 } 215 free(buf); 216 } 217 218 int 219 dlog00_fd(void) 220 { 221 return(dlogfd(DLOG_ALL, O_RDWR|O_CREAT|O_APPEND)); 222 } 223 224 /* 225 * Bulk and Build environment control. These routines are only called 226 * unthreaded or when dsynth threads are idle. 227 */ 228 void 229 addbuildenv(const char *label, const char *data, int type) 230 { 231 buildenv_t *env; 232 233 env = calloc(1, sizeof(*env)); 234 env->a1 = strdup(label); 235 env->a2 = strdup(data); 236 env->label = env->a1; 237 env->data = env->a2; 238 env->type = type; 239 *BuildEnvTail = env; 240 BuildEnvTail = &env->next; 241 } 242 243 void 244 delbuildenv(const char *label) 245 { 246 buildenv_t **envp; 247 buildenv_t *env; 248 249 envp = &BuildEnv; 250 while ((env = *envp) != NULL) { 251 if (strcmp(env->label, label) == 0) { 252 *envp = env->next; 253 if (env->a1) 254 free(env->a1); 255 if (env->a2) 256 free(env->a2); 257 free(env); 258 } else { 259 envp = &env->next; 260 } 261 } 262 BuildEnvTail = envp; 263 } 264 265 void 266 freestrp(char **strp) 267 { 268 if (*strp) { 269 free(*strp); 270 *strp = NULL; 271 } 272 } 273 274 void 275 dupstrp(char **strp) 276 { 277 if (*strp) 278 *strp = strdup(*strp); 279 } 280 281 int 282 ipcreadmsg(int fd, wmsg_t *msg) 283 { 284 size_t res; 285 ssize_t r; 286 char *ptr; 287 288 res = sizeof(*msg); 289 ptr = (char *)(void *)msg; 290 while (res) { 291 r = read(fd, ptr, res); 292 if (r <= 0) { 293 if (errno == EINTR) 294 continue; 295 return -1; 296 } 297 res -= (size_t)r; 298 ptr += r; 299 } 300 return 0; 301 } 302 303 int 304 ipcwritemsg(int fd, wmsg_t *msg) 305 { 306 size_t res; 307 ssize_t r; 308 char *ptr; 309 310 res = sizeof(*msg); 311 ptr = (char *)(void *)msg; 312 while (res) { 313 r = write(fd, ptr, res); 314 if (r < 0) { 315 if (errno == EINTR) 316 continue; 317 return -1; 318 } 319 res -= (size_t)r; 320 ptr += r; 321 } 322 return 0; 323 } 324 325 int 326 askyn(const char *ctl, ...) 327 { 328 va_list va; 329 char buf[256]; 330 int res = 0; 331 332 if (YesOpt) 333 return 1; 334 335 for (;;) { 336 va_start(va, ctl); 337 vprintf(ctl, va); 338 va_end(va); 339 fflush(stdout); 340 if (fgets(buf, sizeof(buf), stdin) == NULL) 341 break; 342 if (buf[0] == 'y' || buf[0] == 'Y') { 343 res = 1; 344 break; 345 } 346 if (buf[0] == 'n' || buf[0] == 'N') { 347 res = 0; 348 break; 349 } 350 printf("Please type y/n\n"); 351 } 352 return res; 353 } 354 355 /* 356 * Get swap% used 0.0-1.0. 357 * 358 * NOTE: This routine is intended to return quickly. 359 * 360 * NOTE: swap_cache (caching for hard drives) is persistent and should 361 * not be counted for our purposes. 362 */ 363 double 364 getswappct(int *noswapp) 365 { 366 long swap_size = 0; 367 long swap_anon = 0; 368 long swap_cache __unused = 0; 369 size_t len; 370 double dswap; 371 372 len = sizeof(swap_size); 373 sysctlbyname("vm.swap_size", &swap_size, &len, NULL, 0); 374 len = sizeof(swap_size); 375 sysctlbyname("vm.swap_anon_use", &swap_anon, &len, NULL, 0); 376 len = sizeof(swap_size); 377 sysctlbyname("vm.swap_cache_use", &swap_cache, &len, NULL, 0); 378 if (swap_size) { 379 dswap = (double)(swap_anon /*+swap_cache*/) / (double)swap_size; 380 *noswapp = 0; 381 } else { 382 dswap = 0.0; 383 *noswapp = 1; 384 } 385 return dswap; 386 } 387 388 /* 389 * dexec_open()/fgets/dexec_close() 390 * 391 * Similar to popen() but directly exec()s the argument list (cav[0] must 392 * be an absolute path). 393 * 394 * If xenv is non-NULL its an array of local buildenv_t's to be used. 395 * The array is terminated with a NULL xenv->label. 396 * 397 * If with_env is non-zero the configured environment is included. 398 * 399 * If with_mvars is non-zero the make environment is passed as VAR=DATA 400 * elements on the command line. 401 */ 402 FILE * 403 dexec_open(const char **cav, int cac, pid_t *pidp, buildenv_t *xenv, 404 int with_env, int with_mvars) 405 { 406 buildenv_t *benv; 407 const char **cenv; 408 char *allocary[MAXCAC*2]; 409 int env_basei; 410 int envi; 411 int alloci; 412 int nullfd; 413 int fds[2]; 414 pid_t pid; 415 FILE *fp; 416 417 env_basei = 0; 418 while (environ[env_basei]) 419 ++env_basei; 420 cenv = calloc(env_basei + MAXCAC, sizeof(char *)); 421 env_basei = 0; 422 for (envi = 0; envi < env_basei; ++envi) 423 cenv[envi] = environ[envi]; 424 425 alloci = 0; 426 for (benv = BuildEnv; benv; benv = benv->next) { 427 if (with_mvars && 428 (benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 429 asprintf(&allocary[alloci], "%s=%s", 430 benv->label, benv->data); 431 cav[cac++] = allocary[alloci]; 432 ++alloci; 433 } 434 if (with_env && 435 (benv->type & BENV_PKGLIST) && 436 (benv->type & BENV_CMDMASK) == BENV_ENVIRONMENT) { 437 asprintf(&allocary[alloci], "%s=%s", 438 benv->label, benv->data); 439 cenv[envi++] = allocary[alloci]; 440 ++alloci; 441 } 442 ddassert(cac < MAXCAC && envi - env_basei < MAXCAC); 443 } 444 445 /* 446 * Extra environment specific to this particular dexec 447 */ 448 while (xenv && xenv->label) { 449 asprintf(&allocary[alloci], "%s=%s", 450 xenv->label, xenv->data); 451 cenv[envi++] = allocary[alloci]; 452 ++alloci; 453 ++xenv; 454 } 455 456 cav[cac] = NULL; 457 cenv[envi] = NULL; 458 459 if (pipe(fds) < 0) 460 dfatal_errno("pipe"); 461 nullfd = open("/dev/null", O_RDWR); 462 if (nullfd < 0) 463 dfatal_errno("open(\"/dev/null\")"); 464 465 /* 466 * We have to be very careful using vfork(), do only the bare 467 * minimum necessary in the child to set it up and exec it. 468 */ 469 pid = vfork(); 470 if (pid == 0) { 471 #if 0 472 int i; 473 printf("%s", cav[0]); 474 for (i = 0; cav[i]; ++i) 475 printf(" %s", cav[i]); 476 printf("\n"); 477 printf("ENV: "); 478 for (i = 0; cenv[i]; ++i) 479 printf(" %s", cenv[i]); 480 #endif 481 482 if (fds[1] != 1) { 483 dup2(fds[1], 1); 484 close(fds[1]); 485 } 486 close(fds[0]); /* safety */ 487 dup2(nullfd, 0); /* no questions! */ 488 closefrom(3); /* be nice */ 489 490 execve(cav[0], (void *)cav, (void *)cenv); 491 write(2, "EXEC FAILURE\n", 13); 492 _exit(1); 493 } 494 close(nullfd); 495 close(fds[1]); 496 if (pid < 0) { 497 close(fds[0]); 498 dfatal_errno("vfork failed"); 499 } 500 fp = fdopen(fds[0], "r"); 501 *pidp = pid; 502 503 while (--alloci >= 0) 504 free(allocary[alloci]); 505 free(cenv); 506 507 return fp; 508 } 509 510 int 511 dexec_close(FILE *fp, pid_t pid) 512 { 513 pid_t rpid; 514 int status; 515 516 fclose(fp); 517 while ((rpid = waitpid(pid, &status, 0)) != pid) { 518 if (rpid < 0) { 519 if (errno == EINTR) 520 continue; 521 return 1; 522 } 523 } 524 return (WEXITSTATUS(status)); 525 } 526 527 const char * 528 getphasestr(worker_phase_t phaseid) 529 { 530 const char *phase; 531 532 switch(phaseid) { 533 case PHASE_PENDING: 534 phase = "pending"; 535 break; 536 case PHASE_INSTALL_PKGS: 537 phase = "install-pkgs"; 538 break; 539 case PHASE_CHECK_SANITY: 540 phase = "check-sanity"; 541 break; 542 case PHASE_PKG_DEPENDS: 543 phase = "pkg-depends"; 544 break; 545 case PHASE_FETCH_DEPENDS: 546 phase = "fetch-depends"; 547 break; 548 case PHASE_FETCH: 549 phase = "fetch"; 550 break; 551 case PHASE_CHECKSUM: 552 phase = "checksum"; 553 break; 554 case PHASE_EXTRACT_DEPENDS: 555 phase = "extract-depends"; 556 break; 557 case PHASE_EXTRACT: 558 phase = "extract"; 559 break; 560 case PHASE_PATCH_DEPENDS: 561 phase = "patch-depends"; 562 break; 563 case PHASE_PATCH: 564 phase = "patch"; 565 break; 566 case PHASE_BUILD_DEPENDS: 567 phase = "build-depends"; 568 break; 569 case PHASE_LIB_DEPENDS: 570 phase = "lib-depends"; 571 break; 572 case PHASE_CONFIGURE: 573 phase = "configure"; 574 break; 575 case PHASE_BUILD: 576 phase = "build"; 577 break; 578 case PHASE_RUN_DEPENDS: 579 phase = "run-depends"; 580 break; 581 case PHASE_STAGE: 582 phase = "stage"; 583 break; 584 case PHASE_TEST: 585 phase = "test"; 586 break; 587 case PHASE_CHECK_PLIST: 588 phase = "check-plist"; 589 break; 590 case PHASE_PACKAGE: 591 phase = "package"; 592 break; 593 case PHASE_INSTALL_MTREE: 594 phase = "install-mtree"; 595 break; 596 case PHASE_INSTALL: 597 phase = "install"; 598 break; 599 case PHASE_DEINSTALL: 600 phase = "deinstall"; 601 break; 602 default: 603 phase = "Run-Unknown"; 604 break; 605 } 606 return phase; 607 } 608