From nobody Mon Jul 13 15:33:36 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gzRKy40JXz6lQZc for ; Mon, 13 Jul 2026 15:33:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gzRKy1X8yz3whp for ; Mon, 13 Jul 2026 15:33:42 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783956822; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=8Qrut3xxCiyYQwSdw6QcqoQg7+ZBr11WBP20rE6t82A=; b=MpVIzbJQnT00xz0MI6dEAVieuJsylLCymHjw+rSrw+1i8i2tomDJ6KoWtQNl63ru4WbANJ Mfw5JL+GXPZvX/FKSPMUd1nGiSEo1ReX5JGKttLaXSvzxrn0GPFOoxSwzoXDcMgi96agbs myjE9GdCc9l1E6FaJqC6lRecOgj7y65CCbjkInNmw9k/C5kp2D+r7sLw+RaFh6OYyJVdjZ kKz9zOuxaLM/CxWLKCOyFJ2rtUPEXz8zZrbPQ5AK9bIk0lPyq3p9bOArVEfLJoXj9UhF1J /kS4D7efg7Iyip4eKMIwpFwOrQr/pxjS2YdIxEsjlaRhQ45xw/FgWz3oy0VEpQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1783956822; a=rsa-sha256; cv=none; b=ZkdlA0yZoAdYCVfAyPermVc6qwx7Fla//xptmkx4EzOCBFdK+jIBp6BYbi0+nUiFoLsuAa /2xCXiGBk9rEqpihFnI6o7b437sBNDaijor3iJGKJRIka/bmZ56n7ElprrhEtvwytq7I/u ZLH17EybovEG2X9DNUg/WwhD76ObfWolQy56a5sQzuI2uv7PSF9ge/yt4LUMjXmBxoJpJF 3KI74aIfg+15GeAvidXmEPXKV0OWNfXmRv1eM2j/clGbEOoaUrS0OMnVxvhHlVRZMHNJzo UWu+J72H/IxS6ekC1eFijtqYH3gw2uWixA6ieJf8vPw83Lp6f8Z8bJ0rHEj+WQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1783956822; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=8Qrut3xxCiyYQwSdw6QcqoQg7+ZBr11WBP20rE6t82A=; b=VPyb29IftEnS3icbtJSlxGbpLBi3Y46GCcO09ogMksVZH+6VzmdNLnRsv3T134yyHjy6H8 wU9jNqOS3x5gkw1gDrJkFbPReiv0rY5Xo8SWcFVfBopCXZ2tycFDpOLfPpHD6hYanHCvCA mxBHaWXk7pcfFCEu3nmA5GuglLYZhqQIb1m2SFId5TUb8hnxfUAwEcN4uyexQZI9FHw/dr y9EbUgsk8q4wRQUqvldvuKkFGa6GeWR/efy8B2YkV1+TAxh3rb+ROrkzp1xX39HJLM9NbK Q6Cyzz5CpfxaJ7SKRCSIUAIB1g1R+OPAVU2Dr7fh/dhtxmbp407Kh96HFwzw8w== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gzRKy0QN2z149y for ; Mon, 13 Jul 2026 15:33:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 40ea8 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Mon, 13 Jul 2026 15:33:36 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Dag-Erling=?utf-8?Q? Sm=C3=B8rg?=rav Subject: git: d94e034d5046 - main - pkg: Add -j and -r options List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: des X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: d94e034d504682be56fc2e9d20ac2c0fe15b70ec Auto-Submitted: auto-generated Date: Mon, 13 Jul 2026 15:33:36 +0000 Message-Id: <6a550550.40ea8.1c8a56c4@gitrepo.freebsd.org> The branch main has been updated by des: URL: https://cgit.FreeBSD.org/src/commit/?id=d94e034d504682be56fc2e9d20ac2c0fe15b70ec commit d94e034d504682be56fc2e9d20ac2c0fe15b70ec Author: Dag-Erling Smørgrav AuthorDate: 2026-07-13 15:32:41 +0000 Commit: Dag-Erling Smørgrav CommitDate: 2026-07-13 15:32:41 +0000 pkg: Add -j and -r options This allows pkg(7) to be used to bootstrap a jail or chroot, and to recognize the -j and -r options and pass them through to pkg(8) if already bootstrapped. Note that this does not address the issue of repository keys. If using a signed package repository, you will still need to copy /usr/share/keys into the target environment before or after bootstrapping, or pkg will be unable to verify package signatures. MFC after: 1 week Reviewed by: imp, bapt Differential Revision: https://reviews.freebsd.org/D58165 --- usr.sbin/pkg/Makefile | 2 +- usr.sbin/pkg/pkg.7 | 28 +++++++++--- usr.sbin/pkg/pkg.c | 116 +++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 115 insertions(+), 31 deletions(-) diff --git a/usr.sbin/pkg/Makefile b/usr.sbin/pkg/Makefile index 68b862826992..3b3249c538dd 100644 --- a/usr.sbin/pkg/Makefile +++ b/usr.sbin/pkg/Makefile @@ -27,7 +27,7 @@ MAN= pkg.7 CFLAGS+=-I${SRCTOP}/contrib/libucl/include .PATH: ${SRCTOP}/contrib/libucl/include -LIBADD= archive der fetch pkgecc ucl crypto ssl util md +LIBADD= archive der fetch jail pkgecc ucl crypto ssl util md CFLAGS+=-I${SRCTOP}/contrib/libder/libder CFLAGS+=-I${SRCTOP}/crypto/libecc/include diff --git a/usr.sbin/pkg/pkg.7 b/usr.sbin/pkg/pkg.7 index d2246f74a3fc..9d6dc21c8472 100644 --- a/usr.sbin/pkg/pkg.7 +++ b/usr.sbin/pkg/pkg.7 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 29, 2025 +.Dd July 13, 2026 .Dt PKG 7 .Os .Sh NAME @@ -31,17 +31,25 @@ .Sh SYNOPSIS .Nm .Op Fl d +.Op Fl j Ar jail +.Op Fl r Ar rootdir .Ar command ... .Nm .Op Fl d +.Op Fl j Ar jail +.Op Fl r Ar rootdir .Cm add .Op Fl fy .Op Fl r Ar reponame .Ar pkg.pkg .Nm +.Op Fl j Ar jail +.Op Fl r Ar rootdir .Fl N .Nm .Op Fl 46d +.Op Fl j Ar jail +.Op Fl r Ar rootdir .Cm bootstrap .Op Fl fy .Op Fl r Ar reponame @@ -61,14 +69,14 @@ will bootstrap the real .Xr pkg 8 from a remote repository. .Bl -tag -.It Nm Ar command ... +.It Nm Oo Fl j Ar jail Oc Oo Fl r Ar rootdir Oc Ar command ... If .Xr pkg 8 is not installed yet, it will be fetched, have its signature verified, installed, and then have the original command forwarded to it. If already installed, the command requested will be forwarded to the real .Xr pkg 8 . -.It Nm Cm add Oo Fl fy Oc Oo Fl r Ar reponame Oc Ar pkg.pkg +.It Nm Oo Fl j Ar jail Oc Oo Fl r Ar rootdir Oc Cm add Oo Fl fy Oc Oo Fl r Ar reponame Oc Ar pkg.pkg Install .Xr pkg 8 from a local package instead of fetching from remote. @@ -88,13 +96,13 @@ If a .Ar reponame has been specified, then the signature configuration for that repository will be used. -.It Nm Fl N +.It Nm Oo Fl j Ar jail Oc Oo Fl r Ar rootdir Oc Fl N Do not bootstrap, just determine if .Xr pkg 8 is actually installed or not. Returns 0 and the number of packages installed if it is, otherwise 1. -.It Nm Oo Fl 46 Oc Cm bootstrap Oo Fl fy Oc \ +.It Nm Oo Fl 46d Oc Oo Fl j Ar jail Oc Oo Fl r Ar rootdir Oc Cm bootstrap Oo Fl fy Oc \ Oo Fl r Ar reponame Oc Attempt to bootstrap and do not forward anything to .Xr pkg 8 @@ -131,6 +139,16 @@ May be specified more than once to increase the level of detail. When specified twice, .Xr fetch 3 debug output is enabled. +.It Fl j Ar jail , Fl -jail Ar jail +Attach to the jail designated by the name or numerical identifier +.Ar jail . +Note that +.Nm +will attach to the jail immediately upon encountering this option, so +any paths should be relative to the root of the jail. +.It Fl r Ar rootdir , Fl -rootdir Ar rootdir +Operate on the system rooted at +.Ar rootdir . .El .Sh CONFIGURATION Configuration varies in whether it is in a repository configuration file diff --git a/usr.sbin/pkg/pkg.c b/usr.sbin/pkg/pkg.c index 33a404474cf2..3621081504cc 100644 --- a/usr.sbin/pkg/pkg.c +++ b/usr.sbin/pkg/pkg.c @@ -28,8 +28,7 @@ */ #include -#include -#include +#include #include #include @@ -41,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -92,6 +92,8 @@ struct fingerprint { }; static const char *bootstrap_name = "pkg.pkg"; +static const char *rootdir = NULL; +static int rootd = -1; STAILQ_HEAD(fingerprint_list, fingerprint); @@ -168,7 +170,8 @@ extract_pkg_static(int fd, char *p, int sz) { struct archive *a; struct archive_entry *ae; - char *end; + const char *name, *end; + char *rname; int ret, r; ret = -1; @@ -192,11 +195,22 @@ extract_pkg_static(int fd, char *p, int sz) ae = NULL; while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { - end = strrchr(archive_entry_pathname(ae), '/'); + name = archive_entry_pathname(ae); + end = strrchr(name, '/'); if (end == NULL) continue; if (strcmp(end, "/pkg-static") == 0) { + if (rootdir) { + if (asprintf(&rname, "%s%s", rootdir, name) < 0) + err(1, NULL); + archive_entry_copy_pathname(ae, rname); + free(rname); + } + if (debug) { + fprintf(stderr, "extracting %s...\n", + archive_entry_pathname(ae)); + } r = archive_read_extract(a, ae, ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | @@ -221,28 +235,56 @@ cleanup: static int install_pkg_static(const char *path, const char *pkgpath, bool force) { + const char *argv[16]; + int argc = 0; int pstat; pid_t pid; - switch ((pid = fork())) { - case -1: + /* assemble the command line */ + argv[argc++] = "pkg-static"; + if (rootdir) { + argv[argc++] = "-r"; + argv[argc++] = rootdir; + } + argv[argc++] = "add"; + if (rootdir) { + /* + * Installing into an empty directory will normally fail + * because none of the shared libraries required by pkg + * are present, but pkg-static will still work and can be + * used to install a packaged base, so tell pkg-static to + * ignore missing dependencies. + */ + argv[argc++] = "-M"; + } + if (force) + argv[argc++] = "-f"; + argv[argc++] = pkgpath; + argv[argc] = NULL; + assert((size_t)argc < sizeof(argv) / sizeof(*argv)); + + if (debug) { + fprintf(stderr, "%s", path); + for (int i = 1; i < argc; i++) + fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + + /* fork and exec */ + if ((pid = fork()) < 0) return (-1); - case 0: - if (force) - execl(path, "pkg-static", "add", "-f", pkgpath, - (char *)NULL); - else - execl(path, "pkg-static", "add", pkgpath, - (char *)NULL); + if (pid == 0) { + /* child */ + execv(path, __DECONST(char **, argv)); _exit(1); - default: - break; } + /* wait for pkg-static to complete */ while (waitpid(pid, &pstat, 0) == -1) if (errno != EINTR) return (-1); + /* check the result */ if (WEXITSTATUS(pstat)) return (WEXITSTATUS(pstat)); else if (WIFSIGNALED(pstat)) @@ -946,7 +988,7 @@ static const char non_interactive_message[] = static const char args_bootstrap_message[] = "Too many arguments\n" -"Usage: pkg [-4|-6] bootstrap [-f] [-y]\n"; +"Usage: pkg [-46d] [-r rootdir] bootstrap [-f] [-y]\n"; static int pkg_query_yes_no(void) @@ -978,7 +1020,7 @@ bootstrap_pkg_local(const char *pkgpath, bool force) fd_sig = -1; ret = -1; - fd_pkg = open(pkgpath, O_RDONLY); + fd_pkg = open(pkgpath, O_RDONLY | O_CLOEXEC); if (fd_pkg == -1) err(EXIT_FAILURE, "Unable to open %s", pkgpath); @@ -992,7 +1034,8 @@ bootstrap_pkg_local(const char *pkgpath, bool force) snprintf(path, sizeof(path), "%s.sig", pkgpath); - if ((fd_sig = open(path, O_RDONLY)) == -1) { + fd_sig = open(path, O_RDONLY | O_CLOEXEC); + if (fd_sig == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto cleanup; @@ -1005,7 +1048,8 @@ bootstrap_pkg_local(const char *pkgpath, bool force) snprintf(path, sizeof(path), "%s.pubkeysig", pkgpath); - if ((fd_sig = open(path, O_RDONLY)) == -1) { + fd_sig = open(path, O_RDONLY | O_CLOEXEC); + if (fd_sig == -1) { fprintf(stderr, "Signature for pkg not " "available.\n"); goto cleanup; @@ -1075,9 +1119,9 @@ main(int argc, char *argv[]) { char pkgpath[MAXPATHLEN]; char **original_argv; - const char *pkgarg, *repo_name; + const char *localbase, *pkgarg, *repo_name; bool activation_test, add_pkg, bootstrap_only, force, yes; - signed char ch; + int ch, jid; const char *fetchOpts; struct repositories *repositories; @@ -1093,21 +1137,35 @@ main(int argc, char *argv[]) struct option longopts[] = { { "debug", no_argument, NULL, 'd' }, + { "jail", required_argument, NULL, 'j' }, { "only-ipv4", no_argument, NULL, '4' }, { "only-ipv6", no_argument, NULL, '6' }, + { "rootdir", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 }, }; - snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getlocalbase()); + localbase = getlocalbase(); + while (localbase[0] == '/' && localbase[1] == '/') + localbase++; + snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", localbase); - while ((ch = getopt_long(argc, argv, "+:dN46", longopts, NULL)) != -1) { + while ((ch = getopt_long(argc, argv, "+:djNr:46", longopts, NULL)) != -1) { switch (ch) { case 'd': debug++; break; + case 'j': + if ((jid = jail_getid(optarg)) == -1) + err(1, "%s", jail_errmsg); + if (jail_attach(jid) != 0) + err(1, "jail_attach(%s)", optarg); + break; case 'N': activation_test = true; break; + case 'r': + rootdir = optarg; + break; case '4': fetchOpts = "4"; break; @@ -1207,7 +1265,14 @@ main(int argc, char *argv[]) } } - if ((bootstrap_only && force) || access(pkgpath, X_OK) == -1) { + if (rootdir) { + rootd = open(rootdir, O_DIRECTORY | O_SEARCH | O_CLOEXEC); + if (rootd < 0) + err(1, "%s", rootdir); + } + + if ((bootstrap_only && force) || + faccessat(rootd, pkgpath + 1, X_OK, 0) == -1) { struct repository *repo; int ret = 0; /* @@ -1259,7 +1324,8 @@ main(int argc, char *argv[]) if (bootstrap_only) exit(EXIT_SUCCESS); } else if (bootstrap_only) { - printf("pkg already bootstrapped at %s\n", pkgpath); + printf("pkg already bootstrapped at %s%s\n", + rootdir ? rootdir : "", pkgpath); exit(EXIT_SUCCESS); }