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 int UseCCache; 41 int UseUsrSrc; 42 int UseTmpfs; 43 int NumCores = 1; 44 int MaxBulk = 8; 45 int MaxWorkers = 8; 46 int MaxJobs = 8; 47 int UseTmpfsWork = 1; 48 int UseTmpfsBase = 1; 49 int UseNCurses = -1; /* indicates default operation (enabled) */ 50 int LeveragePrebuilt = 0; 51 int WorkerProcFlags = 0; 52 int DeleteObsoletePkgs; 53 long PhysMem; 54 const char *OperatingSystemName = "Unknown"; /* e.g. "DragonFly" */ 55 const char *ArchitectureName = "unknown"; /* e.g. "x86_64" */ 56 const char *MachineName = "unknown"; /* e.g. "x86_64" */ 57 const char *VersionName = "unknown"; /* e.g. "DragonFly 5.7-SYNTH" */ 58 const char *VersionOnlyName = "unknown"; /* e.g. "5.7-SYNTH" */ 59 const char *VersionFromParamHeader = "unknown"; /* e.g. "500704" */ 60 const char *ReleaseName = "unknown"; /* e.g. "5.7" */ 61 const char *DPortsPath = "/usr/dports"; 62 const char *CCachePath = DISABLED_STR; 63 const char *PackagesPath = "/build/synth/live_packages"; 64 const char *RepositoryPath = "/build/synth/live_packages/All"; 65 const char *OptionsPath = "/build/synth/options"; 66 const char *DistFilesPath = "/build/synth/distfiles"; 67 const char *BuildBase = "/build/synth/build"; 68 const char *LogsPath = "/build/synth/logs"; 69 const char *SystemPath = "/"; 70 const char *UsePkgSufx = USE_PKG_SUFX; 71 char *StatsBase; 72 char *StatsFilePath; 73 char *StatsLockPath; 74 static const char *ProfileLabel = "[LiveSystem]"; /* with the brackets */ 75 const char *Profile = "LiveSystem"; /* without the brackets */ 76 77 /* 78 * Hooks are scripts in ConfigBase 79 */ 80 int UsingHooks; 81 const char *HookRunStart; 82 const char *HookRunEnd; 83 const char *HookPkgSuccess; 84 const char *HookPkgFailure; 85 const char *HookPkgIgnored; 86 const char *HookPkgSkipped; 87 88 const char *ConfigBase; /* The config base we found */ 89 const char *ConfigBase1 = "/etc/dsynth"; 90 const char *ConfigBase2 = "/usr/local/etc/dsynth"; 91 92 static void parseConfigFile(const char *path); 93 static void parseProfile(const char *cpath, const char *path); 94 static char *stripwhite(char *str); 95 static int truefalse(const char *str); 96 static char *dokernsysctl(int m1, int m2); 97 static void getElfInfo(const char *path); 98 static char *checkhook(const char *scriptname); 99 100 void 101 ParseConfiguration(int isworker) 102 { 103 struct stat st; 104 size_t len; 105 int reln; 106 char *synth_config; 107 char *buf; 108 109 /* 110 * Get the default OperatingSystemName, ArchitectureName, and 111 * ReleaseName. 112 */ 113 OperatingSystemName = dokernsysctl(CTL_KERN, KERN_OSTYPE); 114 ArchitectureName = dokernsysctl(CTL_HW, HW_MACHINE_ARCH); 115 MachineName = dokernsysctl(CTL_HW, HW_MACHINE); 116 ReleaseName = dokernsysctl(CTL_KERN, KERN_OSRELEASE); 117 118 /* 119 * Retrieve resource information from the system. Note that 120 * NumCores and PhysMem will also be used for dynamic load 121 * management. 122 */ 123 NumCores = 1; 124 len = sizeof(NumCores); 125 if (sysctlbyname("hw.ncpu", &NumCores, &len, NULL, 0) < 0) 126 dfatal_errno("Cannot get hw.ncpu"); 127 128 len = sizeof(PhysMem); 129 if (sysctlbyname("hw.physmem", &PhysMem, &len, NULL, 0) < 0) 130 dfatal_errno("Cannot get hw.physmem"); 131 132 /* 133 * Calculate nominal defaults. 134 */ 135 MaxBulk = NumCores; 136 MaxWorkers = MaxBulk / 2; 137 if (MaxWorkers > (int)((PhysMem + (ONEGB/2)) / ONEGB)) 138 MaxWorkers = (PhysMem + (ONEGB/2)) / ONEGB; 139 140 if (MaxBulk < 1) 141 MaxBulk = 1; 142 if (MaxWorkers < 1) 143 MaxWorkers = 1; 144 if (MaxJobs < 1) 145 MaxJobs = 1; 146 147 /* 148 * Configuration file must exist. Look for it in 149 * "/etc/dsynth" and "/usr/local/etc/dsynth". 150 */ 151 ConfigBase = ConfigBase1; 152 asprintf(&synth_config, "%s/dsynth.ini", ConfigBase1); 153 if (stat(synth_config, &st) < 0) { 154 ConfigBase = ConfigBase2; 155 asprintf(&synth_config, "%s/dsynth.ini", ConfigBase2); 156 } 157 158 if (stat(synth_config, &st) < 0) { 159 dfatal("Configuration file missing, " 160 "could not find %s/dsynth.ini or %s/dsynth.ini\n", 161 ConfigBase1, 162 ConfigBase2); 163 } 164 165 /* 166 * Check to see what hooks we have 167 */ 168 HookRunStart = checkhook("hook_run_start"); 169 HookRunEnd = checkhook("hook_run_end"); 170 HookPkgSuccess = checkhook("hook_pkg_success"); 171 HookPkgFailure = checkhook("hook_pkg_failure"); 172 HookPkgIgnored = checkhook("hook_pkg_ignored"); 173 HookPkgSkipped = checkhook("hook_pkg_skipped"); 174 175 /* 176 * Parse the configuration file(s). This may override some of 177 * the above defaults. 178 */ 179 parseConfigFile(synth_config); 180 parseProfile(synth_config, ProfileLabel); 181 182 /* 183 * Figure out whether CCache is configured. Also set UseUsrSrc 184 * if it exists under the system path. 185 * 186 * Not supported for the moment 187 */ 188 if (strcmp(CCachePath, "disabled") != 0) { 189 /* dfatal("Directory_ccache is not supported, please\n" 190 " set to 'disabled'\n"); */ 191 UseCCache = 1; 192 } 193 asprintf(&buf, "%s/usr/src/sys/Makefile", SystemPath); 194 if (stat(buf, &st) == 0) 195 UseUsrSrc = 1; 196 free(buf); 197 198 /* 199 * Default pkg dependency memory target. This is a heuristical 200 * calculation for how much memory we are willing to put towards 201 * pkg install dependencies. The builder count is reduced as needed. 202 * 203 * Reduce the target even further when CCache is enabled due to 204 * its added overhead (even though it doesn't use tmpfs). 205 * (NOT CURRENTLY IMPLEMENTED, LEAVE THE SAME) 206 */ 207 if (PkgDepMemoryTarget == 0) { 208 if (UseCCache) 209 PkgDepMemoryTarget = PhysMem / 3; 210 else 211 PkgDepMemoryTarget = PhysMem / 3; 212 } 213 214 /* 215 * If this is a dsynth WORKER exec it handles a single slot, 216 * just set MaxWorkers to 1. 217 */ 218 if (isworker) 219 MaxWorkers = 1; 220 221 /* 222 * Final check 223 */ 224 if (stat(DPortsPath, &st) < 0) 225 dfatal("Directory missing: %s", DPortsPath); 226 if (stat(PackagesPath, &st) < 0) 227 dfatal("Directory missing: %s", PackagesPath); 228 if (stat(OptionsPath, &st) < 0) 229 dfatal("Directory missing: %s", OptionsPath); 230 if (stat(DistFilesPath, &st) < 0) 231 dfatal("Directory missing: %s", DistFilesPath); 232 if (stat(BuildBase, &st) < 0) 233 dfatal("Directory missing: %s", BuildBase); 234 if (stat(LogsPath, &st) < 0) 235 dfatal("Directory missing: %s", LogsPath); 236 if (stat(SystemPath, &st) < 0) 237 dfatal("Directory missing: %s", SystemPath); 238 if (UseCCache && stat(CCachePath, &st) < 0) 239 dfatal("Directory missing: %s", CCachePath); 240 241 /* 242 * Now use the SystemPath to retrieve file information from /bin/sh, 243 * and use this to set OperatingSystemName, ArchitectureName, 244 * MachineName, and ReleaseName. 245 * 246 * Since this method is used to build for specific releases, require 247 * that it succeed. 248 */ 249 asprintf(&buf, "%s/bin/sh", SystemPath); 250 getElfInfo(buf); 251 free(buf); 252 253 /* 254 * Calculate VersionName from OperatingSystemName and ReleaseName. 255 */ 256 if (strchr(ReleaseName, '-')) { 257 reln = strchr(ReleaseName, '-') - ReleaseName; 258 asprintf(&buf, "%s %*.*s-SYNTH", 259 OperatingSystemName, 260 reln, reln, ReleaseName); 261 VersionName = buf; 262 asprintf(&buf, "%*.*s-SYNTH", reln, reln, ReleaseName); 263 VersionOnlyName = buf; 264 } else { 265 asprintf(&buf, "%s %s-SYNTH", 266 OperatingSystemName, 267 ReleaseName); 268 asprintf(&buf, "%s-SYNTH", ReleaseName); 269 VersionOnlyName = buf; 270 } 271 272 /* 273 * Get __DragonFly_version from the system header via SystemPath 274 */ 275 { 276 char *ptr; 277 FILE *fp; 278 279 asprintf(&buf, "%s/usr/include/sys/param.h", SystemPath); 280 fp = fopen(buf, "r"); 281 if (fp == NULL) 282 dpanic_errno("Cannot open %s", buf); 283 while ((ptr = fgetln(fp, &len)) != NULL) { 284 if (len == 0 || ptr[len-1] != '\n') 285 continue; 286 ptr[len-1] = 0; 287 if (strncmp(ptr, "#define __DragonFly_version", 27)) 288 continue; 289 ptr += 27; 290 ptr = strtok(ptr, " \t\r\n"); 291 VersionFromParamHeader = strdup(ptr); 292 break; 293 } 294 fclose(fp); 295 } 296 297 /* 298 * If RepositoryPath is under PackagesPath, make sure it 299 * is created. 300 */ 301 if (strncmp(RepositoryPath, PackagesPath, strlen(PackagesPath)) == 0) { 302 if (stat(RepositoryPath, &st) < 0) { 303 if (mkdir(RepositoryPath, 0755) < 0) 304 dfatal_errno("Cannot mkdir '%s'", 305 RepositoryPath); 306 } 307 } 308 309 if (stat(RepositoryPath, &st) < 0) 310 dfatal("Directory missing: %s", RepositoryPath); 311 312 /* 313 * StatsBase, StatsFilePath, StatsLockPath 314 */ 315 asprintf(&StatsBase, "%s/stats", LogsPath); 316 asprintf(&StatsFilePath, "%s/monitor.dat", StatsBase); 317 asprintf(&StatsLockPath, "%s/monitor.lk", StatsBase); 318 } 319 320 void 321 DoConfigure(void) 322 { 323 dfatal("Not Implemented"); 324 } 325 326 static void 327 parseConfigFile(const char *path) 328 { 329 char buf[1024]; 330 char copy[1024]; 331 FILE *fp; 332 char *l1; 333 char *l2; 334 size_t len; 335 int mode = -1; 336 int lineno = 0; 337 338 fp = fopen(path, "r"); 339 if (fp == NULL) { 340 ddprintf(0, "Warning: Config file %s does not exist\n", path); 341 return; 342 } 343 if (DebugOpt >= 2) 344 ddprintf(0, "ParseConfig %s\n", path); 345 346 if (ProfileOverrideOpt) { 347 Profile = strdup(ProfileOverrideOpt); 348 asprintf(&l2, "[%s]", Profile); 349 ProfileLabel = l2; 350 } 351 352 while (fgets(buf, sizeof(buf), fp) != NULL) { 353 ++lineno; 354 len = strlen(buf); 355 if (len == 0 || buf[len-1] != '\n') 356 continue; 357 buf[--len] = 0; 358 359 /* 360 * Remove any trailing whitespace, ignore empty lines. 361 */ 362 while (len > 0 && isspace(buf[len-1])) 363 --len; 364 if (len == 0) 365 continue; 366 buf[len] = 0; 367 368 /* 369 * ignore comments 370 */ 371 if (buf[0] == ';' || buf[0] == '#') 372 continue; 373 if (buf[0] == '[') { 374 if (strcmp(buf, "[Global Configuration]") == 0) 375 mode = 0; /* parse global config */ 376 else if (strcmp(buf, ProfileLabel) == 0) 377 mode = 1; /* use profile */ 378 else 379 mode = -1; /* ignore profile */ 380 continue; 381 } 382 383 bcopy(buf, copy, len + 1); 384 385 l1 = strtok(copy, "="); 386 if (l1 == NULL) { 387 dfatal("Syntax error in config line %d: %s\n", 388 lineno, buf); 389 } 390 l2 = strtok(NULL, " \t\n"); 391 if (l2 == NULL) { 392 dfatal("Syntax error in config line %d: %s\n", 393 lineno, buf); 394 } 395 l1 = stripwhite(l1); 396 l2 = stripwhite(l2); 397 398 switch(mode) { 399 case 0: 400 /* 401 * Global Configuration 402 */ 403 if (strcmp(l1, "profile_selected") == 0) { 404 if (ProfileOverrideOpt == NULL) { 405 Profile = strdup(l2); 406 asprintf(&l2, "[%s]", l2); 407 ProfileLabel = l2; 408 } 409 } else { 410 dfatal("Unknown directive in config " 411 "line %d: %s\n", lineno, buf); 412 } 413 break; 414 case 1: 415 /* 416 * Selected Profile 417 */ 418 l2 = strdup(l2); 419 if (strcmp(l1, "Operating_system") == 0) { 420 OperatingSystemName = l2; 421 } else if (strcmp(l1, "Directory_packages") == 0) { 422 PackagesPath = l2; 423 } else if (strcmp(l1, "Directory_repository") == 0) { 424 RepositoryPath = l2; 425 } else if (strcmp(l1, "Directory_portsdir") == 0) { 426 DPortsPath = l2; 427 } else if (strcmp(l1, "Directory_options") == 0) { 428 OptionsPath = l2; 429 } else if (strcmp(l1, "Directory_distfiles") == 0) { 430 DistFilesPath = l2; 431 } else if (strcmp(l1, "Directory_buildbase") == 0) { 432 BuildBase = l2; 433 } else if (strcmp(l1, "Directory_logs") == 0) { 434 LogsPath = l2; 435 } else if (strcmp(l1, "Directory_ccache") == 0) { 436 CCachePath = l2; 437 } else if (strcmp(l1, "Directory_system") == 0) { 438 SystemPath = l2; 439 } else if (strcmp(l1, "Package_suffix") == 0) { 440 UsePkgSufx = l2; 441 dassert(strcmp(l2, ".tgz") == 0 || 442 strcmp(l2, ".tar") == 0 || 443 strcmp(l2, ".txz") == 0 || 444 strcmp(l2, ".tbz") == 0, 445 "Config: Unknown Package_suffix," 446 "specify .tgz .tar .txz or .tbz"); 447 } else if (strcmp(l1, "Number_of_builders") == 0) { 448 MaxWorkers = strtol(l2, NULL, 0); 449 if (MaxWorkers == 0) 450 MaxWorkers = NumCores / 2 + 1; 451 else 452 if (MaxWorkers < 0 || MaxWorkers > MAXWORKERS) { 453 dfatal("Config: Number_of_builders " 454 "must range %d..%d", 455 1, MAXWORKERS); 456 } 457 free(l2); 458 } else if (strcmp(l1, "Max_jobs_per_builder") == 0) { 459 MaxJobs = strtol(l2, NULL, 0); 460 if (MaxJobs == 0) { 461 MaxJobs = NumCores; 462 } else 463 if (MaxJobs < 0 || MaxJobs > MAXJOBS) { 464 dfatal("Config: Max_jobs_per_builder " 465 "must range %d..%d", 466 1, MAXJOBS); 467 } 468 free(l2); 469 } else if (strcmp(l1, "Tmpfs_workdir") == 0) { 470 UseTmpfsWork = truefalse(l2); 471 dassert(UseTmpfsWork == 1, 472 "Config: Tmpfs_workdir must be " 473 "set to true, 'false' not supported"); 474 } else if (strcmp(l1, "Tmpfs_localbase") == 0) { 475 UseTmpfsBase = truefalse(l2); 476 dassert(UseTmpfsBase == 1, 477 "Config: Tmpfs_localbase must be " 478 "set to true, 'false' not supported"); 479 } else if (strcmp(l1, "Display_with_ncurses") == 0) { 480 if (UseNCurses == -1) 481 UseNCurses = truefalse(l2); 482 } else if (strcmp(l1, "leverage_prebuilt") == 0) { 483 LeveragePrebuilt = truefalse(l2); 484 dassert(LeveragePrebuilt == 0, 485 "Config: leverage_prebuilt not " 486 "supported and must be set to false"); 487 } else { 488 dfatal("Unknown directive in profile section " 489 "line %d: %s\n", lineno, buf); 490 } 491 break; 492 default: 493 /* 494 * Ignore unselected profile 495 */ 496 break; 497 } 498 } 499 fclose(fp); 500 } 501 502 /* 503 * NOTE: profile has brackets, e.g. "[LiveSystem]". 504 */ 505 static void 506 parseProfile(const char *cpath, const char *profile) 507 { 508 char buf[1024]; 509 char copy[1024]; 510 char *ppath; 511 FILE *fp; 512 char *l1; 513 char *l2; 514 int len; 515 int plen; 516 int lineno = 0; 517 518 len = strlen(cpath); 519 while (len && cpath[len-1] != '/') 520 --len; 521 if (len == 0) 522 ++len; 523 plen = strlen(profile); 524 ddassert(plen > 2 && profile[0] == '[' && profile[plen-1] == ']'); 525 526 asprintf(&ppath, "%*.*s%*.*s-make.conf", 527 len, len, cpath, plen - 2, plen - 2, profile + 1); 528 fp = fopen(ppath, "r"); 529 if (fp == NULL) { 530 ddprintf(0, "Warning: Profile %s does not exist\n", ppath); 531 return; 532 } 533 if (DebugOpt >= 2) 534 ddprintf(0, "ParseProfile %s\n", ppath); 535 free(ppath); 536 537 while (fgets(buf, sizeof(buf), fp) != NULL) { 538 ++lineno; 539 len = strlen(buf); 540 if (len == 0 || buf[len-1] != '\n') 541 continue; 542 buf[--len] = 0; 543 544 /* 545 * Remove any trailing whitespace, ignore empty lines. 546 */ 547 while (len > 0 && isspace(buf[len-1])) 548 --len; 549 buf[len] = 0; 550 stripwhite(buf); 551 552 /* 553 * Allow empty lines, ignore comments. 554 */ 555 len = strlen(buf); 556 if (len == 0) 557 continue; 558 if (buf[0] == ';' || buf[0] == '#') 559 continue; 560 561 /* 562 * Require env variable name 563 */ 564 bcopy(buf, copy, len + 1); 565 l1 = strtok(copy, "="); 566 if (l1 == NULL) { 567 dfatal("Syntax error in profile line %d: %s\n", 568 lineno, buf); 569 } 570 571 /* 572 * Allow empty assignment 573 */ 574 l2 = strtok(NULL, " \t\n"); 575 if (l2 == NULL) 576 l2 = l1 + strlen(l1); 577 578 l1 = stripwhite(l1); 579 l2 = stripwhite(l2); 580 581 /* 582 * Add to builder environment 583 */ 584 addbuildenv(l1, l2, BENV_MAKECONF); 585 if (DebugOpt >= 2) 586 ddprintf(4, "%s=%s\n", l1, l2); 587 } 588 fclose(fp); 589 if (DebugOpt >= 2) 590 ddprintf(0, "ParseProfile finished\n"); 591 } 592 593 static char * 594 stripwhite(char *str) 595 { 596 size_t len; 597 598 len = strlen(str); 599 while (len > 0 && isspace(str[len-1])) 600 --len; 601 str[len] =0; 602 603 while (*str && isspace(*str)) 604 ++str; 605 return str; 606 } 607 608 static int 609 truefalse(const char *str) 610 { 611 if (strcmp(str, "0") == 0) 612 return 0; 613 if (strcmp(str, "1") == 0) 614 return 1; 615 if (strcasecmp(str, "false") == 0) 616 return 0; 617 if (strcasecmp(str, "true") == 0) 618 return 1; 619 dfatal("syntax error for boolean '%s': " 620 "must be '0', '1', 'false', or 'true'", str); 621 return 0; 622 } 623 624 static char * 625 dokernsysctl(int m1, int m2) 626 { 627 int mib[] = { m1, m2 }; 628 char buf[1024]; 629 size_t len; 630 631 len = sizeof(buf) - 1; 632 if (sysctl(mib, 2, buf, &len, NULL, 0) < 0) 633 dfatal_errno("sysctl for system/architecture"); 634 buf[len] = 0; 635 return(strdup(buf)); 636 } 637 638 struct NoteTag { 639 Elf_Note note; 640 char osname1[12]; 641 int version; /* e.g. 500702 -> 5.7 */ 642 int x1; 643 int x2; 644 int x3; 645 char osname2[12]; 646 int zero; 647 }; 648 649 static void 650 getElfInfo(const char *path) 651 { 652 struct NoteTag note; 653 char *cmd; 654 char *base; 655 FILE *fp; 656 size_t size; 657 size_t n; 658 int r; 659 uint32_t addr; 660 uint32_t v[4]; 661 662 asprintf(&cmd, "readelf -x .note.tag %s", path); 663 fp = popen(cmd, "r"); 664 dassert_errno(fp, "Cannot run: %s", cmd); 665 n = 0; 666 667 while (n != sizeof(note) && 668 (base = fgetln(fp, &size)) != NULL && size) { 669 base[--size] = 0; 670 if (strncmp(base, " 0x", 3) != 0) 671 continue; 672 r = sscanf(base, "%x %x %x %x %x", 673 &addr, &v[0], &v[1], &v[2], &v[3]); 674 v[0] = ntohl(v[0]); 675 v[1] = ntohl(v[1]); 676 v[2] = ntohl(v[2]); 677 v[3] = ntohl(v[3]); 678 if (r < 2) 679 continue; 680 r = (r - 1) * sizeof(v[0]); 681 if (n + r > sizeof(note)) 682 r = sizeof(note) - n; 683 bcopy((char *)v, (char *)¬e + n, r); 684 n += r; 685 } 686 pclose(fp); 687 688 if (n != sizeof(note)) 689 dfatal("Unable to parse output from: %s", cmd); 690 if (strncmp(OperatingSystemName, note.osname1, sizeof(note.osname1))) { 691 dfatal("%s ELF, mismatch OS name %.*s vs %s", 692 path, (int)sizeof(note.osname1), 693 note.osname1, OperatingSystemName); 694 } 695 free(cmd); 696 if (note.version) { 697 asprintf(&cmd, "%d.%d", 698 note.version / 100000, 699 (note.version % 100000) / 100); 700 } else if (note.zero) { 701 asprintf(&cmd, "%d.%d", 702 note.zero / 100000, 703 (note.zero % 100000) / 100); 704 } else { 705 dfatal("%s ELF, cannot extract version info", path); 706 } 707 ReleaseName = cmd; 708 } 709 710 static char * 711 checkhook(const char *scriptname) 712 { 713 struct stat st; 714 char *path; 715 716 asprintf(&path, "%s/%s", ConfigBase, scriptname); 717 if (stat(path, &st) < 0 || (st.st_mode & 0111) == 0) { 718 free(path); 719 return NULL; 720 } 721 UsingHooks = 1; 722 723 return path; 724 } 725