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