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
ParseConfiguration(int isworker)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
DoConfigure(void)333 DoConfigure(void)
334 {
335 dfatal("Not Implemented");
336 }
337
338 static void
parseConfigFile(const char * path)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
parseProfile(const char * cpath,const char * profile)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 *
stripwhite(char * str)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
truefalse(const char * str)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 *
dokernsysctl(int m1,int m2)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
getElfInfo(const char * path)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 *
checkhook(const char * scriptname)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