Amiga Unix — 68040 · 68060

Manual pages › General commands (1)

apkg(1)

remote package client for Amiga Unix (SVR4)

Package: APKG, added for amigaux.

NAME · SYNOPSIS · DESCRIPTION · VERBS · FILESYSTEM · ESSENTIAL PACKAGES · KERNEL SPACE · OPTIONS · CONFIG FILES · CONFIGURATION · NAME RESOLUTION · ENVIRONMENT · EXIT STATUS · FILES · SEE ALSO · NOTES

apkg - remote package client for Amiga Unix (SVR4)

apkg [ -n ] [ -f ] [ -v ] [ -c conffile ] [ -R root ] [ -y | --conf-keep | --conf-new ] [ --force-remove-essential ] verb [ args ]

apkg fetches SVR4 datastream packages from a remote repository over plain HTTP and drives the local pkgadd(1M) and pkgrm(1M) to install and remove them. Installed state is read from the stock /var/sadm/pkg database, so apkg coexists with the native tools.

Fetch repo/catalog into /var/apkg/catalog (temporary file plus rename). When the repository also publishes catalog.md5, the download is verified against it; a missing (404) catalog.md5 is not an error. A catalog whose first line is not #apkg-catalog 1 is rejected.
List catalog packages, one per line: name, catalog version, an installed marker with the installed version (or (none) for VERSION-less stock packages), category and description. An installed essential package shows as [installed version, essential] (see ESSENTIAL PACKAGES). installed restricts the list to packages present in /var/sadm/pkg, and to packages removed with edited config files kept, which show as [config-files] (see CONFIG FILES).
Case-insensitive substring match against package names and descriptions.
Print the full catalog entry and installed state of one package, with an Essential: line when the installed package is essential.
Fetch the package file into the cache directory and verify its md5 checksum against the catalog. The file stays there until a good install uses it or clean removes it.
Remove every package file from the cache directory and say how many bytes that freed; with -n, only say what it would remove. It takes only files named *.pkg, *.pkg.Z and their .part downloads, and refuses while a transaction is journaled, which recover needs them for. The catalog beside the cache stays.
Print the licence notice an installed package carries, saved as /var/sadm/pkg/pkginst/install/copyright when it was installed, and name the licence files among its files. A package's notice goes with the package: it is never removed apart from it. apkg prints no licence at install or upgrade, with -v too: this verb is how to read one, and a transaction says so at its end when a package left a notice.
Resolve dependencies recursively from the catalog, download and verify every needed package first, then run pkgadd -n -S -a admin -d file pkginst for each in dependency order. Packages already installed at the same version are skipped unless -f is given. A package that is installed at an older version than the catalog's is not silently skipped: if it was named on the command line, install refuses and points at upgrade; if it only arrived as somebody's dependency, the version gap is reported, the dependency counts as satisfied, and nothing is upgraded behind your back. Filenames ending in .Z are decompressed with zcat(1) before being handed to pkgadd. A package that names the filesystems it needs (see FILESYSTEM below) and would land on another is refused before anything is installed.
For each named package (with no names: each installed package) whose catalog version is newer than the installed one, download and verify the new package and then run a single pkgadd over the installed instance, with an instance=overwrite admin(4) file. There is no pkgrm step: nothing is removed before its replacement is in place, so a failed fetch, a failed checksum or a failed pkgadd all leave the installed package exactly where it was. (The earlier pkgrm-then-pkgadd sequence could, and on real hardware did, leave the machine with the package uninstalled and unreplaceable.) An installed package without a VERSION field counts as older than any catalog version. When the installed version is equal or newer, say so and leave that name out (exit 0 when nothing is left).
Several names are ONE transaction: every package is fetched and verified before the first pkgadd, one journal covers them, and a transaction that changes kernel space ends with one kbootset build and commit (so a kernel, the loader and kbootset upgrade into one generation). A name not in the catalog (exit 5) or not installed (exit 1) refuses the whole call before anything is fetched. No dependency is pulled in, with one exception: a kernel brings its set (see KERNEL SPACE).
With no names, every installed package whose catalog version is newer moves, as one transaction ("every installed package is up to date" when there is none). This is the way to take a new kernel cut: the kernel, kbootset, the loader and every module package move together.
The packages of a transaction are installed in this order: kbootset, the other ordinary packages, the loader, the modules, the kernel last. The generation is built once, after the last pkgadd; a failure midway leaves the journal for apkg recover.
name Wrapper around pkgrm -n -a admin. Config files you edited are kept; with --purge it is purge. The name is mapped to its package instance through the catalog when one is available; otherwise it is used as the instance name directly. A kernel-space package is known by its record (see KERNEL SPACE); it is removed with pkgrm of the instance owning its recorded files, its record is deleted, and the next generation is built and committed without it. An essential package is refused (see ESSENTIAL PACKAGES).
Remove the package if it is installed, then delete the config files remove keeps: your edited files, their .new and .old files, their PENDING lines and the package's baselines. A package already removed with its config files kept ([config-files]) is purged without a pkgrm. An installed essential package is refused, as by remove.
list (the default) shows the config files of every package, or of name, that wait with a new default beside them (pending) or that you have edited (edited), also for packages already removed. resolve decides the pending ones: at a terminal it asks file by file, as after an upgrade; with --conf-keep or --conf-new it decides without asking. Without a terminal and without either flag it refuses (exit 1).
Finish a transaction that was interrupted (a power cut, a killed process) or that ended owing work, from the journal /var/apkg/txn: every package not yet done is installed again from its staged datastream (re-checked against its size and catalog hash first) with instance=overwrite, or removed (a remove, or the other kernel package of a line that an install replaces first; see KERNEL SPACE), and a kernel-space transaction ends with the kbootset build and commit. With -f the journal is printed and discarded and nothing else is changed; when that leaves a line without a kernel package (a replacement cut between its removal and the new install) it says so. While a journal exists, install, upgrade and remove refuse to start.
Print the client version.

A package whose pkginfo carries APKG_FSTYPE=ufs (a comma-separated list of filesystem types as statvfs(2) names them) installs only onto those filesystems. Packages with file names longer than 14 bytes carry it, because s5 cuts every name at 14 bytes. Before the first pkgadd of an install or upgrade, apkg checks the directory of every such long name (or, when the package has none, its install base), under the -R target, by its nearest existing directory, and refuses the whole transaction with exit 5 when one is on another filesystem type:

apkg: perl: refused: it installs only on ufs, and
/opt/amix/lib/perl5 is on s5, which cuts file names at 14 bytes

(one line). A malformed value refuses the package with exit 3. -f does not override the check; an unmarked package is never checked.

A package the system cannot boot or run without, such as the base package that owns /etc/inittab and /etc/default/login, carries

APKG_ESSENTIAL=yes

in its pkginfo(5). Removing such a package would delete its files, and its conffile(5) class script deletes every config file still equal to its default. remove and purge (also with -n) refuse an installed essential package before the journal or pkgrm is touched, with exit 5:

apkg: base (pkginst BASE) is essential (APKG_ESSENTIAL=yes): the
system may not boot without it; not removed
(--force-remove-essential overrides)

(one line). The mark is read from the installed /var/sadm/pkg/pkginst/pkginfo, so it is the installed version that decides. no or no line at all means not essential; any other value counts as yes. -f does not override the refusal; --force-remove-essential does, with a warning. Installing over the package, and the swap of one kernel package of a line for another (see KERNEL SPACE), are not removals and are not affected.

Only apkg honours the mark. The engine ignores the parameter, and the stock pkgrm(1M) removes an essential package like any other when asked to directly.

Kernel, loader and module packages are listed in a block of the catalog that every older client skips as comments:

#ext kpkg 1
#kpkg <name> <version> role=kernel|module line=k30|k46

kabi=<tag> pin=<compiler pin> size=<bytes> sha256=<64 hex>
file=<datastream> #kpkg <name> <version> role=loader line=any loader=<n>
size=<bytes> sha256=<64 hex> file=<datastream> #ksig <key-id> <file> #end kpkg

(one #kpkg line per package, every line under 2048 bytes; optional pkginst= and offset=). The keys depend on the role: an entry that lacks a key its role requires, or carries one its role excludes (kabi= or pin= on a loader), is refused. An unknown key is ignored; an unknown key starting with ! refuses that entry. A structural error (an unterminated block, an ordinary entry inside it, a second block, a name used twice) rejects the catalog. A block of another version is skipped.

A kernel-space package is verified by SHA-256 and size after download (userland packages stay md5 and size). There is no signature in this release: a #ksig line is recorded and not acted on, the catalog block is reported "unverified", and every record says verified no.

Before anything is fetched, a transaction is pre-flighted: a module must match the kernel of its line (in the transaction, or installed) in kabi family and major, its minor must not exceed the kernel's, and the compiler pins must be equal; a static kernel takes no modules; a new kernel may not strand an installed module of its line. Every installed module the transaction does not move is checked against the kernel its line will have, in every line, as kbootset's gate 2 checks the generation; each misfit is named with the reason, then one summary:

apkg: mc: kabi k46.1.0 does not match the kernel kset-k (kabi k46.2.0)
apkg: the installed module mc must be upgraded or removed with the kernel kset-k
apkg: 1 installed module would be left behind: mc

A refusal exits 5 and changes nothing. The box's CPU line is checked by kbootset (its gate 10), not here.

A kernel brings its set (apkg 0.3.5). kbootset refuses a generation while an installed module was built with another compiler pin than the kernel, so a new cut moves the kernel, kbootset and every installed module of its line together. When a transaction upgrades a kernel, apkg adds every installed module of its line whose catalog version is newer and fits the new kernel, and the loader and kbootset when the catalog has newer ones; each addition is printed before anything is fetched ("with the kernel kernel-k46-m: upgrading z3660eth-k46 1.1.0-8 -> 1.1.0-9"; -n shows the set and changes nothing). A module the catalog has no fitting version of is left for the pre-flight, which refuses the transaction: remove it, or wait for its package.

A kernel row may carry kbootset=version: the lowest kbootset that composes a generation with it. When neither the transaction's kbootset nor the installed one is that new, the transaction is refused before anything is fetched ("kernel-k46-m needs kbootset 1.1.0.261101-34 or newer; 1.1.0.261031-33 is installed and the catalog has none newer"). Older clients ignore the key.

When kbootset refuses the build at a gate anyway (exit 7), apkg names every installed module that does not fit the installed kernel of its line and what to do (upgrade, or remove it), or says that every module fits and kbootset's own line names the gate. The generation committed before stays in the boot store and still boots.

One kernel package per line. Every kernel package of a line, a static family's and the module kernel's alike, installs /stand/line/unix (and its kabi sidecar). So when a transaction installs a kernel and a kernel record of the same line exists under another package instance, apkg replaces that package in the same journaled transaction. The plan (and -n) says so:

kernel-k46-m replaces kernel-k46-z (KERN46Z): one kernel package per line

The pre-flight then checks the transaction and every installed module against the new kernel; a module that does not fit refuses the transaction before anything changes ("zeth46: built for kabi k46.1.0, the new kernel kernel-k46-z is static: remove the module packages first"), so going back from the module kernel to a static family needs the module packages removed first. Once everything is staged and verified, the other package is removed (pkgrm, then its record; "removed KERN46Z") before the new pkgadd, because both ship the same files; then the new package is installed and recorded, and the kbootset trigger runs once at the end. A cut between the removal and the install leaves the line without a kernel package until apkg recover finishes the install; the box still boots meanwhile, from the boot store's own copies of the kernel. Two or more kernel records of one line (a box damaged by hand) refuse any kernel or module install of that line ("two kernel records for line k46 (KERN46M, KERN46Z): remove one"), as do two kernels of one line in the same transaction. A package of the same instance is an upgrade, as before.

A kernel-space package is pure files: once staged, and before anything is installed, its pkgmap may hold only f and d objects of class none and no install scripts (of the i entries only pkginfo, depend and copyright), and at least one file. A module package may also carry the device nodes of its driver's majors as c entries under /dev/, in full (c none /dev/zen0 51 0 0644 root root), and a block driver's as b entries under /dev/dsk/; pkgadd makes the node and records it in the contents database. A node's class is none or its module's name - the file name of the package's one object in /etc/conf/mod.d/ (c z3660eth /dev/zen0 51 0 0600 root root) - when the package's CLASSES lists it, as the stock pkgadd installs only the listed classes. Anything else (e, v, links, a device node in a kernel or loader package, a character node outside /dev/ or a block node outside /dev/dsk/, a file or directory in a class other than none, a node in another class or in its module's class unlisted, a script) refuses the transaction with exit 3.

After each kernel-space pkgadd, apkg writes the record /var/apkg/kpkg/pkginst — the file is named by the entry's pkginst= (else its name), because an s5 root keeps 14 characters of a file name and a long name and its .new would collide; the full name is the record's name line — (name, version, role, line, then kabi and pin for a kernel or module or loader for the loader, the datastream's sha256, the source, the install time, verified no, and one file path size sha256 line per installed plain file, taken from the contents database and hashed on disk; a module's device node has no bytes and no file line: the contents database records it, and pkgrm removes it). The instance pkgadd installs comes from the datastream's header and must equal pkginst= when the entry gives one.

Kernel-space packages are installed, upgraded and removed only through apkg. The record is apkg's alone: a bare pkgadd or pkgrm never writes or removes it, and a package cannot carry a script that would. kbootset compares every generation member with its record, so a file laid by hand or by a bare pkgadd is refused at the next build ("has no apkg record" or "differs from what package X installed"), and the committed generation keeps booting. From apkg 0.3.6, apkg -f install name lays the catalog's version again over the installed instance (instance=overwrite, the same version too) and writes a new record; with no record at all a plain install does the same. A package not in the repository's catalog goes into a local repository first; see repo under CONFIGURATION.

After a transaction that installed or removed kernel space, and only when every package in it is done, apkg calls kbootset build and then kbootset commit (two calls, with -R target under -R and an explicit --journal). apkg never writes a boot slice itself. With bootset=manual in /etc/default/bootset it prints the two commands instead. On success it repeats kbootset's last line ("generation N committed; power-cycle to boot it"). A refusal (kbootset status 3 or 4) exits 7 with kbootset's line and "the committed generation is unchanged"; an error (status 1 or 2), or a kbootset that cannot run, exits 6 and keeps the journal, so apkg recover runs the build and commit again. A failure after a good build leaves the built generation uncommitted.

Dry run: print what would be fetched and which commands would run, change nothing.
Force: reinstall the packages named on the command line even when they are already installed, over the installed instance (instance=overwrite, the same version too). It does not propagate to their dependencies - an already-satisfied dependency is left alone (forcing the whole closure meant re-installing the multi- megabyte engine package on every run, minutes of pointless work on a 68k). A dependency that is genuinely missing is still installed.
Verbose: progress notes, HTTP details, exact command lines.
Read configuration from conffile instead of /etc/apkg.conf.
Install into, remove from and read the state of the system under root (the engine runs with -R root); see FILESYSTEM.
Keep every edited config file without asking, and delete the new default's .new file (see CONFIG FILES). For batch runs and the installer.
Take every new config default without asking; your file is saved as file.old.
Let remove and purge remove an essential package (see ESSENTIAL PACKAGES). Like the other long options it comes before the verb.

A package may mark files you edit in place, such as /etc/apkg.conf, as config files (conffile(5)). The package's own scripts keep an edited one in every pkgadd and write the new default beside it as file.new; the file is then listed in /var/sadm/conffiles/PENDING. The package tools cannot ask anything while they run, so apkg asks afterwards. After an install or upgrade it lists the pending files of the packages it installed and then, per file:

•
at a terminal, with no flag, asks: keep yours (the default answer), install the new default, show the diff of yours against it, or decide later;
•
with -y or --conf-keep, keeps yours;
•
with --conf-new, installs the new default;
•
otherwise (no terminal, no flag) changes nothing: the file stays pending for apkg conffiles resolve.

Keeping yours deletes the .new file; the default stays in the package's baseline under /var/sadm/conffiles. Taking the new default renames yours to file.old, moves the .new file into place and records it in the package database with installf -c conffile. A file you have edited is asked about only when the package's default changed. remove keeps the files you edited; purge deletes them.

/etc/apkg.conf holds key=value lines; # starts a comment (apkg.conf(5)). It is a conffile(5) of the APKG package: an upgrade installs the new default over a file you never edited, and keeps one you did, writing the new default beside it as /etc/apkg.conf.new. The default of the installed version is also /usr/apkg/lib/apkg.conf. A key the file does not name takes its built-in default. Keys and defaults:

Repository base URL (default http://pkg.amigaux.org/1.1). Each Amix release has its own tree on the host, its catalog and pkgs/ under http://pkg.amigaux.org/release (/1.0 for 1.0, /1.1 for 1.1); the base URL names the release's tree, and apkg fetches repo/catalog and repo/pkgs/filename below it. One trailing slash is ignored. A local repository has the same layout: the datastreams in dir/pkgs/, gen-catalog.py -o dir dir/pkgs on the host that made them, and repo=file://dir or repo=dir. The host is a name-based virtual host, so the repository must be named by hostname; a bare IP address is answered 404. The name is resolved through /etc/inet/hosts, nameserver below or /etc/resolv.conf (see NAME RESOLUTION). The catalog is fetched from repo/catalog and packages from repo/pkgs/filename.
Package download cache (default /var/apkg/cache). The catalog lives next to it, in the cache directory's parent. A good install or upgrade removes the packages it fetched from here once its journal has cleared; a failed one keeps them for recover or another try. The directory is apkg's own: do not point it at a repository.
1 keeps the fetched packages after a good transaction (default 0).
pkgadd binary (default /usr/apkg/bin/pkgadd; falls back to /usr/sbin/pkgadd when the default is missing). The stock /usr/sbin/pkgadd refuses any package whose pkginfo carries BASEDIR (“requires a base directory”, interaction required), whatever the admin file says, so such a package installs only through APKGENG's pkgadd. It also stops to ask about files another package owns and about install scripts, even with conflict=nocheck and action=nocheck, so under it such a package does not install non-interactively.
pkgrm binary (default /usr/apkg/bin/pkgrm, fallback /usr/sbin/pkgrm). The stock /usr/sbin/pkgrm does not remove a package that has removal scripts, conffiles included, without a person to answer it.
admin(4) file passed to pkgadd/pkgrm with -a (default /var/apkg/admin). apkg creates this file if it is missing - it is shipped by the APKG package itself, so a pkgrm APKG deletes it if you never edited it, and an engine call that merely assumed it was there would fail with “unable to open admin file”. An existing file is never rewritten, and it is a conffile(5) like /etc/apkg.conf, so an upgrade of APKG keeps your edits (see admin(5)).Foran upgrade (and for a -f install over a different version) apkg derives a second file, admin.overwrite, from it: every key you set is carried across and only instance= is replaced with overwrite.

The remaining keys tune the fetch path. Every constant that path uses is one of these - there is no compiled-in number an operator cannot move.

The generation composer called after a kernel-space transaction; default /sbin/kbootset. Like pkgadd it runs from this system and is told -R target under -R.
The --journal path handed to kbootset; empty (the default) means target/var/kbootset/journal, kbootset's own default, passed explicitly.
Seconds of network silence tolerated during a transfer (default 300). This is an inactivity timeout, not a cap on how long a transfer may take: the timer is re-armed by every chunk that arrives, so a slow but flowing transfer never trips it however long it runs. A multi-megabyte package over a period link is minutes of perfectly healthy transfer; size it against the longest legitimate gap instead.
Seconds allowed for host resolution plus the TCP connect (default 30). Separate from timeout so a dead host fails in seconds rather than inheriting the patience a mid-transfer stall needs.
Extra attempts after a network failure (default 2; 0 disables). A non-200 HTTP status is the server answering, not a failure, and is never retried.
Seconds before the first retry (default 5); it doubles before each further one. 0 retries immediately.
Bytes between progress lines on stderr (default 262144). 0 silences them; -v quarters the interval. Nothing is printed for a transfer smaller than one interval, so ordinary runs stay quiet.
Socket read buffer in bytes (default 8192). Clamped to the range 512-262144; a value outside it is clamped with a warning.
Number of 301/302 hops followed (default 1). 0 refuses to follow any.
The TCP receive window, in bytes. The default is 0 - leave the stack's window alone - and that is a measured result, not caution.
Amix's compiled-in 4096-byte window looks exactly like the thing capping a single stream on a bandwidth-delay-bound path (throughput is at most window divided by round-trip time). It is not. Two server-side packet captures of a real 68LC060 A4000D fetching from pkg.amigaux.org over the Z3660 ethernet driver (2026-09-14/15) found:
  • SO_RCVBUF 64 KB requested before connect(3N); the box advertised win 4096 in its SYN and in all 2999 steady-state ACKs. The socket option does not reach this tcp module at all.
  • Raising the window really does work through the ioctl below - 16384 was seen on the wire - and it is slower: 4096 gives about 54 KB/s, 8192 about 32 KB/s, 16384 about 36-40 KB/s, with server-side retransmits rising from 1.0% to 3.6%.
  • The cause is an RX-path loss cliff at roughly 8 segments in flight (about 10 KB outstanding): up to 7 segments per burst lose 2-3%, 8 lose about 60%, 9 or more lose about 100%, and every loss costs a server retransmission timeout. The stock 4096 window admits at most 8 segments (7 x 536 + 344) and so sits exactly at the loss-free edge - near-optimal for this driver as it stands.
The real lever is the RX drop mechanism (the Z3660 firmware discarding its whole backlog on overflow, and the 16.67 ms poll quantum), and it is not in this client. Do not raise this expecting speed.
Setting it above 0 is an operator knob, and it does two things:
1.
STREAMS INITQPARMS. apkg issues the privileged INITQPARMS ioctl (from <net/strioc.h>, iqp_type = IQP_RQ|IQP_HIWAT) with I_STR(7) on a TCP stream, asking the tcp module for a receive high-water mark of rcvbuf. This is the mechanism that actually moves the window. It requires root - apkg install already runs as root, and a non-root apkg falls back to mechanism 2 and says so. It rewrites the tcp module's default high-water mark, not this one stream's, so every TCP stream opened on the box afterwards inherits it - box-wide, not apkg wide, and standing until reboot. apkg therefore says so at warning level, not only under -v. Nothing can read the previous value back (the ioctl is set-only, and sockmod's cached SO_RCVBUF starts at 0 rather than at the module's high-water mark), so apkg does not restore it: put it back with a root run at rcvbuf=4096, or by rebooting. The value is capped at 65535 because the ioctl carries a 16-bit field.
2.
SO_RCVBUF requested with setsockopt(3N) before connect(3N), kept as the unprivileged fallback and for non-Amix hosts. On Amix this alone does not reach the tcp module. The getsockopt(3N) value -v prints can come from sockmod's own cached copy while TCP below it still runs at 4096; read that line as "what was asked, and what the socket layer says", never as "what TCP is using".
If you do set it, 16384 is the highest worth considering. Nothing above 65535 can be advertised in any case (Amix predates RFC 1323, so there is no window scaling), and the practical limit is lower still: MSS to a non-local host is pinned at 536 and the Z3660 firmware's RX ring has 32 usable slots, so past 32 x 536, about 17 KB, the ring itself overruns - and that firmware discards its entire queued backlog rather than back-pressuring, which TCP sees as a full-window loss and a retransmission timeout.
SO_SNDBUF in bytes (default 0, i.e. leave the stack default). apkg sends requests and receives bodies, so this has no effect on fetch throughput; it is here because the receive side is configurable and an asymmetric pair would be a trap.
IPv4 address of a nameserver for apkg's stub resolver. The default list is empty; apkg then asks the servers /etc/resolv.conf names, if it names any. The key is repeatable and the servers are tried in the order written; a line with an empty value clears the list. A :port suffix is accepted after the address and exists for testing; leave it off. A name is not accepted here - resolving the resolver is a bootstrap problem. See NAME RESOLUTION below.
Seconds to wait for one DNS query attempt (default 5). Not per name and not per nameserver: a UDP query comes back quickly or it is lost.
Extra attempts per nameserver (default 2). Every configured nameserver is asked in turn regardless, so a name nothing answers costs at most nameservers x (dns_retries + 1) x dns_timeout seconds. A server that answers no such name has answered: it is not retried.

Stock Amix has no resolver. The gethostbyname(3N) apkg links reads /etc/inet/hosts and nothing else, also on a box with DNS turned on. The package repository is a name-based virtual host - a request whose Host: header carries a bare IP address is answered 404 - so the repository has to be named by hostname.

apkg therefore carries its own stub resolver. A host name is resolved in three steps:

1.
an IPv4 literal is used as it stands;
2.
the host database is consulted - that is gethostbyname(3N), i.e. /etc/inet/hosts on Amix;
3.
only if that says nothing, each configured nameserver is asked, in order; with none configured, each nameserver line of /etc/resolv.conf, which is what a box with DNS turned on has (from apkg 0.3.7). Under -R, that is the file of the system apkg runs on, not the target's.

A nameserver in the configuration is the override: the file is then not read, also when those servers do not answer. With no hosts line and no name server in either place, apkg says so and stops.

The hosts file is consulted first on purpose: a box that resolves today cannot regress when a nameserver is configured, and a line an operator wrote by hand can never be shadowed by DNS. So a hosts line whose address has gone stale still wins: remove it to let DNS answer.

The resolver is a stub in the strict sense: one UDP query per attempt, A records only, recursion requested, no EDNS, no cache beyond the process, no search list and no TCP fallback. It follows a CNAME chain to an A record and ignores TTLs. A reply that does not match the query it answers - wrong transaction id, wrong question, malformed, or a compression pointer that does not point backwards - is discarded and the next attempt or nameserver is tried; a partial answer is never used. Root privilege is not required.

It resolves apkg's own lookups and nothing else. telnet(1), ftp(1) and the rest of the box still read /etc/inet/hosts and are entirely unaffected by nameserver. Keep that file as you need it.

-v reports which of the three steps answered, and for DNS which nameserver did.

Prefix prepended to every compiled-in default path (/etc/apkg.conf, /var/apkg/..., /var/sadm/pkg, and the pkgadd/pkgrm defaults with their fallbacks). Lets tests and image builds point the whole client at a fake root. Values read from the configuration file are never prefixed.

0
Success (including "nothing to do").
1
Usage or configuration error.
2
Network failure (name not resolved by the hosts file or any nameserver, connect, inactivity timeout with the configured retries exhausted, HTTP error status).
3
Verification failure: md5, sha256 or size mismatch, bad catalog header, malformed catalog, a refused kernel-space entry, a datastream whose header contradicts the catalog, a kernel-space datastream that is not pure files, a malformed APKG_FSTYPE.
4
pkgadd/pkgrm (or the zcat decompression step) failed, or installf failed to record a new config default taken with --conf-new.
5
Dependency cycle, or a dependency (or install target) missing from the catalog; a kernel-space pre-flight refusal (kabi, pin, line); a package refused for the filesystem it would land on (see FILESYSTEM); the removal of an essential package refused (see ESSENTIAL PACKAGES).
6
kbootset could not run, failed (status 1) or rejected its arguments (status 2). The packages are installed; the journal is kept for apkg recover.
7
kbootset refused the build or commit by a rule (status 3) or at a gate (status 4); the committed generation is unchanged.

/etc/apkg.conf
configuration; a conffile, kept by an upgrade when edited
/etc/apkg.conf.new
the new default, when an upgrade kept your edited file
/usr/apkg/lib/apkg.conf
the shipped default of the installed version (not read by apkg)
/var/apkg/catalog
local copy of the repository catalog
/var/apkg/cache
downloaded packages
/var/apkg/admin
permissive admin(5) file used for non-interactive pkgadd/pkgrm; created on demand if absent; a conffile, kept by an upgrade when edited
/var/sadm/conffiles/PENDING
the conffiles whose new default waits in a .new file; see conffile(5)
/var/sadm/conffiles/pkginst/
the baselines: the config defaults each package last shipped
a new default waiting beside your file; your file after --conf-new
/var/apkg/admin.overwrite
derived from the above with instance=overwrite, for the upgrade verb; rewritten on every use
/etc/inet/hosts
the host database, consulted before any nameserver (read only, and never written by apkg)
/etc/resolv.conf
the nameservers to ask when nameserver names none (read only)
/var/sadm/pkg/*/pkginfo
installed-package state (read only)
/var/sadm/install/contents
the files of each installed kernel-space package (read only)
/var/apkg/kpkg/*
apkg's install records of kernel-space packages; kbootset reads them
/var/apkg/txn
the transaction journal
/etc/default/bootset
bootset=auto (the default) or manual

pkgadd(1M), pkgrm(1M), pkginfo(1), pkgtrans(1), apkg.conf(5), admin(5), conffile(5), kbootset(1M)

apkg prints no licence at install or upgrade: it hands pkgadd -S to the engine, which still saves a package's notice as /var/sadm/pkg/pkginst/install/copyright; apkg licence reads it. The stock /usr/sbin/pkgadd refuses -S, so it is not passed there; run with -n as apkg runs it, that engine prints no notice either.

Only plain http:// URLs are supported; the transport uses HTTP/1.0 with a Host: header and follows at most one 301/302 redirect.

November 2026 apkg 0.3.11