From 398b29f07f7d1fa6a045b3ccf242a55cb61207c0 Mon Sep 17 00:00:00 2001 From: Devin Teske Date: Thu, 8 Oct 2026 09:26:43 -0700 Subject: [PATCH] sysconf: add a poudriere target poudriere.conf is handed to sysrc(8), including the jail, tree, and set overlays poudriere(8) consults. make, src, and src-env are sub-targets over the matching make(1) files under poudriere.d. -l and -L list one path per line; -L includes overlays that do not exist yet. A bare -- ends the sub-target so the next word is a variable. Bump sysconf(8) to 2.2. Reviewed by: fuz, kfv, bcr Differential Revision: https://reviews.freebsd.org/D59943 --- usr.sbin/sysconf/Makefile | 2 + usr.sbin/sysconf/sysconf-poudriere.8 | 364 +++++++++++++ usr.sbin/sysconf/sysconf-targets.8 | 4 +- usr.sbin/sysconf/sysconf-targets.c | 70 ++- usr.sbin/sysconf/sysconf.8 | 57 ++- usr.sbin/sysconf/sysconf.c | 18 +- usr.sbin/sysconf/sysconf_poudriere.c | 677 +++++++++++++++++++++++++ usr.sbin/sysconf/sysconf_priv.h | 13 +- usr.sbin/sysconf/tests/sysconf_test.sh | 201 ++++++++ 9 files changed, 1378 insertions(+), 28 deletions(-) create mode 100644 usr.sbin/sysconf/sysconf-poudriere.8 create mode 100644 usr.sbin/sysconf/sysconf_poudriere.c diff --git a/usr.sbin/sysconf/Makefile b/usr.sbin/sysconf/Makefile index 2c96997ff787..cbe5440d6da7 100644 --- a/usr.sbin/sysconf/Makefile +++ b/usr.sbin/sysconf/Makefile @@ -6,6 +6,7 @@ MAN= sysconf.8 \ sysconf-generic.8 \ sysconf-loader.8 \ sysconf-make.8 \ + sysconf-poudriere.8 \ sysconf-rc.8 \ sysconf-src.8 \ sysconf-sysctl.8 \ @@ -16,6 +17,7 @@ SRCS= sysconf.c \ sysconf_edit_assign.c \ sysconf_edit_make.c \ sysconf_edit_write.c \ + sysconf_poudriere.c \ sysconf_print.c \ sysconf_query_desc.c \ sysconf_query_sysctl.c \ diff --git a/usr.sbin/sysconf/sysconf-poudriere.8 b/usr.sbin/sysconf/sysconf-poudriere.8 new file mode 100644 index 000000000000..a5a61fea4421 --- /dev/null +++ b/usr.sbin/sysconf/sysconf-poudriere.8 @@ -0,0 +1,364 @@ +.\" Copyright (c) 2013-2026 Devin Teske +.\" Copyright (c) 2021-2026 Faraz Vahedi +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd September 23, 2026 +.Dt SYSCONF-POUDRIERE 8 +.Os +.Sh NAME +.Nm "sysconf poudriere" +.Nd configure poudriere.conf and poudriere.d overlay files +.Sh SYNOPSIS +.Nm sysconf +.Cm poudriere +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Ar sysrc_argument ... +.Nm sysconf +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl ceFinNqsvVx +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl j Ar jail | Fl R Ar dir +.Op Fl f Ar file +.Ar name Ns Op Ns Oo +|- Oc Ns = Ns Ar value +.Ar ... +.Nm sysconf +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl eFnNqvV +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl f Ar file +.Fl a +.Nm sysconf +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl E +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl f Ar file +.Fl l | L +.Sh DESCRIPTION +The +.Cm poudriere +target of +.Xr sysconf 8 +reaches the same files +.Xr poudriere 8 +sources. +With no sub-target, it is the +.Pa poudriere.conf +collection, handed to +.Xr sysrc 8 +as +.Fl f +files. +The sub-targets are +.Cm make , +.Cm src , +and +.Cm src-env . +Each is the matching +.Xr make 1 +collection under +.Pa poudriere.d , +handled by the same engine as +.Xr sysconf-make 8 . +.Pp +A unique prefix of the +.Cm poudriere +keyword is accepted +.Pq Ql p . +A unique prefix of a sub-target is accepted the same way +.Po +.Ql m +selects +.Cm make ; +.Ql s +and +.Ql sr +are ambiguous between +.Cm src +and +.Cm src-env +.Pc . +.Ss poudriere.conf +.Pa poudriere.conf +is +.Xr rc.conf 5 Ns -style +shell written by the administrator; +poudriere never writes it. +The default collection, sourced by every poudriere command, is in order: +.Pa POUDRIERE_ETC/poudriere.conf +if readable +.Pq default Pa /usr/local/etc/poudriere.conf , +then +.Pa POUDRIERED/poudriere.conf +if readable and not the same file as the first +.Pq default Pa POUDRIERE_ETC/poudriere.d/poudriere.conf . +At least one of those two files must exist. +Honor +.Ev POUDRIERE_ETC +and +.Ev POUDRIERED +when set. +.Pp +When a jail, ports tree, or set is given, the matching files are +appended, in the order +.Xr poudriere 8 +uses: +.Bd -unfilled -offset indent +.Pa POUDRIERED/-poudriere.conf +.Pa POUDRIERED/-poudriere.conf +.Pa POUDRIERED/-poudriere.conf +.Pa POUDRIERED/--poudriere.conf +.Pa POUDRIERED/--poudriere.conf +.Pa POUDRIERED/--poudriere.conf +.Pa POUDRIERED/---poudriere.conf +.Ed +.Pp +An explicit +.Xr sysrc 8 +.Fl f +replaces discovery. +.Fl l +and +.Fl L +are handled here and printed one path per line. +For this +.Pa poudriere.conf +collection, +.Fl l +lists each readable file. +.Fl L +lists every location the given jail, tree, and set would consult, +including overlays that do not exist yet. +If no jail, tree, or set is given, the list is the two global +.Pa poudriere.conf +paths. +.Fl E +limits either list to files that exist. +.Fl l +and +.Fl L +take no variable names. +Other arguments except the +.Cm poudriere +keyword itself are handed to +.Xr sysrc 8 +verbatim. +A bare +.Ql -- +means the next word is a variable in this collection, even when that +word is +.Cm make , +.Cm src , +.Cm src-env , +or a unique prefix of one of those sub-targets. +.Ss make.conf, src.conf, and src-env.conf +These live only under +.Pa POUDRIERED +and use +.Xr make 1 +syntax, so they are not mixed into the +.Pa poudriere.conf +sysrc collection. +.Pp +.Cm poudriere make +consults, in the order shown by +.Xr poudriere 8 : +.Bd -unfilled -offset indent +.Pa POUDRIERED/make.conf +.Pa POUDRIERED/-make.conf +.Pa POUDRIERED/-make.conf +.Pa POUDRIERED/-make.conf +.Pa POUDRIERED/--make.conf +.Pa POUDRIERED/--make.conf +.Pa POUDRIERED/--make.conf +.Pa POUDRIERED/---make.conf +.Ed +.Pp +.Fl l +always prints the global path, even when that file does not exist: +.Pa POUDRIERED/make.conf +for +.Cm make , +.Pa POUDRIERED/src.conf +for +.Cm src , +and +.Pa POUDRIERED/src-env.conf +for +.Cm src-env . +An overlay is printed only when the file exists. +.Fl L +lists every location the given jail, tree, and set would consult, +including files that do not exist yet. +.Cm src +and +.Cm src-env +have no per-tree file, so +.Fl Fl tree +does not add a path to those lists. +.Fl E +limits either list to files that exist, so a missing global is omitted. +On a read, a missing global is skipped and an +overlay is consulted only when the file exists. +Hook-plugin +.Pa make.conf +files are not collected. +.Pp +.Cm poudriere src +follows the shorter list +.Xr poudriere 8 +uses when building jails +.Pq global, then set, then jail; no tree : +.Bd -unfilled -offset indent +.Pa POUDRIERED/src.conf +.Pa POUDRIERED/-src.conf +.Pa POUDRIERED/-src.conf +.Ed +.Pp +.Cm poudriere src-env +uses that same order, with +.Pa src-env.conf +in place of +.Pa src.conf : +.Bd -unfilled -offset indent +.Pa POUDRIERED/src-env.conf +.Pa POUDRIERED/-src-env.conf +.Pa POUDRIERED/-src-env.conf +.Ed +.Pp +An explicit +.Fl f +replaces discovery, as on other +.Nm sysconf +targets. +A name already defined is rewritten in the last file in the +collection that defines it, so the change is not placed where a later +overlay would mask it. +A name defined nowhere is appended to the global file, and that write +creates the global file when it is missing. +A bare +.Ar name +reads, and +.Ar name Ns = Ns Ar value +writes, including +.Ql += +and +.Ql -= . +Those rules are the +.Dq Assignment syntax +section of +.Xr sysconf 8 . +.Ss Context long options +.Bl -tag -width indent +.It Fl Fl jail Ar name +Poudriere jail name, as in +.Pa -make.conf +and the other jail files. +.It Fl Fl ports Ar name , Fl Fl tree Ar name +Poudriere ports tree. +.Fl Fl tree +is an alias of +.Fl Fl ports ; +when both are given, the last wins. +A tree applies to +.Pa poudriere.conf +and the +.Cm make +sub-target. +.Cm src +and +.Cm src-env +ignore it; poudriere does not read a per-tree src file. +.It Fl Fl set Ar name +Poudriere set name. +.El +.Pp +.Fl Fl jail Ns = Ns Ar name +(and the same attached form for the others) is accepted. +These long options are consumed here and never passed to +.Xr sysrc 8 +or mistaken for +.Nm sysconf +operands. +They are distinct from +.Fl j +on +.Xr sysconf 8 +and +.Xr sysrc 8 , +which name a running jail. +.Ss Out of scope +Poudriere maintains the following itself, or they are a different format: +.Pp +.Bl -tag -width indent -compact +.It Pa poudriere.d/jails/ , poudriere.d/ports/ +One file per property; the whole file is the value. +.It Pa poudriere.d/*-options/ +A +.Ev PORT_DBDIR +tree of per-port +.Ql OPTIONS +files, written by +.Ql poudriere options +.Pq Ql make config . +.It Pa poudriere.d/*-blacklist +Port origins, one per line +.Pq with optional Ql @flavor ; +.Ql # +comments. +Not +.Ql name=value . +.It Pa poudriere.d/hooks/ +Shell scripts, including plugin +.Pa make.conf +files under +.Pa hooks/plugins/ . +.El +.Sh ENVIRONMENT +.Bl -tag -width POUDRIERE_ETC +.It Ev POUDRIERE_ETC +Directory containing +.Pa poudriere.conf +.Pq default Pa /usr/local/etc . +.It Ev POUDRIERED +Overlay directory +.Pq default Pa POUDRIERE_ETC/poudriere.d . +.El +.Sh EXAMPLES +.Dl sysconf poudriere ZPOOL=zroot +.Dl sysconf poudriere --jail 131amd64 PARALLEL_JOBS=8 +.Dl sysconf p ZPOOL +.Dl sysconf p -- src +.Dl sysconf poudriere make --jail 131amd64 CFLAGS +.Dl sysconf p m --jail 131amd64 CFLAGS+=-O2 +.Dl sysconf poudriere src --set dev WITHOUT_LLVM_BINUTILS= +.Dl sysconf p src-env --jail 131amd64 SRC_ENV_VAR +.Sh SEE ALSO +.Xr poudriere 8 , +.Xr sysconf 8 , +.Xr sysconf-make 8 , +.Xr sysconf-rc 8 , +.Xr sysrc 8 +.Sh HISTORY +The +.Nm sysconf +utility first appeared in +.Fx 16.0 . +.Sh AUTHORS +.An Devin Teske Aq Mt dteske@FreeBSD.org +.An Faraz Vahedi Aq Mt kfv@FreeBSD.org diff --git a/usr.sbin/sysconf/sysconf-targets.8 b/usr.sbin/sysconf/sysconf-targets.8 index 0b611bc5e0a7..e87992852677 100644 --- a/usr.sbin/sysconf/sysconf-targets.8 +++ b/usr.sbin/sysconf/sysconf-targets.8 @@ -3,7 +3,7 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd August 2, 2026 +.Dd September 22, 2026 .Dt SYSCONF-TARGETS 8 .Os .Sh NAME @@ -24,6 +24,7 @@ Per-target file lists and syntax quirks are in .Xr sysconf-make 8 , .Xr sysconf-src 8 , .Xr sysconf-rc 8 , +.Xr sysconf-poudriere 8 , and .Xr sysconf-generic 8 . Shared options remain in @@ -160,6 +161,7 @@ but may differ when the value depends on conditionals or mk defaults. .Xr sysconf-generic 8 , .Xr sysconf-loader 8 , .Xr sysconf-make 8 , +.Xr sysconf-poudriere 8 , .Xr sysconf-rc 8 , .Xr sysconf-src 8 , .Xr sysconf-sysctl 8 diff --git a/usr.sbin/sysconf/sysconf-targets.c b/usr.sbin/sysconf/sysconf-targets.c index 940502d4ad02..9031b6c06748 100644 --- a/usr.sbin/sysconf/sysconf-targets.c +++ b/usr.sbin/sysconf/sysconf-targets.c @@ -6,7 +6,8 @@ */ /* - * Target keyword selection: unique-prefix match, and the rc pass-through. + * Target keyword selection: unique-prefix match, the rc pass-through, + * and the poudriere target (sysrc collection or native make overlays). */ #include "sysconf_priv.h" @@ -20,6 +21,7 @@ static const char *const target_keywords[] = { "generic", "loader", "make", + "poudriere", "rc", "src", "sysctl", @@ -82,31 +84,47 @@ resolve_target_keyword(const char *token) } /* - * Locate the target keyword in `argv' without consuming or validating - * anything: the first argument that is neither an option cluster nor the - * argument of one (per OPTSTRING; an unknown option letter is assumed to - * take no argument). A `--' ends option scanning as usual. Returns the - * argv index of the target, or -1 when there is none. Used before any - * getopt(3) processing so that the `rc' pass-through target (see - * exec_sysrc() below) can be detected while the command line is still - * pristine. + * Locate an operand in `argv' without consuming or validating anything: + * the first argument that is neither an option cluster nor the argument + * of one (per OPTSTRING; an unknown option letter is assumed to take no + * argument). Used before any getopt(3) processing so that pass-through + * targets (`rc', `poudriere'; see exec_sysrc() / poudriere_dispatch()) + * can be detected while the command line is still pristine. * * Attached arguments (`-fPATH', `-R/altroot') are self-contained: the rest * of the cluster is the option's argument, so the following argv element is * not consumed. Only a trailing option letter that takes an argument and * has nothing after it in the cluster (`-f PATH') skips the next element. + * + * Poudriere context long options (`--jail'/`--ports'/`--tree'/`--set') + * take an argument and must not be mistaken for a keyword. + * + * `past_dd' is for the top-level target: `--' ends options and the next + * word is the keyword (`sysconf -- loader'). The poudriere nested keyword + * passes zero: `--' ends the stem, and the following word is a name + * (`sysconf p -- src' reads the variable src from poudriere.conf). */ -static int -find_target(int argc, char *argv[]) +int +find_operand(int argc, char *argv[], int from, int past_dd) { int n; const char *o; const char *p; - for (n = 1; n < argc; n++) { - if (strcmp(argv[n], "--") == 0) + for (n = from; n < argc; n++) { + if (strcmp(argv[n], "--") == 0) { + if (!past_dd) + return (-1); return (n + 1 < argc ? n + 1 : -1); + } if (argv[n][0] == '-' && argv[n][1] != '\0') { + if (argv[n][1] == '-') { + if (poudriere_longopt_p(argv[n]) && + strchr(argv[n], '=') == NULL && + n + 1 < argc) + n++; + continue; + } for (p = argv[n] + 1; *p != '\0'; p++) { o = strchr(OPTSTRING, *p); if (o != NULL && o[1] == ':') { @@ -129,6 +147,13 @@ find_target(int argc, char *argv[]) return (-1); } +static int +find_target(int argc, char *argv[]) +{ + + return (find_operand(argc, argv, 1, 1)); +} + /* * The `rc' pass-through: rc.conf(5) is sysrc(8)'s domain, and its feature * set is a superset of ours, so no option or argument is validated, @@ -159,21 +184,28 @@ exec_sysrc(int argc, char *argv[], int skip) /* * Resolve the target keyword. The `rc' target execs sysrc(8) and does - * not return, so `--help' and `--version' are not intercepted on that - * path. Unique-prefix shorthands are expanded (`r', `m'). Returns the - * canonical keyword, or NULL when the token is unknown or absent. + * not return. The `poudriere' target either execs sysrc(8) or compacts + * argv for a native make/src/src-env overlay (`*argcp' is updated) and + * returns NULL so main does not treat it as a libbsdconf keyword. + * Unique-prefix shorthands are expanded (`r', `m', `p'). Returns the + * canonical keyword, or NULL when the token is unknown, absent, or + * handled as a poudriere overlay. */ const char * -target_resolve(int argc, char *argv[]) +target_resolve(int *argcp, char *argv[]) { const char *resolved; int n; - n = find_target(argc, argv); + n = find_target(*argcp, argv); if (n < 0) return (NULL); resolved = resolve_target_keyword(argv[n]); if (resolved != NULL && strcmp(resolved, "rc") == 0) - exec_sysrc(argc, argv, n); /* never returns */ + exec_sysrc(*argcp, argv, n); /* never returns */ + if (resolved != NULL && strcmp(resolved, "poudriere") == 0) { + *argcp = poudriere_dispatch(*argcp, argv, n); + return (NULL); + } return (resolved); } diff --git a/usr.sbin/sysconf/sysconf.8 b/usr.sbin/sysconf/sysconf.8 index b6147bd39088..6d8428b5f72a 100644 --- a/usr.sbin/sysconf/sysconf.8 +++ b/usr.sbin/sysconf/sysconf.8 @@ -3,7 +3,7 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd September 22, 2026 +.Dd September 23, 2026 .Dt SYSCONF 8 .Os .Sh NAME @@ -38,6 +38,45 @@ .Nm .Cm rc .Op Ar sysrc_argument ... +.Nm +.Cm poudriere +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Ar sysrc_argument ... +.Nm +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl ceFinNqsvVx +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl j Ar jail | Fl R Ar dir +.Op Fl f Ar file +.Ar name Ns Op Ns Oo +|- Oc Ns = Ns Ar value +.Ar ... +.Nm +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl eFnNqvV +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl f Ar file +.Fl a +.Nm +.Cm poudriere +.Bro Cm make Ns | Ns Cm src Ns | Ns Cm src-env Brc +.Op Fl E +.Op Fl Fl jail Ar name +.Op Fl Fl ports Ar name +.Op Fl Fl tree Ar name +.Op Fl Fl set Ar name +.Op Fl f Ar file +.Fl l | L .Sh DESCRIPTION The .Nm @@ -259,6 +298,20 @@ Pass-through to .Xr sysrc 8 . See .Xr sysconf-rc 8 . +.It Cm poudriere +.Pa poudriere.conf +collection via +.Xr sysrc 8 ; +.Cm poudriere make , +.Cm poudriere src , +and +.Cm poudriere src-env +for the +.Xr make 1 +overlays under +.Pa poudriere.d . +See +.Xr sysconf-poudriere 8 . .It Cm generic Foreign or unknown format; requires @@ -682,6 +735,7 @@ Further examples for each target appear in .Xr sysconf-make 8 , .Xr sysconf-src 8 , .Xr sysconf-rc 8 , +.Xr sysconf-poudriere 8 , and .Xr sysconf-generic 8 . .Sh SEE ALSO @@ -695,6 +749,7 @@ and .Xr sysconf-generic 8 , .Xr sysconf-loader 8 , .Xr sysconf-make 8 , +.Xr sysconf-poudriere 8 , .Xr sysconf-rc 8 , .Xr sysconf-src 8 , .Xr sysconf-sysctl 8 , diff --git a/usr.sbin/sysconf/sysconf.c b/usr.sbin/sysconf/sysconf.c index 8e23dc7a8278..41091aa2ed58 100644 --- a/usr.sbin/sysconf/sysconf.c +++ b/usr.sbin/sysconf/sysconf.c @@ -171,10 +171,10 @@ main(int argc, char *argv[]) pgm = getprogname(); /* - * Target selection, including the rc pass-through, runs while - * argv is still pristine (see target_resolve()). + * Target selection, including the rc and poudriere pass-throughs, + * runs while argv is still pristine (see target_resolve()). */ - resolved = target_resolve(argc, argv); + resolved = target_resolve(&argc, argv); /* * Honor `--help' and `--version' wherever they appear (getopt(3) @@ -206,8 +206,11 @@ main(int argc, char *argv[]) * sysctl(8) (e.g., `sysconf sysctl -L'). getopt(3) skips over its * first argument, so hand it the target keyword's slot as the * program name. + * + * Nested `poudriere make|src|src-env' already stripped both keywords; + * remaining argv is options plus names. */ - if (argc > 0) { + if (argc > 0 && !poudriere_native()) { target = resolved != NULL ? resolved : argv[0]; argc--; argv++; @@ -254,7 +257,10 @@ main(int argc, char *argv[]) #endif /* Resolve the target format and its backing files */ - resolve_target(target); + if (poudriere_native()) + poudriere_setup(); + else + resolve_target(target); /* `-k' requires a target with a module drop-in directory */ if (module != NULL) { @@ -564,6 +570,7 @@ usage(void) " -l | -L\n", pgm); fprintf(stderr, " %s rc [sysrc(8) argument ...]\n", pgm); + poudriere_usage(); fprintf(stderr, "Try `%s --help' for more information.\n", pgm); exit(EXIT_FAILURE); } @@ -598,6 +605,7 @@ help(void) "__MAKE_CONF, SRCCONF)"); fprintf(stderr, TGTFMT, "rc", "pass-through: all other arguments go to sysrc(8) verbatim"); + poudriere_help(); fprintf(stderr, TGTFMT, "generic", "no default files; requires -f file"); fprintf(stderr, diff --git a/usr.sbin/sysconf/sysconf_poudriere.c b/usr.sbin/sysconf/sysconf_poudriere.c new file mode 100644 index 000000000000..6e8b1e0e0295 --- /dev/null +++ b/usr.sbin/sysconf/sysconf_poudriere.c @@ -0,0 +1,677 @@ +/* + * Copyright (c) 2013-2026 Devin Teske + * Copyright (c) 2021-2026 Faraz Vahedi + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +/* + * poudriere(8) target: poudriere.conf via sysrc(8), and the make(1) + * overlays (make.conf, src.conf, src-env.conf) under poudriere.d. + */ + +#include "sysconf_priv.h" + +#define POUDRIERE_ETC_DEF "/usr/local/etc" +#define POUDRIERE_MAXFILES 16 + +/* + * Nested poudriere targets (make(1) overlay files under poudriere.d). + * Exact match wins; otherwise a unique prefix. `s' and `sr' are + * ambiguous (src vs src-env); `m' uniquely selects make. + */ +static const char *const poudriere_subs[] = { + "make", + "src", + "src-env", + NULL +}; + +/* Poudriere context; filled when the poudriere target is selected. */ +static const char *poudriere_sub = NULL; /* make, src, src-env */ +static const char *poudriere_jail = NULL; +static const char *poudriere_tree = NULL; /* --ports or --tree */ +static const char *poudriere_set = NULL; + +/* + * True when `arg' is `--name' or `--name=value'. + */ +static int +longopt_eq(const char *arg, const char *name) +{ + size_t nlen; + + if (arg[0] != '-' || arg[1] != '-') + return (0); + nlen = strlen(name); + if (strncmp(arg + 2, name, nlen) != 0) + return (0); + return (arg[2 + nlen] == '\0' || arg[2 + nlen] == '='); +} + +int +poudriere_longopt_p(const char *arg) +{ + + return (longopt_eq(arg, "jail") || longopt_eq(arg, "ports") || + longopt_eq(arg, "tree") || longopt_eq(arg, "set")); +} + +static void +poudriere_add_path(char **files, unsigned int *nfiles, unsigned int max, + const char *path) +{ + + if (*nfiles >= max) + errx(EXIT_FAILURE, "too many poudriere files"); + if ((files[*nfiles] = strdup(path)) == NULL) + err(EXIT_FAILURE, NULL); + (*nfiles)++; +} + +static void +poudriere_add_if_readable(char **files, unsigned int *nfiles, + unsigned int max, const char *path) +{ + + if (access(path, R_OK) != 0) + return; + poudriere_add_path(files, nfiles, max, path); +} + +static int +same_realpath(const char *a, const char *b) +{ + char ra[PATH_MAX]; + char rb[PATH_MAX]; + + if (realpath(a, ra) == NULL || realpath(b, rb) == NULL) + return (0); + return (strcmp(ra, rb) == 0); +} + +/* + * Join `dir'/`name', then prefix `-R dir' when set. `rootdir' is empty + * until parse_options(), so the sysrc pass-through is unprefixed. + */ +static void +poudriere_join(char *dst, size_t dstsz, const char *dir, const char *name) +{ + char raw[PATH_MAX]; + int n; + + n = snprintf(raw, sizeof(raw), "%s/%s", dir, name); + if (n < 0 || (size_t)n >= sizeof(raw)) + errx(EXIT_FAILURE, "%s/%s: %s", dir, name, + strerror(ENAMETOOLONG)); + n = snprintf(dst, dstsz, "%s%s", rootdir, raw); + if (n < 0 || (size_t)n >= dstsz) + errx(EXIT_FAILURE, "%s%s: %s", rootdir, raw, + strerror(ENAMETOOLONG)); +} + +static void +poudriere_etc_d(const char **etc, const char **d, char *dbuf, size_t dbufsz) +{ + + *etc = getenv("POUDRIERE_ETC"); + if (*etc == NULL || **etc == '\0') + *etc = POUDRIERE_ETC_DEF; + *d = getenv("POUDRIERED"); + if (*d == NULL || **d == '\0') { + if (snprintf(dbuf, dbufsz, "%s/poudriere.d", *etc) >= + (int)dbufsz) + errx(EXIT_FAILURE, "%s/poudriere.d: %s", *etc, + strerror(ENAMETOOLONG)); + *d = dbuf; + } +} + +static void +poudriere_try_named(char **files, unsigned int *nfiles, unsigned int max, + const char *dir, const char *stem, const char *suffix, int only_readable) +{ + char name[PATH_MAX]; + char path[PATH_MAX]; + int n; + + if (stem == NULL || *stem == '\0') + return; + n = snprintf(name, sizeof(name), "%s-%s", stem, suffix); + if (n < 0 || (size_t)n >= sizeof(name)) + errx(EXIT_FAILURE, "poudriere overlay name: %s", + strerror(ENAMETOOLONG)); + poudriere_join(path, sizeof(path), dir, name); + if (only_readable) + poudriere_add_if_readable(files, nfiles, max, path); + else + poudriere_add_path(files, nfiles, max, path); +} + +/* + * Overlay stems in poudriere(8) construction order. `full' is the + * make.conf / poudriere.conf set (set, tree, jail, and the four combos). + * src.conf and src-env.conf are global plus set and jail only. + */ +static void +poudriere_add_overlays(char **files, unsigned int *nfiles, unsigned int max, + const char *dir, const char *suffix, int full, int only_readable) +{ + char stem[PATH_MAX]; + int n; + + poudriere_try_named(files, nfiles, max, dir, poudriere_set, suffix, + only_readable); + if (full) + poudriere_try_named(files, nfiles, max, dir, poudriere_tree, + suffix, only_readable); + poudriere_try_named(files, nfiles, max, dir, poudriere_jail, suffix, + only_readable); + if (!full) + return; + if (poudriere_tree != NULL && *poudriere_tree != '\0' && + poudriere_set != NULL && *poudriere_set != '\0') { + n = snprintf(stem, sizeof(stem), "%s-%s", poudriere_tree, + poudriere_set); + if (n < 0 || (size_t)n >= sizeof(stem)) + errx(EXIT_FAILURE, "poudriere overlay name: %s", + strerror(ENAMETOOLONG)); + poudriere_try_named(files, nfiles, max, dir, stem, suffix, + only_readable); + } + if (poudriere_jail != NULL && *poudriere_jail != '\0' && + poudriere_tree != NULL && *poudriere_tree != '\0') { + n = snprintf(stem, sizeof(stem), "%s-%s", poudriere_jail, + poudriere_tree); + if (n < 0 || (size_t)n >= sizeof(stem)) + errx(EXIT_FAILURE, "poudriere overlay name: %s", + strerror(ENAMETOOLONG)); + poudriere_try_named(files, nfiles, max, dir, stem, suffix, + only_readable); + } + if (poudriere_jail != NULL && *poudriere_jail != '\0' && + poudriere_set != NULL && *poudriere_set != '\0') { + n = snprintf(stem, sizeof(stem), "%s-%s", poudriere_jail, + poudriere_set); + if (n < 0 || (size_t)n >= sizeof(stem)) + errx(EXIT_FAILURE, "poudriere overlay name: %s", + strerror(ENAMETOOLONG)); + poudriere_try_named(files, nfiles, max, dir, stem, suffix, + only_readable); + } + if (poudriere_jail != NULL && *poudriere_jail != '\0' && + poudriere_tree != NULL && *poudriere_tree != '\0' && + poudriere_set != NULL && *poudriere_set != '\0') { + n = snprintf(stem, sizeof(stem), "%s-%s-%s", poudriere_jail, + poudriere_tree, poudriere_set); + if (n < 0 || (size_t)n >= sizeof(stem)) + errx(EXIT_FAILURE, "poudriere overlay name: %s", + strerror(ENAMETOOLONG)); + poudriere_try_named(files, nfiles, max, dir, stem, suffix, + only_readable); + } +} + +/* + * Build the poudriere.conf collection in include_poudriere_confs() order: + * POUDRIERE_ETC/poudriere.conf, then POUDRIERED/poudriere.conf if distinct, + * then optional set/tree/jail overlays. At least one global file must exist. + */ +static void +poudriere_collect_files(char **files, unsigned int *nfiles, unsigned int max, + int only_readable) +{ + char dbuf[PATH_MAX]; + char path[PATH_MAX]; + const char *d; + const char *etc; + + poudriere_etc_d(&etc, &d, dbuf, sizeof(dbuf)); + + poudriere_join(path, sizeof(path), etc, "poudriere.conf"); + if (only_readable) + poudriere_add_if_readable(files, nfiles, max, path); + else + poudriere_add_path(files, nfiles, max, path); + + poudriere_join(path, sizeof(path), d, "poudriere.conf"); + if (*nfiles == 0 || !same_realpath(files[0], path)) { + if (only_readable) + poudriere_add_if_readable(files, nfiles, max, path); + else + poudriere_add_path(files, nfiles, max, path); + } + + if (only_readable && *nfiles == 0) + errx(EXIT_FAILURE, + "Unable to find a readable poudriere.conf in %s or %s", + etc, d); + + poudriere_add_overlays(files, nfiles, max, d, "poudriere.conf", 1, + only_readable); +} + +/* + * make.conf / src.conf / src-env.conf under POUDRIERED. The global file + * is always listed (ENOENT is tolerated on read; a write creates it). + * Overlays are appended only when readable, unless `only_readable' is 0 + * (`-L' lists every stem, including files that do not exist yet). + * Plugin hook make.conf is not collected. + */ +static void +poudriere_collect_make_files(char **files, unsigned int *nfiles, + unsigned int max, const char *suffix, int full, int only_readable) +{ + char dbuf[PATH_MAX]; + char path[PATH_MAX]; + const char *d; + const char *etc; + + poudriere_etc_d(&etc, &d, dbuf, sizeof(dbuf)); + poudriere_join(path, sizeof(path), d, suffix); + poudriere_add_path(files, nfiles, max, path); + poudriere_add_overlays(files, nfiles, max, d, suffix, full, + only_readable); +} + +/* + * True when remaining argv (except the target at `skip') contains sysrc(8) + * `-f'. Clusters are scanned like getopt: `f' takes an argument, as do + * `j', `R', and `s' (so `-qf FILE' counts and `-F' does not). + */ +static int +argv_has_sysrc_f(int argc, char *argv[], int skip) +{ + const char *p; + int n; + + for (n = 1; n < argc; n++) { + if (n == skip) + continue; + if (strcmp(argv[n], "--") == 0) + return (0); + if (argv[n][0] != '-' || argv[n][1] == '\0' || + argv[n][1] == '-') + continue; + for (p = argv[n] + 1; *p != '\0'; p++) { + if (*p == 'f') + return (1); + if (*p == 'j' || *p == 'R' || *p == 's') { + if (p[1] != '\0') + break; + if (n + 1 < argc) + n++; + break; + } + } + } + return (0); +} + +/* + * If `arg' is `--name' or `--name=value', return the value. Separate-argv + * form consumes `next' (*consumed = 1). Missing or empty values are errors. + */ +static const char * +longopt_arg(const char *arg, const char *next, int *consumed, + const char *name) +{ + size_t nlen; + + *consumed = 0; + if (!longopt_eq(arg, name)) + return (NULL); + nlen = strlen(name); + if (arg[2 + nlen] == '=') { + if (arg[3 + nlen] == '\0') + errx(EXIT_FAILURE, + "option `--%s' requires an argument", name); + return (arg + 3 + nlen); + } + if (next == NULL || *next == '\0') + errx(EXIT_FAILURE, "option `--%s' requires an argument", + name); + *consumed = 1; + return (next); +} + +static void +poudriere_parse_longopts(int argc, char *argv[]) +{ + const char *next; + const char *val; + int consumed; + int n; + + for (n = 1; n < argc; n++) { + if (strcmp(argv[n], "--") == 0) + break; + consumed = 0; + next = (n + 1 < argc) ? argv[n + 1] : NULL; + if ((val = longopt_arg(argv[n], next, &consumed, + "jail")) != NULL) + poudriere_jail = val; + else if ((val = longopt_arg(argv[n], next, &consumed, + "ports")) != NULL) + poudriere_tree = val; + else if ((val = longopt_arg(argv[n], next, &consumed, + "tree")) != NULL) + poudriere_tree = val; + else if ((val = longopt_arg(argv[n], next, &consumed, + "set")) != NULL) + poudriere_set = val; + if (consumed) + n++; + } +} + +/* + * Drop the poudriere keyword, optional nested target, and context long + * options so the remainder can be parsed as a native sysconf command + * line (or, for the sysrc path, rebuilt without those tokens). + */ +static int +poudriere_compact(int argc, char *argv[], int tgt, int sub) +{ + int dst; + int endopts; + int n; + + dst = 1; + endopts = 0; + for (n = 1; n < argc; n++) { + if (n == tgt || n == sub) + continue; + if (!endopts && strcmp(argv[n], "--") == 0) + endopts = 1; + if (!endopts && poudriere_longopt_p(argv[n])) { + if (strchr(argv[n], '=') == NULL && n + 1 < argc) + n++; + continue; + } + argv[dst++] = argv[n]; + } + argv[dst] = NULL; + return (dst); +} + +/* + * `-l' / `-L' on this target. Returns 1 when either is present. + * `*all' is `-L' (every location for the named jail, tree, and set, + * including files that do not exist). `*exist' is `-E'. `*filep' is an + * explicit `-f'. `*names' is a non-option operand (listing takes none). + * `-R dir' is applied as the sysconf root for the paths printed. + */ +static int +argv_wants_list(int argc, char *argv[], int skip, int *all, int *exist, + const char **filep, int *names) +{ + const char *arg; + const char *p; + int list; + int n; + + *all = 0; + *exist = 0; + *filep = NULL; + *names = 0; + list = 0; + for (n = 1; n < argc; n++) { + if (n == skip) + continue; + if (strcmp(argv[n], "--") == 0) { + if (n + 1 < argc) + *names = 1; + break; + } + if (argv[n][0] == '-' && argv[n][1] == '-') { + if (poudriere_longopt_p(argv[n]) && + strchr(argv[n], '=') == NULL && n + 1 < argc) + n++; + continue; + } + if (argv[n][0] != '-' || argv[n][1] == '\0') { + *names = 1; + continue; + } + for (p = argv[n] + 1; *p != '\0'; p++) { + if (*p == 'l') + list = 1; + else if (*p == 'L') { + list = 1; + *all = 1; + } else if (*p == 'E') + *exist = 1; + else if (*p == 'f' || *p == 'j' || *p == 'R' || + *p == 's') { + if (p[1] != '\0') + arg = p + 1; + else if (n + 1 < argc) + arg = argv[++n]; + else + arg = NULL; + if (*p == 'f') + *filep = arg; + else if (*p == 'R' && arg != NULL) + rootdir = arg; + break; + } + } + } + return (list); +} + +static void +poudriere_puts_files(char **files, unsigned int nfiles, int exist_only) +{ + unsigned int n; + + for (n = 0; n < nfiles; n++) { + if (exist_only && access(files[n], F_OK) != 0) + continue; + puts(files[n]); + } +} + +/* + * The `poudriere' target without a nested make/src/src-env keyword: + * resolve the shell-sourced poudriere.conf collection (same order as + * poudriere's include_poudriere_confs()) and hand it to sysrc(8) as + * `-f' files. `--jail'/`--ports'/`--tree'/`--set' select overlays; + * they are stripped so sysrc never sees them. An explicit sysrc `-f' + * replaces discovery. `-l' and `-L' are printed here, one path per + * line. Never returns. + */ +static void +exec_poudriere_sysrc(int argc, char *argv[], int skip) +{ + char *files[POUDRIERE_MAXFILES]; + char **nargv; + char path[PATH_MAX]; + const char *list_file; + unsigned int nfiles; + int all; + int endopts; + int exist; + int n; + int names; + int nargc; + int user_f; + + nfiles = 0; + nargc = 0; + if (argv_wants_list(argc, argv, skip, &all, &exist, &list_file, + &names)) { + if (names) + errx(EXIT_FAILURE, "-l/-L take no names"); + if (list_file != NULL) { + if (snprintf(path, sizeof(path), "%s%s", rootdir, + list_file) >= (int)sizeof(path)) + errx(EXIT_FAILURE, "%s%s: %s", rootdir, + list_file, strerror(ENAMETOOLONG)); + if (!exist || access(path, F_OK) == 0) + puts(path); + exit(EXIT_SUCCESS); + } + poudriere_collect_files(files, &nfiles, POUDRIERE_MAXFILES, + all ? 0 : 1); + poudriere_puts_files(files, nfiles, exist); + exit(EXIT_SUCCESS); + } + user_f = argv_has_sysrc_f(argc, argv, skip); + if (!user_f) + poudriere_collect_files(files, &nfiles, POUDRIERE_MAXFILES, + 1); + + if ((nargv = calloc((size_t)argc + (size_t)nfiles * 2 + 2, + sizeof(*nargv))) == NULL) + err(EXIT_FAILURE, NULL); + nargv[nargc++] = (char *)(uintptr_t)"sysrc"; + for (n = 0; n < (int)nfiles; n++) { + nargv[nargc++] = (char *)(uintptr_t)"-f"; + nargv[nargc++] = files[n]; + } + endopts = 0; + for (n = 1; n < argc; n++) { + if (n == skip) + continue; + if (!endopts && strcmp(argv[n], "--") == 0) { + endopts = 1; + nargv[nargc++] = argv[n]; + continue; + } + if (!endopts && poudriere_longopt_p(argv[n])) { + if (strchr(argv[n], '=') == NULL && n + 1 < argc) + n++; + continue; + } + nargv[nargc++] = argv[n]; + } + nargv[nargc] = NULL; + + execvp("sysrc", nargv); + err(EXIT_FAILURE, "sysrc"); +} + +int +poudriere_native(void) +{ + + return (poudriere_sub != NULL); +} + +/* + * Nested `poudriere make|src|src-env': native make(1) format over the + * matching poudriere.d overlay collection. `-f' replaces discovery. + */ +void +poudriere_setup(void) +{ + char *files[POUDRIERE_MAXFILES]; + char path[PATH_MAX]; + const char *prefix; + const char *suffix; + int full; + unsigned int i; + unsigned int nfiles; + + format = BSDCONF_FORMAT_MAKE; + if (file != NULL) { + prefix = file_stdin ? "" : rootdir; + if ((conf_files = calloc(1, sizeof(*conf_files))) == NULL) + err(EXIT_FAILURE, NULL); + if (snprintf(path, sizeof(path), "%s%s", prefix, file) >= + (int)sizeof(path)) + errx(EXIT_FAILURE, "%s%s: %s", prefix, file, + strerror(ENAMETOOLONG)); + if ((conf_files[0] = strdup(path)) == NULL) + err(EXIT_FAILURE, NULL); + nconf_files = 1; + write_idx = 0; + return; + } + + if (strcmp(poudriere_sub, "make") == 0) { + suffix = "make.conf"; + full = 1; + } else if (strcmp(poudriere_sub, "src") == 0) { + suffix = "src.conf"; + full = 0; + } else { + suffix = "src-env.conf"; + full = 0; + } + + nfiles = 0; + /* `-L' lists missing overlays; reads and `-l' do not. */ + poudriere_collect_make_files(files, &nfiles, POUDRIERE_MAXFILES, + suffix, full, list_all ? 0 : 1); + if ((conf_files = calloc(nfiles, sizeof(*conf_files))) == NULL) + err(EXIT_FAILURE, NULL); + for (i = 0; i < nfiles; i++) + conf_files[i] = files[i]; + nconf_files = nfiles; + write_idx = 0; +} + +/* + * On the sysrc path, exec and do not return. On the native + * make/src/src-env path, rewrite argv and return the new argc. + * A bare `--' before the next word ends the nested keyword, so that + * word is a poudriere.conf variable (`sysconf p -- src'). + */ +int +poudriere_dispatch(int argc, char *argv[], int tgt) +{ + int subn; + + poudriere_parse_longopts(argc, argv); + subn = find_operand(argc, argv, tgt + 1, 0); + if (subn > 0 && strchr(argv[subn], '=') == NULL) + poudriere_sub = resolve_keyword(argv[subn], poudriere_subs, + "poudriere target"); + if (poudriere_sub == NULL) + exec_poudriere_sysrc(argc, argv, tgt); /* never returns */ + return (poudriere_compact(argc, argv, tgt, subn)); +} + +void +poudriere_usage(void) +{ + + fprintf(stderr, + " %s poudriere [--jail name] [--ports name] [--tree name]\n", + pgm); + fprintf(stderr, + " [--set name] [sysrc(8) argument ...]\n"); + fprintf(stderr, + " %s poudriere {make|src|src-env} [-ceFinNqsvVx]\n", pgm); + fprintf(stderr, + " [--jail name] [--ports name] [--tree name]\n"); + fprintf(stderr, + " [--set name] [-j jail | -R dir] [-f file]\n"); + fprintf(stderr, + " name[[+|-]=value] ...\n"); + fprintf(stderr, + " %s poudriere {make|src|src-env} [-eFnNqvV]\n", pgm); + fprintf(stderr, + " [--jail name] [--ports name] [--tree name]\n"); + fprintf(stderr, + " [--set name] [-f file] -a\n"); + fprintf(stderr, + " %s poudriere {make|src|src-env} [-E]\n", pgm); + fprintf(stderr, + " [--jail name] [--ports name] [--tree name]\n"); + fprintf(stderr, + " [--set name] [-f file] -l | -L\n"); +} + +void +poudriere_help(void) +{ + + fprintf(stderr, "\t%-9s %s\n", "poudriere", + "poudriere.conf via sysrc(8); make/src/src-env overlays"); + fprintf(stderr, "\t%-9s %s\n", "", + "(--jail/--ports/--tree/--set select poudriere.d files)"); +} diff --git a/usr.sbin/sysconf/sysconf_priv.h b/usr.sbin/sysconf/sysconf_priv.h index 8add1fb3956e..a2d9190df284 100644 --- a/usr.sbin/sysconf/sysconf_priv.h +++ b/usr.sbin/sysconf/sysconf_priv.h @@ -37,7 +37,7 @@ #define __unused /* not all compilers support attributes */ #endif -#define SYSCONF_VERSION "2.1 2026-09-22" +#define SYSCONF_VERSION "2.2 2026-09-22" /* getopt(3) optstring; shared by parse_options() and find_target() */ #define OPTSTRING "AacdDEeFf:hij:k:lLnNqR:svVx" @@ -197,12 +197,21 @@ int sysctl_writable(const char *_name, const char *_value, #endif /* sysconf-targets.c */ +int find_operand(int _argc, char **_argv, int _from, int _past_dd); const char *resolve_keyword(const char *_token, const char *const *_keywords, const char *_what); -const char *target_resolve(int _argc, char **_argv); +const char *target_resolve(int *_argcp, char **_argv); /* sysconf.c */ void usage(void); void help(void); +/* sysconf_poudriere.c */ +int poudriere_longopt_p(const char *_arg); +int poudriere_dispatch(int _argc, char **_argv, int _tgt); +int poudriere_native(void); +void poudriere_setup(void); +void poudriere_usage(void); +void poudriere_help(void); + #endif /* !_SYSCONF_PRIV_H_ */ diff --git a/usr.sbin/sysconf/tests/sysconf_test.sh b/usr.sbin/sysconf/tests/sysconf_test.sh index 0914e8727d2f..61cc0f8e4e40 100644 --- a/usr.sbin/sysconf/tests/sysconf_test.sh +++ b/usr.sbin/sysconf/tests/sysconf_test.sh @@ -486,6 +486,205 @@ target_prefix_cleanup() cleanup_root } +atf_test_case poudriere_sysrc cleanup +poudriere_sysrc_head() +{ + atf_set "descr" \ + "poudriere target feeds sysrc -f over the conf collection" +} +poudriere_sysrc_body() +{ + find_sysconf + command -v sysrc > /dev/null 2>&1 || + atf_skip "sysrc not installed" + ETC="$( pwd )/petc" + mkdir -p "$ETC/poudriere.d" + printf 'ZPOOL=old\nFOO=global\n' > "$ETC/poudriere.conf" + printf 'FOO=jail\nJOBS=2\n' \ + > "$ETC/poudriere.d/131amd64-poudriere.conf" + export POUDRIERE_ETC="$ETC" + + # -l / -L are printed here, one path per line (not sysrc -L) + atf_check -o inline:"$ETC/poudriere.conf\n" \ + "$SYSCONF" p -l + atf_check -o inline:"$ETC/poudriere.conf\n$ETC/poudriere.d/poudriere.conf\n" \ + "$SYSCONF" p -L + atf_check -o inline:"$ETC/poudriere.conf\n$ETC/poudriere.d/131amd64-poudriere.conf\n" \ + "$SYSCONF" p --jail 131amd64 -l + atf_check -o inline:"$ETC/poudriere.conf\n$ETC/poudriere.d/poudriere.conf\n$ETC/poudriere.d/131amd64-poudriere.conf\n" \ + "$SYSCONF" p --jail 131amd64 -L + atf_check -o inline:"$ETC/poudriere.conf\n" \ + "$SYSCONF" p --jail 99amd64 -l + atf_check -o match:'99amd64-poudriere.conf' \ + -o not-match:'rc\.conf' \ + "$SYSCONF" p --jail 99amd64 -L + atf_check -o inline:"$ETC/poudriere.conf\n" \ + "$SYSCONF" p -E -L + atf_check -o inline:"$ETC/poudriere.conf\n$ETC/poudriere.d/131amd64-poudriere.conf\n" \ + "$SYSCONF" p --jail 131amd64 -E -L + atf_check -s not-exit:0 -e match:'take no names' \ + "$SYSCONF" p -l ZPOOL + + # Shorthand p; read from the global file + atf_check -o match:'ZPOOL: old' \ + "$SYSCONF" p ZPOOL + # Write a new name into the first file + atf_check -o match:'BASEFS' \ + "$SYSCONF" poudriere BASEFS=/poudriere + atf_check -o match:'BASEFS=' cat "$ETC/poudriere.conf" + atf_check -o not-match:'BASEFS' \ + cat "$ETC/poudriere.d/131amd64-poudriere.conf" + + # Overlay collection: --jail appends the jail overlay last + atf_check -o match:'FOO: jail' \ + "$SYSCONF" poudriere --jail 131amd64 FOO + # Without --jail the overlay is not in the collection + atf_check -o match:'FOO: global' \ + "$SYSCONF" poudriere FOO + # --tree is an alias of --ports + printf 'FOO=tree\n' > "$ETC/poudriere.d/default-poudriere.conf" + atf_check -o match:'FOO: tree' \ + "$SYSCONF" poudriere --tree default FOO + atf_check -o match:'FOO: tree' \ + "$SYSCONF" poudriere --ports default FOO + # Rewrite the overlay's existing var + atf_check -o match:'JOBS' \ + "$SYSCONF" poudriere --jail 131amd64 JOBS=8 + atf_check -o match:'JOBS: 8' \ + "$SYSCONF" poudriere --jail 131amd64 JOBS + + # Attached --jail= + atf_check -o match:'FOO: jail' \ + "$SYSCONF" poudriere --jail=131amd64 FOO + + # `--' ends the nested keyword; colliding names are variables + printf 'src=from-conf\nm=short\n' >> "$ETC/poudriere.conf" + printf 'src=from-overlay\n' > "$ETC/poudriere.d/src.conf" + atf_check -o match:'src: from-conf' \ + "$SYSCONF" p -- src + atf_check -o match:'m: short' \ + "$SYSCONF" p -- m + atf_check -s not-exit:0 -e match:"unknown variable 'make'" \ + "$SYSCONF" p -- make + atf_check -o match:'src: from-overlay' \ + "$SYSCONF" p src src + + # -f replaces discovery + printf 'BAR=only\n' > "$ETC/other.conf" + atf_check -o match:'BAR: only' \ + "$SYSCONF" poudriere -f "$ETC/other.conf" BAR + + # Missing globals + unset POUDRIERE_ETC + export POUDRIERE_ETC="$( pwd )/missing-etc" + mkdir -p "$POUDRIERE_ETC" + atf_check -s not-exit:0 -e match:'poudriere.conf' \ + "$SYSCONF" p ZPOOL +} +poudriere_sysrc_cleanup() +{ + rm -rf "$( pwd )/petc" "$( pwd )/missing-etc" +} + +atf_test_case poudriere_overlays cleanup +poudriere_overlays_head() +{ + atf_set "descr" \ + "poudriere make/src/src-env overlays with --jail/--tree/--set" +} +poudriere_overlays_body() +{ + find_sysconf + ETC="$( pwd )/petc" + D="$ETC/poudriere.d" + mkdir -p "$D" + export POUDRIERE_ETC="$ETC" + + printf 'CFLAGS=global\n' > "$D/make.conf" + printf 'CFLAGS=jail\n' > "$D/131amd64-make.conf" + printf 'CFLAGS=tree\n' > "$D/default-make.conf" + printf 'CFLAGS=set\n' > "$D/dev-make.conf" + printf 'CFLAGS=combo\n' > "$D/131amd64-default-make.conf" + + # Shorthand p m + atf_check -o inline:'CFLAGS: global\n' \ + "$SYSCONF" p m CFLAGS + atf_check -o inline:'CFLAGS: jail\n' \ + "$SYSCONF" poudriere make --jail 131amd64 CFLAGS + atf_check -o inline:'CFLAGS: tree\n' \ + "$SYSCONF" p m --tree default CFLAGS + atf_check -o inline:'CFLAGS: tree\n' \ + "$SYSCONF" poudriere make --ports default CFLAGS + atf_check -o inline:'CFLAGS: set\n' \ + "$SYSCONF" poudriere make --set dev CFLAGS + # jail-tree combo is last of those two + atf_check -o inline:'CFLAGS: combo\n' \ + "$SYSCONF" poudriere make --jail 131amd64 --tree default CFLAGS + + # Rewrite overlay's existing name; new name goes to global + atf_check -o match:'CFLAGS' \ + "$SYSCONF" poudriere make --jail 131amd64 CFLAGS=-O2 + atf_check -o match:'CFLAGS=-O2' cat "$D/131amd64-make.conf" + atf_check -o match:'CFLAGS=global' cat "$D/make.conf" + atf_check -o match:'CXXFLAGS' \ + "$SYSCONF" poudriere make --jail 131amd64 CXXFLAGS=-pipe + atf_check -o match:'CXXFLAGS=-pipe' cat "$D/make.conf" + atf_check -o not-match:'CXXFLAGS' cat "$D/131amd64-make.conf" + + # Create missing global + rm -f "$D/make.conf" + atf_check -o match:'LDFLAGS' \ + "$SYSCONF" p m LDFLAGS=-L/usr/local/lib + atf_check -o match:'LDFLAGS=-L/usr/local/lib' cat "$D/make.conf" + + # src vs src-env; --tree ignored (no tree-src.conf in poudriere(8)) + printf 'WITHOUT_FOO=\n' > "$D/src.conf" + printf 'WITHOUT_TREE=\n' > "$D/default-src.conf" + printf 'SRC_ENV_VAR=1\n' > "$D/src-env.conf" + printf 'SRC_ENV_VAR=tree\n' > "$D/default-src-env.conf" + atf_check -o inline:'WITHOUT_FOO (present)\n' \ + "$SYSCONF" poudriere src WITHOUT_FOO + atf_check -s not-exit:0 -e match:'unknown directive' \ + "$SYSCONF" poudriere src --tree default WITHOUT_TREE + atf_check -o inline:'SRC_ENV_VAR: 1\n' \ + "$SYSCONF" poudriere src-env SRC_ENV_VAR + atf_check -o inline:'SRC_ENV_VAR: 1\n' \ + "$SYSCONF" poudriere src-env --tree default SRC_ENV_VAR + atf_check -o inline:'SRC_ENV_VAR: 1\n' \ + "$SYSCONF" p src-e SRC_ENV_VAR + + # Ambiguous nested prefix + atf_check -s not-exit:0 -e match:'ambiguous' -e match:'src' \ + -e match:'src-env' \ + "$SYSCONF" p s FOO + + # -l lists the collection + atf_check -o match:'make.conf' -o match:'131amd64-make.conf' \ + "$SYSCONF" poudriere make --jail 131amd64 -l + + # -L lists every poudriere(8) stem, including files not on disk + atf_check -o inline:"$D/make.conf\n$D/abc-make.conf\n$D/xyz-make.conf\n$D/160-make.conf\n$D/xyz-abc-make.conf\n$D/160-xyz-make.conf\n$D/160-abc-make.conf\n$D/160-xyz-abc-make.conf\n" \ + "$SYSCONF" p m --jail 160 --set abc --tree xyz -L + atf_check -o inline:"$D/make.conf\n$D/xyz-make.conf\n$D/160-make.conf\n$D/160-xyz-make.conf\n" \ + "$SYSCONF" p m --jail 160 --tree xyz -L + atf_check -o inline:"$D/make.conf\n" \ + "$SYSCONF" p m --jail 160 --set abc --tree xyz -l + atf_check -o inline:"$D/make.conf\n" \ + "$SYSCONF" p m --jail 160 --set abc --tree xyz -E -L + atf_check -o inline:"$D/src.conf\n$D/abc-src.conf\n$D/160-src.conf\n" \ + "$SYSCONF" p src --jail 160 --set abc --tree xyz -L + atf_check -o inline:"$D/src.conf\n$D/160-src.conf\n" \ + "$SYSCONF" p src --jail 160 --tree xyz -L + atf_check -o inline:"$D/src-env.conf\n$D/abc-src-env.conf\n$D/160-src-env.conf\n" \ + "$SYSCONF" p src-env --jail 160 --set abc --tree xyz -L + atf_check -o inline:"$D/src-env.conf\n" \ + "$SYSCONF" p src-env --jail 160 --set abc --tree xyz -E -L +} +poudriere_overlays_cleanup() +{ + rm -rf "$( pwd )/petc" +} + atf_init_test_cases() { atf_add_test_case make_src_knobs @@ -500,4 +699,6 @@ atf_init_test_cases() atf_add_test_case sysctl_oid_range atf_add_test_case max_bytes_cap atf_add_test_case target_prefix + atf_add_test_case poudriere_sysrc + atf_add_test_case poudriere_overlays }