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