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