xref: /dragonfly/usr.bin/dsynth/config.c (revision a361ab312536a661d74caf5630c7ae20bcd8e3e4)
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 *)&note + 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