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