diff options
Diffstat (limited to 'usr.sbin')
35 files changed, 1691 insertions, 2808 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index 51908818e550..dcfe2037f8ce 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -56,7 +56,6 @@ SUBDIR= adduser \ nfsuserd \ nmtree \ nologin \ - nvmfd \ pciconf \ periodic \ pnfsdscopymr \ diff --git a/usr.sbin/autofs/common.c b/usr.sbin/autofs/common.c index 18756752876c..2dd7c290cebc 100644 --- a/usr.sbin/autofs/common.c +++ b/usr.sbin/autofs/common.c @@ -149,10 +149,11 @@ create_directory(const char *path) error = mkdir(partial, 0755); if (error != 0 && errno != EEXIST) { log_warn("cannot create %s", partial); - return; + break; } } + free(partial); free(tofree); } diff --git a/usr.sbin/bhyve/pci_xhci.c b/usr.sbin/bhyve/pci_xhci.c index 0871bbb87fe5..ff12e40359e2 100644 --- a/usr.sbin/bhyve/pci_xhci.c +++ b/usr.sbin/bhyve/pci_xhci.c @@ -406,7 +406,7 @@ pci_xhci_usbcmd_write(struct pci_xhci_softc *sc, uint32_t cmd) * XHCI 4.19.3 USB2 RxDetect->Polling, * USB3 Polling->U0 */ - if (dev->dev_ue->ue_usbver == 2) + if (dev->hci.hci_usbver == 2) port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL); else @@ -2590,7 +2590,7 @@ pci_xhci_reset_port(struct pci_xhci_softc *sc, int portn, int warm) port->portsc |= XHCI_PS_PED | XHCI_PS_SPEED_SET(dev->hci.hci_speed); - if (warm && dev->dev_ue->ue_usbver == 3) { + if (warm && dev->hci.hci_usbver == 3) { port->portsc |= XHCI_PS_WRC; } @@ -2620,7 +2620,7 @@ pci_xhci_init_port(struct pci_xhci_softc *sc, int portn) port->portsc = XHCI_PS_CCS | /* connected */ XHCI_PS_PP; /* port power */ - if (dev->dev_ue->ue_usbver == 2) { + if (dev->hci.hci_usbver == 2) { port->portsc |= XHCI_PS_PLS_SET(UPS_PORT_LS_POLL) | XHCI_PS_SPEED_SET(dev->hci.hci_speed); } else { @@ -2785,8 +2785,8 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl) cookie = NULL; while ((name = nvlist_next(slots_nvl, &type, &cookie)) != NULL) { - if (usb2_port == ((sc->usb2_port_start) + XHCI_MAX_DEVS/2) || - usb3_port == ((sc->usb3_port_start) + XHCI_MAX_DEVS/2)) { + if (usb2_port == ((sc->usb2_port_start) + XHCI_MAX_DEVS / 2) || + usb3_port == ((sc->usb3_port_start) + XHCI_MAX_DEVS / 2)) { WPRINTF(("pci_xhci max number of USB 2 or 3 " "devices reached, max %d", XHCI_MAX_DEVS/2)); goto bad; @@ -2834,12 +2834,25 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl) dev->hci.hci_intr = pci_xhci_dev_intr; dev->hci.hci_event = pci_xhci_dev_event; dev->hci.hci_speed = USB_SPEED_MAX; + dev->hci.hci_usbver = -1; - if (ue->ue_usbver == 2) { + devsc = ue->ue_probe(&dev->hci, nvl); + if (devsc == NULL) { + free(dev); + goto bad; + } + dev->dev_sc = devsc; + + if (dev->hci.hci_usbver == -1) + dev->hci.hci_usbver = ue->ue_usbver; + + if (dev->hci.hci_usbver == 2) { if (usb2_port == sc->usb2_port_start + XHCI_MAX_DEVS / 2) { WPRINTF(("pci_xhci max number of USB 2 devices " "reached, max %d", XHCI_MAX_DEVS / 2)); + free(dev->dev_sc); + free(dev); goto bad; } dev->hci.hci_port = usb2_port; @@ -2849,6 +2862,8 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl) XHCI_MAX_DEVS / 2) { WPRINTF(("pci_xhci max number of USB 3 devices " "reached, max %d", XHCI_MAX_DEVS / 2)); + free(dev->dev_sc); + free(dev); goto bad; } dev->hci.hci_port = usb3_port; @@ -2857,13 +2872,10 @@ pci_xhci_parse_devices(struct pci_xhci_softc *sc, nvlist_t *nvl) XHCI_DEVINST_PTR(sc, dev->hci.hci_port) = dev; dev->hci.hci_address = 0; - devsc = ue->ue_init(&dev->hci, nvl); - if (devsc == NULL) { + if (ue->ue_init(dev->dev_sc)) goto bad; - } dev->dev_ue = ue; - dev->dev_sc = devsc; if (dev->hci.hci_speed == USB_SPEED_MAX) dev->hci.hci_speed = ue->ue_usbspeed; @@ -2885,6 +2897,8 @@ portsfinal: bad: for (i = 1; i <= XHCI_MAX_DEVS; i++) { + if (XHCI_DEVINST_PTR(sc, i) != NULL) + free(XHCI_DEVINST_PTR(sc, i)->dev_sc); free(XHCI_DEVINST_PTR(sc, i)); } @@ -3232,6 +3246,7 @@ pci_xhci_snapshot(struct vm_snapshot_meta *meta) SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_address, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_port, meta, ret, done); SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_speed, meta, ret, done); + SNAPSHOT_VAR_OR_LEAVE(dev->hci.hci_usbver, meta, ret, done); } SNAPSHOT_VAR_OR_LEAVE(sc->usb2_port_start, meta, ret, done); diff --git a/usr.sbin/bhyve/usb_emul.h b/usr.sbin/bhyve/usb_emul.h index 85dedfeacd3b..43b6b53b5205 100644 --- a/usr.sbin/bhyve/usb_emul.h +++ b/usr.sbin/bhyve/usb_emul.h @@ -52,7 +52,8 @@ struct usb_devemu { int ue_usbspeed; /* usb device speed */ /* instance creation */ - void *(*ue_init)(struct usb_hci *hci, nvlist_t *nvl); + void *(*ue_probe)(struct usb_hci *hci, nvlist_t *nvl); + int (*ue_init)(void *sc); /* handlers */ int (*ue_request)(void *sc, struct usb_data_xfer *xfer); @@ -86,6 +87,7 @@ struct usb_hci { int hci_address; int hci_port; int hci_speed; + int hci_usbver; }; /* diff --git a/usr.sbin/bhyve/usb_mouse.c b/usr.sbin/bhyve/usb_mouse.c index a37941c0cd9d..82b1159d5f61 100644 --- a/usr.sbin/bhyve/usb_mouse.c +++ b/usr.sbin/bhyve/usb_mouse.c @@ -295,20 +295,28 @@ umouse_event(uint8_t button, int x, int y, void *arg) } static void * -umouse_init(struct usb_hci *hci, nvlist_t *nvl __unused) +umouse_probe(struct usb_hci *hci, nvlist_t *nvl __unused) { struct umouse_softc *sc; sc = calloc(1, sizeof(struct umouse_softc)); sc->hci = hci; + return (sc); +} + +static int +umouse_init(void *scarg) +{ + struct umouse_softc *sc = (struct umouse_softc *)scarg; + sc->hid.protocol = 1; /* REPORT protocol */ pthread_mutex_init(&sc->mtx, NULL); pthread_mutex_init(&sc->ev_mtx, NULL); console_ptr_register(umouse_event, sc, 10); - return (sc); + return (0); } #define UREQ(x,y) ((x) | ((y) << 8)) @@ -811,6 +819,7 @@ static struct usb_devemu ue_mouse = { .ue_emu = "tablet", .ue_usbver = 3, .ue_usbspeed = USB_SPEED_HIGH, + .ue_probe = umouse_probe, .ue_init = umouse_init, .ue_request = umouse_request, .ue_data = umouse_data_handler, diff --git a/usr.sbin/certctl/Makefile b/usr.sbin/certctl/Makefile index 88c024daf7e6..6900f0ce3b65 100644 --- a/usr.sbin/certctl/Makefile +++ b/usr.sbin/certctl/Makefile @@ -1,5 +1,14 @@ +.include <src.opts.mk> + PACKAGE= certctl -SCRIPTS=certctl.sh +PROG= certctl MAN= certctl.8 +LIBADD= crypto +HAS_TESTS= +SUBDIR.${MK_TESTS}= tests + +.ifdef BOOTSTRAPPING +CFLAGS+=-DBOOTSTRAPPING +.endif .include <bsd.prog.mk> diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8 index 7e49bb89e2ac..edf993e1361a 100644 --- a/usr.sbin/certctl/certctl.8 +++ b/usr.sbin/certctl/certctl.8 @@ -24,7 +24,7 @@ .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 17, 2025 +.Dd August 18, 2025 .Dt CERTCTL 8 .Os .Sh NAME @@ -32,63 +32,85 @@ .Nd "tool for managing trusted and untrusted TLS certificates" .Sh SYNOPSIS .Nm -.Op Fl v +.Op Fl lv .Ic list .Nm -.Op Fl v +.Op Fl lv .Ic untrusted .Nm -.Op Fl cnUv +.Op Fl BnUv .Op Fl D Ar destdir .Op Fl M Ar metalog .Ic rehash .Nm -.Op Fl cnv -.Ic untrust Ar file +.Op Fl nv +.Ic untrust Ar .Nm -.Op Fl cnv -.Ic trust Ar file +.Op Fl nv +.Ic trust Ar .Sh DESCRIPTION The .Nm utility manages the list of TLS Certificate Authorities that are trusted by applications that use OpenSSL. .Pp -Flags: +The following options are available: .Bl -tag -width 4n -.It Fl c -Copy certificates instead of linking to them. +.It Fl B +Do not generate a bundle. +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl D Ar destdir Specify the DESTDIR (overriding values from the environment). .It Fl d Ar distbase Specify the DISTBASE (overriding values from the environment). +.It Fl l +When listing installed (trusted or untrusted) certificates, show the +full path and distinguished name for each certificate. .It Fl M Ar metalog -Specify the path of the METALOG file (default: $DESTDIR/METALOG). +Specify the path of the METALOG file +.Po +default: +.Pa ${DESTDIR}/METALOG +.Pc . +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl n -No-Op mode, do not actually perform any actions. +Dry-run mode. +Do not actually perform any actions except write the metalog. .It Fl v -Be verbose, print details about actions before performing them. +Verbose mode. +Print detailed information about each action taken. .It Fl U -Unprivileged mode, do not change the ownership of created links. -Do record the ownership in the METALOG file. +Unprivileged mode. +Do not attempt to set the ownership of created files. +This option is only valid in conjunction with the +.Fl M +option and the +.Ic rehash +command. .El .Pp Primary command functions: .Bl -tag -width untrusted .It Ic list -List all currently trusted certificate authorities. +List all currently trusted certificates. .It Ic untrusted List all currently untrusted certificates. .It Ic rehash -Rebuild the list of trusted certificate authorities by scanning all directories +Rebuild the list of trusted certificates by scanning all directories in .Ev TRUSTPATH and all untrusted certificates in .Ev UNTRUSTPATH . -A symbolic link to each trusted certificate is placed in +A copy of each trusted certificate is placed in .Ev CERTDESTDIR and each untrusted certificate in .Ev UNTRUSTDESTDIR . +In addition, a bundle containing the trusted certificates is placed in +.Ev BUNDLEFILE . .It Ic untrust Add the specified file to the untrusted list. .It Ic trust @@ -97,9 +119,13 @@ Remove the specified file from the untrusted list. .Sh ENVIRONMENT .Bl -tag -width UNTRUSTDESTDIR .It Ev DESTDIR -Alternate destination directory to operate on. +Absolute path to an alternate destination directory to operate on +instead of the file system root, e.g. +.Dq Li /tmp/install . .It Ev DISTBASE Additional path component to include when operating on certificate directories. +This must start with a slash, e.g. +.Dq Li /base . .It Ev LOCALBASE Location for local programs. Defaults to the value of the user.localbase sysctl which is usually @@ -107,32 +133,34 @@ Defaults to the value of the user.localbase sysctl which is usually .It Ev TRUSTPATH List of paths to search for trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/trusted -.Pa <DESTDIR><DISTBASE>/usr/local/share/certs -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/certs +.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs .It Ev UNTRUSTPATH List of paths to search for untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/blacklisted -.It Ev CERTDESTDIR +.Pa ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/untrusted +.It Ev TRUSTDESTDIR Destination directory for symbolic links to trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/certs +.Pa ${DESTDIR}${DISTBASE}/etc/ssl/certs .It Ev UNTRUSTDESTDIR Destination directory for symbolic links to untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/untrusted -.It Ev EXTENSIONS -List of file extensions to read as certificate files. -Default: *.pem *.crt *.cer *.crl *.0 +.Pa ${DESTDIR}${DISTBASE}/etc/ssl/untrusted +.It Ev BUNDLE +File name of bundle to produce. .El .Sh SEE ALSO .Xr openssl 1 .Sh HISTORY .Nm first appeared in -.Fx 12.2 +.Fx 12.2 . .Sh AUTHORS -.An Allan Jude Aq Mt allanjude@freebsd.org +.An -nosplit +The original shell implementation was written by +.An Allan Jude Aq Mt allanjude@FreeBSD.org . +The current C implementation was written by +.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c new file mode 100644 index 000000000000..ed7f05126ca7 --- /dev/null +++ b/usr.sbin/certctl/certctl.c @@ -0,0 +1,1114 @@ +/*- + * Copyright (c) 2023-2025 Dag-Erling Smørgrav <des@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/sysctl.h> +#include <sys/stat.h> +#include <sys/tree.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/ssl.h> + +#define info(fmt, ...) \ + do { \ + if (verbose) \ + fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ + } while (0) + +static char * +xasprintf(const char *fmt, ...) +{ + va_list ap; + char *str; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0 || str == NULL) + err(1, NULL); + return (str); +} + +static char * +xstrdup(const char *str) +{ + char *dup; + + if ((dup = strdup(str)) == NULL) + err(1, NULL); + return (dup); +} + +static void usage(void); + +static bool dryrun; +static bool longnames; +static bool nobundle; +static bool unprivileged; +static bool verbose; + +static const char *localbase; +static const char *destdir; +static const char *distbase; +static const char *metalog; + +static const char *uname = "root"; +static const char *gname = "wheel"; + +static const char *const default_trusted_paths[] = { + "/usr/share/certs/trusted", + "%L/share/certs/trusted", + "%L/share/certs", + NULL +}; +static char **trusted_paths; + +static const char *const default_untrusted_paths[] = { + "/usr/share/certs/untrusted", + "%L/share/certs/untrusted", + NULL +}; +static char **untrusted_paths; + +static char *trusted_dest; +static char *untrusted_dest; +static char *bundle_dest; + +#define SSL_PATH "/etc/ssl" +#define TRUSTED_DIR "certs" +#define TRUSTED_PATH SSL_PATH "/" TRUSTED_DIR +#define UNTRUSTED_DIR "untrusted" +#define UNTRUSTED_PATH SSL_PATH "/" UNTRUSTED_DIR +#define LEGACY_DIR "blacklisted" +#define LEGACY_PATH SSL_PATH "/" LEGACY_DIR +#define BUNDLE_FILE "cert.pem" +#define BUNDLE_PATH SSL_PATH "/" BUNDLE_FILE + +static FILE *mlf; + +/* + * Remove duplicate and trailing slashes from a path. + */ +static char * +normalize_path(const char *str) +{ + char *buf, *dst; + + if ((buf = malloc(strlen(str) + 1)) == NULL) + err(1, NULL); + for (dst = buf; *str != '\0'; dst++) { + if ((*dst = *str++) == '/') { + while (*str == '/') + str++; + if (*str == '\0') + break; + } + } + *dst = '\0'; + return (buf); +} + +/* + * Split a colon-separated list into a NULL-terminated array. + */ +static char ** +split_paths(const char *str) +{ + char **paths; + const char *p, *q; + unsigned int i, n; + + for (p = str, n = 1; *p; p++) { + if (*p == ':') + n++; + } + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (p = q = str, i = 0; i < n; i++, p = q + 1) { + q = strchrnul(p, ':'); + if ((paths[i] = strndup(p, q - p)) == NULL) + err(1, NULL); + } + return (paths); +} + +/* + * Expand %L into LOCALBASE and prefix DESTDIR and DISTBASE as needed. + */ +static char * +expand_path(const char *template) +{ + if (template[0] == '%' && template[1] == 'L') + return (xasprintf("%s%s%s", destdir, localbase, template + 2)); + return (xasprintf("%s%s%s", destdir, distbase, template)); +} + +/* + * Expand an array of paths. + */ +static char ** +expand_paths(const char *const *templates) +{ + char **paths; + unsigned int i, n; + + for (n = 0; templates[n] != NULL; n++) + continue; + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (i = 0; i < n; i++) + paths[i] = expand_path(templates[i]); + return (paths); +} + +/* + * If destdir is a prefix of path, returns a pointer to the rest of path, + * otherwise returns path. + * + * Note that this intentionally does not strip distbase from the path! + * Unlike destdir, distbase is expected to be included in the metalog. + */ +static const char * +unexpand_path(const char *path) +{ + const char *p = path; + const char *q = destdir; + + while (*p && *p == *q) { + p++; + q++; + } + return (*q == '\0' && *p == '/' ? p : path); +} + +/* + * X509 certificate in a rank-balanced tree. + */ +struct cert { + RB_ENTRY(cert) entry; + unsigned long hash; + char *name; + X509 *x509; + char *path; +}; + +static void +free_cert(struct cert *cert) +{ + free(cert->name); + X509_free(cert->x509); + free(cert->path); + free(cert); +} + +static int +certcmp(const struct cert *a, const struct cert *b) +{ + return (X509_cmp(a->x509, b->x509)); +} + +RB_HEAD(cert_tree, cert); +static struct cert_tree trusted = RB_INITIALIZER(&trusted); +static struct cert_tree untrusted = RB_INITIALIZER(&untrusted); +RB_GENERATE_STATIC(cert_tree, cert, entry, certcmp); + +static void +free_certs(struct cert_tree *tree) +{ + struct cert *cert, *tmp; + + RB_FOREACH_SAFE(cert, cert_tree, tree, tmp) { + RB_REMOVE(cert_tree, tree, cert); + free_cert(cert); + } +} + +static struct cert * +find_cert(struct cert_tree *haystack, X509 *x509) +{ + struct cert needle = { .x509 = x509 }; + + return (RB_FIND(cert_tree, haystack, &needle)); +} + +/* + * File containing a certificate in a rank-balanced tree sorted by + * certificate hash and disambiguating counter. This is needed because + * the certificate hash function is prone to collisions, necessitating a + * counter to distinguish certificates that hash to the same value. + */ +struct file { + RB_ENTRY(file) entry; + const struct cert *cert; + unsigned int c; +}; + +static int +filecmp(const struct file *a, const struct file *b) +{ + if (a->cert->hash > b->cert->hash) + return (1); + if (a->cert->hash < b->cert->hash) + return (-1); + return (a->c - b->c); +} + +RB_HEAD(file_tree, file); +RB_GENERATE_STATIC(file_tree, file, entry, filecmp); + +/* + * Lexicographical sort for scandir(). + */ +static int +lexisort(const struct dirent **d1, const struct dirent **d2) +{ + return (strcmp((*d1)->d_name, (*d2)->d_name)); +} + +/* + * Read certificate(s) from a single file and insert them into a tree. + * Ignore certificates that already exist in the tree. If exclude is not + * null, also ignore certificates that exist in exclude. + * + * Returns the number certificates added to the tree, or -1 on failure. + */ +static int +read_cert(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + FILE *f; + X509 *x509; + X509_NAME *name; + struct cert *cert; + unsigned long hash; + int len, ni, no; + + if ((f = fopen(path, "r")) == NULL) { + warn("%s", path); + return (-1); + } + for (ni = no = 0; + (x509 = PEM_read_X509(f, NULL, NULL, NULL)) != NULL; + ni++) { + hash = X509_subject_name_hash(x509); + if (exclude && find_cert(exclude, x509)) { + info("%08lx: excluded", hash); + X509_free(x509); + continue; + } + if (find_cert(tree, x509)) { + info("%08lx: duplicate", hash); + X509_free(x509); + continue; + } + if ((cert = calloc(1, sizeof(*cert))) == NULL) + err(1, NULL); + cert->x509 = x509; + name = X509_get_subject_name(x509); + cert->hash = X509_NAME_hash_ex(name, NULL, NULL, NULL); + len = X509_NAME_get_text_by_NID(name, NID_commonName, + NULL, 0); + if (len > 0) { + if ((cert->name = malloc(len + 1)) == NULL) + err(1, NULL); + X509_NAME_get_text_by_NID(name, NID_commonName, + cert->name, len + 1); + } else { + /* fallback for certificates without CN */ + cert->name = X509_NAME_oneline(name, NULL, 0); + } + cert->path = xstrdup(unexpand_path(path)); + if (RB_INSERT(cert_tree, tree, cert) != NULL) + errx(1, "unexpected duplicate"); + info("%08lx: %s", cert->hash, cert->name); + no++; + } + /* + * ni is the number of certificates we found in the file. + * no is the number of certificates that weren't already in our + * tree or on the exclusion list. + */ + if (ni == 0) + warnx("%s: no valid certificates found", path); + fclose(f); + return (no); +} + +/* + * Load all certificates found in the specified path into a tree, + * optionally excluding those that already exist in a different tree. + * + * Returns the number of certificates added to the tree, or -1 on failure. + */ +static int +read_certs(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + struct stat sb; + char *paths[] = { (char *)(uintptr_t)path, NULL }; + FTS *fts; + FTSENT *ent; + int fts_options = FTS_LOGICAL | FTS_NOCHDIR; + int ret, total = 0; + + if (stat(path, &sb) != 0) { + return (-1); + } else if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (-1); + } + if ((fts = fts_open(paths, fts_options, NULL)) == NULL) + err(1, "fts_open()"); + while ((ent = fts_read(fts)) != NULL) { + if (ent->fts_info != FTS_F) { + if (ent->fts_info == FTS_ERR) + warnc(ent->fts_errno, "fts_read()"); + continue; + } + info("found %s", ent->fts_path); + ret = read_cert(ent->fts_path, tree, exclude); + if (ret > 0) + total += ret; + } + fts_close(fts); + return (total); +} + +/* + * Save the contents of a cert tree to disk. + * + * Returns 0 on success and -1 on failure. + */ +static int +write_certs(const char *dir, struct cert_tree *tree) +{ + struct file_tree files = RB_INITIALIZER(&files); + struct cert *cert; + struct file *file, *tmp; + struct dirent **dents, **ent; + char *path, *tmppath = NULL; + FILE *f; + mode_t mode = 0444; + int cmp, d, fd, ndents, ret = 0; + + /* + * Start by generating unambiguous file names for each certificate + * and storing them in lexicographical order + */ + RB_FOREACH(cert, cert_tree, tree) { + if ((file = calloc(1, sizeof(*file))) == NULL) + err(1, NULL); + file->cert = cert; + for (file->c = 0; file->c < INT_MAX; file->c++) + if (RB_INSERT(file_tree, &files, file) == NULL) + break; + if (file->c == INT_MAX) + errx(1, "unable to disambiguate %08lx", cert->hash); + free(cert->path); + cert->path = xasprintf("%08lx.%d", cert->hash, file->c); + } + /* + * Open and scan the directory. + */ + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0 || +#ifdef BOOTSTRAPPING + (ndents = scandir(dir, &dents, NULL, lexisort)) +#else + (ndents = fdscandir(d, &dents, NULL, lexisort)) +#endif + < 0) + err(1, "%s", dir); + /* + * Iterate over the directory listing and the certificate listing + * in parallel. If the directory listing gets ahead of the + * certificate listing, we need to write the current certificate + * and advance the certificate listing. If the certificate + * listing is ahead of the directory listing, we need to delete + * the current file and advance the directory listing. If they + * are neck and neck, we have a match and could in theory compare + * the two, but in practice it's faster to just replace the + * current file with the current certificate (and advance both). + */ + ent = dents; + file = RB_MIN(file_tree, &files); + for (;;) { + if (ent < dents + ndents) { + /* skip directories */ + if ((*ent)->d_type == DT_DIR) { + free(*ent++); + continue; + } + if (file != NULL) { + /* compare current dirent to current cert */ + path = file->cert->path; + cmp = strcmp((*ent)->d_name, path); + } else { + /* trailing files in directory */ + path = NULL; + cmp = -1; + } + } else { + if (file != NULL) { + /* trailing certificates */ + path = file->cert->path; + cmp = 1; + } else { + /* end of both lists */ + path = NULL; + break; + } + } + if (cmp < 0) { + /* a file on disk with no matching certificate */ + info("removing %s/%s", dir, (*ent)->d_name); + if (!dryrun) + (void)unlinkat(d, (*ent)->d_name, 0); + free(*ent++); + continue; + } + if (cmp == 0) { + /* a file on disk with a matching certificate */ + info("replacing %s/%s", dir, (*ent)->d_name); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_TRUNC, mode); + if (!unprivileged && fd >= 0) + (void)fchmod(fd, mode); + } + free(*ent++); + } else { + /* a certificate with no matching file */ + info("writing %s/%s", dir, path); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_EXCL, mode); + } + } + /* write the certificate */ + if (fd < 0 || + (f = fdopen(fd, "w")) == NULL || + !PEM_write_X509(f, file->cert->x509)) { + if (tmppath != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmppath, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmppath ? tmppath : path); + } + /* rename temp file if applicable */ + if (tmppath != NULL) { + if (ret == 0 && renameat(d, tmppath, d, path) != 0) { + warn("%s/%s", dir, path); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmppath, 0); + free(tmppath); + tmppath = NULL; + } + fflush(f); + /* emit metalog */ + if (mlf != NULL) { + fprintf(mlf, ".%s/%s type=file " + "uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), path, + uname, gname, mode, ftell(f)); + } + fclose(f); + /* advance certificate listing */ + tmp = RB_NEXT(file_tree, &files, file); + RB_REMOVE(file_tree, &files, file); + free(file); + file = tmp; + } + free(dents); + close(d); + return (ret); +} + +/* + * Save all certs in a tree to a single file (bundle). + * + * Returns 0 on success and -1 on failure. + */ +static int +write_bundle(const char *dir, const char *file, struct cert_tree *tree) +{ + struct cert *cert; + char *tmpfile = NULL; + FILE *f; + int d, fd, ret = 0; + mode_t mode = 0444; + + if (dir != NULL) { + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0) + err(1, "%s", dir); + } else { + dir = "."; + d = AT_FDCWD; + } + info("writing %s/%s", dir, file); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmpfile = xasprintf(".%s", file); + fd = openat(d, tmpfile, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if (fd < 0 || (f = fdopen(fd, "w")) == NULL) { + if (tmpfile != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmpfile, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmpfile ? tmpfile : file); + } + RB_FOREACH(cert, cert_tree, tree) { + if (!PEM_write_X509(f, cert->x509)) { + warn("%s/%s", dir, tmpfile ? tmpfile : file); + ret = -1; + break; + } + } + if (tmpfile != NULL) { + if (ret == 0 && renameat(d, tmpfile, d, file) != 0) { + warn("%s/%s", dir, file); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmpfile, 0); + free(tmpfile); + } + if (ret == 0 && mlf != NULL) { + fprintf(mlf, + ".%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), file, uname, gname, mode, ftell(f)); + } + fclose(f); + if (d != AT_FDCWD) + close(d); + return (ret); +} + +/* + * Load trusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_trusted(bool all, struct cert_tree *exclude) +{ + unsigned int i, n; + int ret; + + /* load external trusted certs */ + for (i = n = 0; all && trusted_paths[i] != NULL; i++) { + ret = read_certs(trusted_paths[i], &trusted, exclude); + if (ret > 0) + n += ret; + } + + /* load installed trusted certs */ + ret = read_certs(trusted_dest, &trusted, exclude); + if (ret > 0) + n += ret; + + info("%d trusted certificates found", n); + return (n); +} + +/* + * Load untrusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_untrusted(bool all) +{ + char *path; + unsigned int i, n; + int ret; + + /* load external untrusted certs */ + for (i = n = 0; all && untrusted_paths[i] != NULL; i++) { + ret = read_certs(untrusted_paths[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + + /* load installed untrusted certs */ + ret = read_certs(untrusted_dest, &untrusted, NULL); + if (ret > 0) + n += ret; + + /* load legacy untrusted certs */ + path = expand_path(LEGACY_PATH); + ret = read_certs(path, &untrusted, NULL); + if (ret > 0) { + warnx("certificates found in legacy directory %s", + path); + n += ret; + } else if (ret == 0) { + warnx("legacy directory %s can safely be deleted", + path); + } + free(path); + + info("%d untrusted certificates found", n); + return (n); +} + +/* + * Save trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_trusted(void) +{ + int ret; + + /* save untrusted certs */ + ret = write_certs(trusted_dest, &trusted); + return (ret); +} + +/* + * Save untrusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_untrusted(void) +{ + int ret; + + ret = write_certs(untrusted_dest, &untrusted); + return (ret); +} + +/* + * Save certificate bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_bundle(void) +{ + char *dir, *file, *sep; + int ret; + + if ((sep = strrchr(bundle_dest, '/')) == NULL) { + dir = NULL; + file = bundle_dest; + } else { + dir = xasprintf("%.*s", (int)(sep - bundle_dest), bundle_dest); + file = sep + 1; + } + ret = write_bundle(dir, file, &trusted); + free(dir); + return (ret); +} + +/* + * Save everything. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_all(void) +{ + int ret = 0; + + ret |= save_untrusted(); + ret |= save_trusted(); + if (!nobundle) + ret |= save_bundle(); + return (ret); +} + +/* + * List the contents of a certificate tree. + */ +static void +list_certs(struct cert_tree *tree) +{ + struct cert *cert; + char *path, *name; + + RB_FOREACH(cert, cert_tree, tree) { + path = longnames ? NULL : strrchr(cert->path, '/'); + name = longnames ? NULL : strrchr(cert->name, '='); + printf("%s\t%s\n", path ? path + 1 : cert->path, + name ? name + 1 : cert->name); + } +} + +/* + * Load installed trusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_list(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load trusted certificates */ + load_trusted(false, NULL); + /* list them */ + list_certs(&trusted); + free_certs(&trusted); + return (0); +} + +/* + * Load installed untrusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrusted(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load untrusted certificates */ + load_untrusted(false); + /* list them */ + list_certs(&untrusted); + free_certs(&untrusted); + return (0); +} + +/* + * Load trusted and untrusted certificates from all sources, then + * regenerate both the hashed directories and the bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_rehash(int argc, char **argv __unused) +{ + int ret; + + if (argc > 1) + usage(); + + if (unprivileged && (mlf = fopen(metalog, "a")) == NULL) { + warn("%s", metalog); + return (-1); + } + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + if (mlf != NULL) + fclose(mlf); + return (ret); +} + +/* + * Manually add one or more certificates to the list of trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_trust(int argc, char **argv) +{ + struct cert_tree extra = RB_INITIALIZER(&extra); + struct cert *cert, *other, *tmp; + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* now load the additional trusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &extra, &trusted); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new trusted certificates found"); + free_certs(&untrusted); + free_certs(&trusted); + free_certs(&extra); + return (0); + } + + /* + * For each new trusted cert, move it from the extra list to the + * trusted list, then check if a matching certificate exists on + * the untrusted list. If that is the case, warn the user, then + * remove the matching certificate from the untrusted list. + */ + RB_FOREACH_SAFE(cert, cert_tree, &extra, tmp) { + RB_REMOVE(cert_tree, &extra, cert); + RB_INSERT(cert_tree, &trusted, cert); + if ((other = RB_FIND(cert_tree, &untrusted, cert)) != NULL) { + warnx("%s was previously untrusted", cert->name); + RB_REMOVE(cert_tree, &untrusted, other); + free_cert(other); + } + } + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +/* + * Manually add one or more certificates to the list of untrusted + * certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrust(int argc, char **argv) +{ + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* now load the additional untrusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new untrusted certificates found"); + free_certs(&untrusted); + return (0); + } + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +static void +set_defaults(void) +{ + const char *value; + char *str; + size_t len; + + if (localbase == NULL && + (localbase = getenv("LOCALBASE")) == NULL) { + if ((str = malloc((len = PATH_MAX) + 1)) == NULL) + err(1, NULL); + while (sysctlbyname("user.localbase", str, &len, NULL, 0) < 0) { + if (errno != ENOMEM) + err(1, "sysctl(user.localbase)"); + if ((str = realloc(str, len + 1)) == NULL) + err(1, NULL); + } + str[len] = '\0'; + localbase = str; + } + + if (destdir == NULL && + (destdir = getenv("DESTDIR")) == NULL) + destdir = ""; + destdir = normalize_path(destdir); + + if (distbase == NULL && + (distbase = getenv("DISTBASE")) == NULL) + distbase = ""; + if (*distbase != '\0' && *distbase != '/') + errx(1, "DISTBASE=%s does not begin with a slash", distbase); + distbase = normalize_path(distbase); + + if (unprivileged && metalog == NULL && + (metalog = getenv("METALOG")) == NULL) + metalog = xasprintf("%s/METALOG", destdir); + + if (!verbose) { + if ((value = getenv("CERTCTL_VERBOSE")) != NULL) { + if (value[0] != '\0') { + verbose = true; + } + } + } + + if ((value = getenv("TRUSTPATH")) != NULL) + trusted_paths = split_paths(value); + else + trusted_paths = expand_paths(default_trusted_paths); + + if ((value = getenv("UNTRUSTPATH")) != NULL) + untrusted_paths = split_paths(value); + else + untrusted_paths = expand_paths(default_untrusted_paths); + + if ((value = getenv("TRUSTDESTDIR")) != NULL || + (value = getenv("CERTDESTDIR")) != NULL) + trusted_dest = xstrdup(value); + else + trusted_dest = expand_path(TRUSTED_PATH); + + if ((value = getenv("UNTRUSTDESTDIR")) != NULL) + untrusted_dest = xstrdup(value); + else + untrusted_dest = expand_path(UNTRUSTED_PATH); + + if ((value = getenv("BUNDLE")) != NULL) + bundle_dest = xstrdup(value); + else + bundle_dest = expand_path(BUNDLE_PATH); + + info("localbase:\t%s", localbase); + info("destdir:\t%s", destdir); + info("distbase:\t%s", distbase); + info("unprivileged:\t%s", unprivileged ? "true" : "false"); + info("verbose:\t%s", verbose ? "true" : "false"); +} + +typedef int (*main_t)(int, char **); + +static struct { + const char *name; + main_t func; +} commands[] = { + { "list", certctl_list }, + { "untrusted", certctl_untrusted }, + { "rehash", certctl_rehash }, + { "untrust", certctl_untrust }, + { "trust", certctl_trust }, + { 0 }, +}; + +static void +usage(void) +{ + fprintf(stderr, "usage: certctl [-lv] [-D destdir] [-d distbase] list\n" + " certctl [-lv] [-D destdir] [-d distbase] untrusted\n" + " certctl [-BnUv] [-D destdir] [-d distbase] [-M metalog] rehash\n" + " certctl [-nv] [-D destdir] [-d distbase] untrust <file>\n" + " certctl [-nv] [-D destdir] [-d distbase] trust <file>\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const char *command; + int opt; + + while ((opt = getopt(argc, argv, "BcD:d:g:lL:M:no:Uv")) != -1) + switch (opt) { + case 'B': + nobundle = true; + break; + case 'c': + /* ignored for compatibility */ + break; + case 'D': + destdir = optarg; + break; + case 'd': + distbase = optarg; + break; + case 'g': + gname = optarg; + break; + case 'l': + longnames = true; + break; + case 'L': + localbase = optarg; + break; + case 'M': + metalog = optarg; + break; + case 'n': + dryrun = true; + break; + case 'o': + uname = optarg; + break; + case 'U': + unprivileged = true; + break; + case 'v': + verbose = true; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + command = *argv; + + if ((nobundle || unprivileged || metalog != NULL) && + strcmp(command, "rehash") != 0) + usage(); + if (!unprivileged && metalog != NULL) { + warnx("-M may only be used in conjunction with -U"); + usage(); + } + + set_defaults(); + + for (unsigned i = 0; commands[i].name != NULL; i++) + if (strcmp(command, commands[i].name) == 0) + exit(!!commands[i].func(argc, argv)); + usage(); +} diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh deleted file mode 100755 index 2bde651de126..000000000000 --- a/usr.sbin/certctl/certctl.sh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/sh -#- -# SPDX-License-Identifier: BSD-2-Clause -# -# Copyright 2018 Allan Jude <allanjude@freebsd.org> -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted providing that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING -# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# - -set -u - -############################################################ CONFIGURATION - -: ${DESTDIR:=} -: ${DISTBASE:=} - -############################################################ GLOBALS - -SCRIPTNAME="${0##*/}" -LINK=-lrs -ERRORS=0 -NOOP=false -UNPRIV=false -VERBOSE=false - -############################################################ FUNCTIONS - -info() -{ - echo "${0##*/}: $@" >&2 -} - -verbose() -{ - if "${VERBOSE}" ; then - info "$@" - fi -} - -perform() -{ - if ! "${NOOP}" ; then - "$@" - fi -} - -cert_files_in() -{ - find -L "$@" -type f \( \ - -name '*.pem' -or \ - -name '*.crt' -or \ - -name '*.cer' \ - \) 2>/dev/null -} - -eolcvt() -{ - cat "$@" | tr -s '\r' '\n' -} - -do_hash() -{ - local hash - - if hash=$(openssl x509 -noout -subject_hash -in "$1") ; then - echo "$hash" - return 0 - else - info "Error: $1" - ERRORS=$((ERRORS + 1)) - return 1 - fi -} - -get_decimal() -{ - local checkdir hash decimal - - checkdir=$1 - hash=$2 - decimal=0 - - while [ -e "$checkdir/$hash.$decimal" ] ; do - decimal=$((decimal + 1)) - done - - echo ${decimal} - return 0 -} - -create_trusted() -{ - local hash certhash otherfile otherhash - local suffix - - hash=$(do_hash "$1") || return - certhash=$(openssl x509 -sha1 -in "$1" -noout -fingerprint) - for otherfile in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - info "Skipping untrusted certificate $hash ($otherfile)" - return 0 - fi - done - for otherfile in $(find $CERTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - verbose "Skipping duplicate entry for certificate $hash" - return 0 - fi - done - suffix=$(get_decimal "$CERTDESTDIR" "$hash") - verbose "Adding $hash.$suffix to trust store" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$(realpath "$1")" "$CERTDESTDIR/$hash.$suffix" -} - -# Accepts either dot-hash form from `certctl list` or a path to a valid cert. -resolve_certname() -{ - local hash srcfile filename - local suffix - - # If it exists as a file, we'll try that; otherwise, we'll scan - if [ -e "$1" ] ; then - hash=$(do_hash "$1") || return - srcfile=$(realpath "$1") - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - elif [ -e "${CERTDESTDIR}/$1" ] ; then - srcfile=$(realpath "${CERTDESTDIR}/$1") - hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//') - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - fi -} - -create_untrusted() -{ - local srcfile filename - - set -- $(resolve_certname "$1") - srcfile=$1 - filename=$2 - - if [ -z "$srcfile" -o -z "$filename" ] ; then - return - fi - - verbose "Adding $filename to untrusted list" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$srcfile" "$UNTRUSTDESTDIR/$filename" -} - -do_scan() -{ - local CFUNC CSEARCH CPATH CFILE CERT SPLITDIR - local oldIFS="$IFS" - CFUNC="$1" - CSEARCH="$2" - - IFS=: - set -- $CSEARCH - IFS="$oldIFS" - for CFILE in $(cert_files_in "$@") ; do - verbose "Reading $CFILE" - case $(eolcvt "$CFILE" | egrep -c '^-+BEGIN CERTIFICATE-+$') in - 0) - ;; - 1) - "$CFUNC" "$CFILE" - ;; - *) - verbose "Multiple certificates found, splitting..." - SPLITDIR=$(mktemp -d) - eolcvt "$CFILE" | egrep '^(---|[0-9A-Za-z/+=]+$)' | \ - split -p '^-+BEGIN CERTIFICATE-+$' - "$SPLITDIR/x" - for CERT in $(find "$SPLITDIR" -type f) ; do - "$CFUNC" "$CERT" - done - rm -rf "$SPLITDIR" - ;; - esac - done -} - -do_list() -{ - local CFILE subject - - for CFILE in $(find "$@" \( -type f -or -type l \) -name '*.[0-9]') ; do - if [ ! -s "$CFILE" ] ; then - info "Unable to read $CFILE" - ERRORS=$((ERRORS + 1)) - continue - fi - subject= - if ! "$VERBOSE" ; then - subject=$(openssl x509 -noout -subject -nameopt multiline -in "$CFILE" | sed -n '/commonName/s/.*= //p') - fi - if [ -z "$subject" ] ; then - subject=$(openssl x509 -noout -subject -in "$CFILE") - fi - printf "%s\t%s\n" "${CFILE##*/}" "$subject" - done -} - -cmd_rehash() -{ - - if [ -e "$CERTDESTDIR" ] ; then - perform find "$CERTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$CERTDESTDIR" - fi - if [ -e "$UNTRUSTDESTDIR" ] ; then - perform find "$UNTRUSTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$UNTRUSTDESTDIR" - fi - - do_scan create_untrusted "$UNTRUSTPATH" - do_scan create_trusted "$TRUSTPATH" -} - -cmd_list() -{ - info "Listing Trusted Certificates:" - do_list "$CERTDESTDIR" -} - -cmd_untrust() -{ - local UTFILE - - shift # verb - perform install -d -m 0755 "$UNTRUSTDESTDIR" - for UTFILE in "$@"; do - info "Adding $UTFILE to untrusted list" - create_untrusted "$UTFILE" - done -} - -cmd_trust() -{ - local UTFILE untrustedhash certhash hash - - shift # verb - for UTFILE in "$@"; do - if [ -s "$UTFILE" ] ; then - hash=$(do_hash "$UTFILE") - certhash=$(openssl x509 -sha1 -in "$UTFILE" -noout -fingerprint) - for UNTRUSTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - untrustedhash=$(openssl x509 -sha1 -in "$UNTRUSTEDFILE" -noout -fingerprint) - if [ "$certhash" = "$untrustedhash" ] ; then - info "Removing $(basename "$UNTRUSTEDFILE") from untrusted list" - perform rm -f $UNTRUSTEDFILE - fi - done - elif [ -e "$UNTRUSTDESTDIR/$UTFILE" ] ; then - info "Removing $UTFILE from untrusted list" - perform rm -f "$UNTRUSTDESTDIR/$UTFILE" - else - info "Cannot find $UTFILE" - ERRORS=$((ERRORS + 1)) - fi - done -} - -cmd_untrusted() -{ - info "Listing Untrusted Certificates:" - do_list "$UNTRUSTDESTDIR" -} - -usage() -{ - exec >&2 - echo "Manage the TLS trusted certificates on the system" - echo " $SCRIPTNAME [-v] list" - echo " List trusted certificates" - echo " $SCRIPTNAME [-v] untrusted" - echo " List untrusted certificates" - echo " $SCRIPTNAME [-cnUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash" - echo " Rehash all trusted and untrusted certificates" - echo " $SCRIPTNAME [-cnv] untrust <file>" - echo " Add <file> to the list of untrusted certificates" - echo " $SCRIPTNAME [-cnv] trust <file>" - echo " Remove <file> from the list of untrusted certificates" - exit 64 -} - -############################################################ MAIN - -while getopts cD:d:M:nUv flag; do - case "$flag" in - c) LINK=-c ;; - D) DESTDIR=${OPTARG} ;; - d) DISTBASE=${OPTARG} ;; - M) METALOG=${OPTARG} ;; - n) NOOP=true ;; - U) UNPRIV=true ;; - v) VERBOSE=true ;; - esac -done -shift $((OPTIND - 1)) - -DESTDIR=${DESTDIR%/} - -if ! [ -z "${CERTCTL_VERBOSE:-}" ] ; then - VERBOSE=true -fi -: ${METALOG:=${DESTDIR}/METALOG} -INSTALLFLAGS= -if "$UNPRIV" ; then - INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR:-/} -o root -g wheel" -fi -: ${LOCALBASE:=$(sysctl -n user.localbase)} -: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs} -: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted} -: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs} -: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted} - -[ $# -gt 0 ] || usage -case "$1" in -list) cmd_list ;; -rehash) cmd_rehash ;; -blacklist) cmd_untrust "$@" ;; -untrust) cmd_untrust "$@" ;; -trust) cmd_trust "$@" ;; -unblacklist) cmd_trust "$@" ;; -untrusted) cmd_untrusted ;; -blacklisted) cmd_untrusted ;; -*) usage # NOTREACHED -esac - -retval=$? -if [ $ERRORS -gt 0 ] ; then - info "Encountered $ERRORS errors" -fi -exit $retval - -################################################################################ -# END -################################################################################ diff --git a/usr.sbin/certctl/tests/Makefile b/usr.sbin/certctl/tests/Makefile new file mode 100644 index 000000000000..da301c3ded03 --- /dev/null +++ b/usr.sbin/certctl/tests/Makefile @@ -0,0 +1,5 @@ +PACKAGE= tests +ATF_TESTS_SH= certctl_test +${PACKAGE}FILES+= certctl.subr + +.include <bsd.test.mk> diff --git a/usr.sbin/certctl/tests/certctl.subr b/usr.sbin/certctl/tests/certctl.subr new file mode 100644 index 000000000000..841cc1781e69 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl.subr @@ -0,0 +1,44 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Generate a random name +rand_name() { + local length=${1:-32} + + jot -r -c -s '' ${length} A Z +} + +# Generate a subject for a given name +subject() { + local crtname=$1 + + echo "/CN=${crtname}/O=FreeBSD/OU=Test/" +} + +# Generate a key +gen_key() { + local keyname=$1 + + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl genrsa -out ${keyname}.key +} + +# Generate a certificate for a given name, key, and serial number +gen_crt() { + local crtname=$1 + local keyname=${2:-${crtname}} + local serial=${3:-1} + + if ! [ -f "${keyname}".key ]; then + gen_key "${keyname}" + fi + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl req -x509 -new \ + -subj="$(subject ${crtname})" \ + -set_serial ${serial} \ + -key ${keyname}.key \ + -out ${crtname}.crt +} diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh new file mode 100644 index 000000000000..f60bac6ffbb3 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl_test.sh @@ -0,0 +1,332 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +. $(atf_get_srcdir)/certctl.subr + +# Random sets of eight non-colliding names +set1() +{ + cat <<EOF +AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB 0ca83bbe +UYSYXKDNNJTYOQPBGIKQDHRJYZHTDPKK 0d9a6512 +LODHGFXMZYKGOKAYGWTMMYQJYHDATDDM 4e6219f5 +NBBTQHJLHKBFFFWJTHHSNKOQYMGLHLPW 5dd76abc +BJFAQZXZHYQLIDDPCAQFPDMNXICUXBXW ad68573d +IOKNTHVEVVIJMNMYAVILMEMQQWLVRESN b577803d +BHGMAJJGNJPIVMHMFCUTJLGFROJICEKN c98a6338 +HCRFQMGDQJALMLUQNXMPGLXFLLJRODJW f50c6379 +EOF +} + +set2() +{ + cat <<EOF +GOHKZTSKIPDSYNLMGYXGLROPTATELXIU 30789c88 +YOOTYHEGHZIYFXOBLNKENPSJUDGOPJJU 7fadbc13 +ETRINNYBGKIENAVGOKVJYFSSHFZIJZRH 8ed664af +DBFGMFFMRNLPQLQPOLXOEUVLCRXLRSWT 8f34355e +WFOPBQPLQFHDHZOUQFEIDGSYDUOTSNDQ ac0471df +HMNETZMGNIWRGXQCVZXVZGWSGFBRRDQC b32f1472 +SHFYBXDVAUACBFPPAIGDAQIAGYOYGMQE baca75fa +PCBGDNVPYCDGNRQSGRSLXFHYKXLAVLHW ddeeae01 +EOF +} + +set3() +{ + cat <<EOF +NJWIRLPWAIICVJBKXXHFHLCPAERZATRL 000aa2e5 +RJAENDPOCZQEVCPFUWOWDXPCSMYJPVYC 021b95a3 +PQUQDSWHBNVLBTNBGONYRLGZZVEFXVLO 071e8c50 +VZEXRKJUPZSFBDWBOLUZXOGLNTEAPCZM 3af7bb9b +ZXOWOXQTXNZMAMZIWVFDZDJEWOOAGAOH 48d5c7cc +KQSFQYVJMFTMADIHJIWGSQISWKSHRYQO 509f5ba1 +AIECYSLWZOIEPJWWUTWSQXCNCIHHZHYI 8cb0c503 +RFHWDJZEPOFLMPGXAHVEJFHCDODAPVEV 9ae4e049 +EOF +} + +# Random set of three colliding names +collhash=f2888ce3 +coll() +{ + cat <<EOF +EJFTZEOANQLOYPEHWWXBWEWEFVKHMSNA $collhash +LEMRWZAZLKZLPPSFLNLQZVGKKBEOFYWG $collhash +ZWUPHYWKKTVEFBJOLLPDAIKGRDFVXZID $collhash +EOF +} + +sortfile() { + for filename; do + sort "${filename}" >"${filename}"- + mv "${filename}"- "${filename}" + done +} + +certctl_setup() +{ + export DESTDIR="$PWD" + + # Create input directories + mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted + mkdir -p ${DESTDIR}/usr/local/share/certs + + # Create output directories + mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/certs + mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/untrusted + + # Generate a random key + keyname="testkey" + gen_key ${keyname} + + # Generate certificates + :>metalog.expect + :>trusted.expect + :>untrusted.expect + metalog() { + echo ".${DISTBASE}$@ type=file" >>metalog.expect + } + trusted() { + local crtname=$1 + local filename=$2 + printf "%s\t%s\n" "${filename}" "${crtname}" >>trusted.expect + metalog "/etc/ssl/certs/${filename}" + } + untrusted() { + local crtname=$1 + local filename=$2 + printf "%s\t%s\n" "${filename}" "${crtname}" >>untrusted.expect + metalog "/etc/ssl/untrusted/${filename}" + } + set1 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + trusted "${crtname}" "${hash}.0" + done + local c=0 + coll | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted + trusted "${crtname}" "${hash}.${c}" + c=$((c+1)) + done + set2 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + openssl x509 -in ${crtname}.crt + rm ${crtname}.crt + trusted "${crtname}" "${hash}.0" + done >usr/local/share/certs/bundle.crt + set3 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted + untrusted "${crtname}" "${hash}.0" + done + metalog "/etc/ssl/cert.pem" + unset -f untrusted + unset -f trusted + unset -f metalog + sortfile *.expect +} + +check_trusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -e ignore -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}${DISTBASE}/etc/ssl/certs + atf_check -e ignore -o not-match:"found: [1-9]" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}${DISTBASE}/etc/ssl/untrusted +} + +check_untrusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -e ignore -o not-match:"found: [1-9]" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}/${DISTBASE}/etc/ssl/certs + atf_check -e ignore -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + ${DESTDIR}/${DISTBASE}/etc/ssl/untrusted +} + +check_in_bundle() { + local b=${DISTBASE}${DISTBASE+/} + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check grep -q "${line}" ${DESTDIR}${DISTBASE}/etc/ssl/cert.pem +} + +check_not_in_bundle() { + local b=${DISTBASE}${DISTBASE+/} + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check -s exit:1 grep -q "${line}" etc/ssl/cert.pem +} + +atf_test_case rehash +rehash_head() +{ + atf_set "descr" "Test the rehash command" +} +rehash_body() +{ + certctl_setup + atf_check certctl rehash + + # Verify non-colliding trusted certificates + (set1; set2) >trusted + while read crtname hash ; do + check_trusted "${crtname}" + done <trusted + + # Verify colliding trusted certificates + coll >coll + while read crtname hash ; do + check_trusted "${crtname}" $(wc -l <coll) + done <coll + + # Verify untrusted certificates + set3 >untrusted + while read crtname hash ; do + check_untrusted "${crtname}" + done <untrusted + + # Verify bundle + for f in etc/ssl/certs/*.? ; do + check_in_bundle "${f}" + done + for f in etc/ssl/untrusted/*.? ; do + check_not_in_bundle "${f}" + done +} + +atf_test_case list +list_head() +{ + atf_set "descr" "Test the list and untrusted commands" +} +list_body() +{ + certctl_setup + atf_check certctl rehash + + atf_check -o save:trusted.out certctl list + sortfile trusted.out + # the ordering of the colliding certificates is partly + # determined by fields that change every time we regenerate + # them, so ignore them in the diff + atf_check diff -u \ + --ignore-matching-lines $collhash \ + trusted.expect trusted.out + + atf_check -o save:untrusted.out certctl untrusted + sortfile untrusted.out + atf_check diff -u \ + untrusted.expect untrusted.out +} + +atf_test_case trust +trust_head() +{ + atf_set "descr" "Test the trust command" +} +trust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=$(set3 | (read crtname hash ; echo ${crtname})) + crtfile=usr/share/certs/untrusted/${crtname}.crt + check_untrusted ${crtname} + check_not_in_bundle ${crtfile} + atf_check -e match:"was previously untrusted" \ + certctl trust ${crtfile} + check_trusted ${crtname} + check_in_bundle ${crtfile} +} + +atf_test_case untrust +untrust_head() +{ + atf_set "descr" "Test the untrust command" +} +untrust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=$(set1 | (read crtname hash ; echo ${crtname})) + crtfile=usr/share/certs/trusted/${crtname}.crt + check_trusted "${crtname}" + check_in_bundle ${crtfile} + atf_check certctl untrust "${crtfile}" + check_untrusted "${crtname}" + check_not_in_bundle ${crtfile} +} + +atf_test_case metalog +metalog_head() +{ + atf_set "descr" "Verify the metalog" +} +metalog_body() +{ + export DISTBASE=/base + certctl_setup + + # certctl gets DESTDIR and DISTBASE from environment + rm -f metalog.orig + atf_check certctl -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short + + # certctl gets DESTDIR and DISTBASE from command line + rm -f metalog.orig + atf_check env -uDESTDIR -uDISTBASE \ + certctl -D ${DESTDIR} -d ${DISTBASE} -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short + + # as above, but intentionally add trailing slashes + rm -f metalog.orig + atf_check env -uDESTDIR -uDISTBASE \ + certctl -D ${DESTDIR}// -d ${DISTBASE}/ -U -M metalog.orig rehash + sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short + atf_check diff -u metalog.expect metalog.short +} + +atf_test_case misc +misc_head() +{ + atf_set "descr" "Test miscellaneous edge cases" +} +misc_body() +{ + # certctl rejects DISTBASE that does not begin with a slash + atf_check -s exit:1 -e match:"begin with a slash" \ + certctl -d base -n rehash + atf_check -s exit:1 -e match:"begin with a slash" \ + env DISTBASE=base certctl -n rehash +} + +atf_init_test_cases() +{ + atf_add_test_case rehash + atf_add_test_case list + atf_add_test_case trust + atf_add_test_case untrust + atf_add_test_case metalog + atf_add_test_case misc +} diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c index d9fb29474d87..e1af0a4131d3 100644 --- a/usr.sbin/chroot/chroot.c +++ b/usr.sbin/chroot/chroot.c @@ -103,7 +103,9 @@ main(int argc, char *argv[]) gid = 0; uid = 0; + gids = 0; user = group = grouplist = NULL; + gidlist = NULL; nonprivileged = false; while ((ch = getopt(argc, argv, "G:g:u:n")) != -1) { switch(ch) { @@ -119,6 +121,11 @@ main(int argc, char *argv[]) break; case 'G': grouplist = optarg; + + /* + * XXX Why not allow us to drop all of our supplementary + * groups? + */ if (*grouplist == '\0') usage(); break; @@ -139,23 +146,20 @@ main(int argc, char *argv[]) if (group != NULL) gid = resolve_group(group); - ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; - if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL) - err(1, "malloc"); - /* Populate the egid slot in our groups to avoid accidents. */ - if (gid == 0) - gidlist[0] = getegid(); - else - gidlist[0] = gid; - for (gids = 1; - (p = strsep(&grouplist, ",")) != NULL && gids < ngroups_max; ) { - if (*p == '\0') - continue; - - gidlist[gids++] = resolve_group(p); + if (grouplist != NULL) { + ngroups_max = sysconf(_SC_NGROUPS_MAX); + if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL) + err(1, "malloc"); + for (gids = 0; (p = strsep(&grouplist, ",")) != NULL && + gids < ngroups_max; ) { + if (*p == '\0') + continue; + + gidlist[gids++] = resolve_group(p); + } + if (p != NULL && gids == ngroups_max) + errx(1, "too many supplementary groups provided"); } - if (p != NULL && gids == ngroups_max) - errx(1, "too many supplementary groups provided"); if (user != NULL) uid = resolve_user(user); @@ -175,7 +179,7 @@ main(int argc, char *argv[]) err(1, "%s", argv[0]); } - if (gids && setgroups(gids, gidlist) == -1) + if (gidlist != NULL && setgroups(gids, gidlist) == -1) err(1, "setgroups"); if (group && setgid(gid) == -1) err(1, "setgid"); diff --git a/usr.sbin/freebsd-update/freebsd-update.sh b/usr.sbin/freebsd-update/freebsd-update.sh index c388e76644dc..81040431ea79 100644 --- a/usr.sbin/freebsd-update/freebsd-update.sh +++ b/usr.sbin/freebsd-update/freebsd-update.sh @@ -3000,7 +3000,7 @@ install_from_index () { if [ -z "${LINK}" ]; then # Create a file, without setting flags. gunzip < files/${HASH}.gz > ${HASH} - install -S -o ${OWNER} -g ${GROUP} \ + install -o ${OWNER} -g ${GROUP} \ -m ${PERM} ${HASH} ${BASEDIR}/${FPATH} rm ${HASH} else diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c index 9609faf0b104..21ce44c77033 100644 --- a/usr.sbin/inetd/builtins.c +++ b/usr.sbin/inetd/builtins.c @@ -606,6 +606,8 @@ ident_stream(int s, struct servtab *sep) */ if (initgroups(pw->pw_name, pw->pw_gid) == -1) iderror(lport, fport, s, ID_UNKNOWN); + if (setegid(pw->pw_gid) == -1) + iderror(lport, fport, s, ID_UNKNOWN); if (seteuid(pw->pw_uid) == -1) iderror(lport, fport, s, ID_UNKNOWN); /* diff --git a/usr.sbin/jail/config.c b/usr.sbin/jail/config.c index 70de82e662e7..1bad04ccde68 100644 --- a/usr.sbin/jail/config.c +++ b/usr.sbin/jail/config.c @@ -189,7 +189,7 @@ load_config(const char *cfname) * jail is created or found. */ if (j->intparams[KP_NAME] == NULL) - add_param(j, j->intparams[KP_JID], KP_NAME, NULL); + add_param(j, NULL, KP_NAME, j->name); /* Resolve any variable substitutions. */ pgen = 0; diff --git a/usr.sbin/jail/tests/jail_basic_test.sh b/usr.sbin/jail/tests/jail_basic_test.sh index 509900e8569c..6802da7b049a 100755 --- a/usr.sbin/jail/tests/jail_basic_test.sh +++ b/usr.sbin/jail/tests/jail_basic_test.sh @@ -198,7 +198,7 @@ clean_jails() fi while read jail; do - if jls -e -j "$jail"; then + if jls -c -j "$jail"; then jail -r "$jail" fi done < jails.lst @@ -211,10 +211,23 @@ jid_name_set_body() echo "basejail" >> jails.lst echo "$jid { name = basejail; persist; }" > jail.conf atf_check -o match:"$jid: created" jail -f jail.conf -c "$jid" + # Confirm that we didn't override the explicitly-set name with the jid + # as the name. + atf_check -o match:"basejail" jls -j "$jid" name + atf_check -o match:"$jid: removed" jail -f jail.conf -r "$jid" + + echo "$jid { host.hostname = \"\${name}\"; persist; }" > jail.conf + atf_check -o match:"$jid: created" jail -f jail.conf -c "$jid" + # Confirm that ${name} expanded and expanded correctly to the + # jid-implied name. + atf_check -o match:"$jid" jls -j "$jid" host.hostname atf_check -o match:"$jid: removed" jail -f jail.conf -r "$jid" echo "basejail { jid = $jid; persist; }" > jail.conf atf_check -o match:"basejail: created" jail -f jail.conf -c basejail + # Confirm that our jid assigment in the definition worked out and we + # did in-fact create the jail there. + atf_check -o match:"$jid" jls -j "basejail" jid atf_check -o match:"basejail: removed" jail -f jail.conf -r basejail } diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c index a3da852de46e..b4db5bb2e29f 100644 --- a/usr.sbin/lpr/lpc/lpc.c +++ b/usr.sbin/lpr/lpc/lpc.c @@ -358,6 +358,8 @@ ingroup(const char *grname) err(1, "getgroups"); } gid = gptr->gr_gid; + if (gid == getegid()) + return(1); for (i = 0; i < ngroups; i++) if (gid == groups[i]) return(1); diff --git a/usr.sbin/makefs/zfs.c b/usr.sbin/makefs/zfs.c index 8d50c450541b..e33a182e5c8f 100644 --- a/usr.sbin/makefs/zfs.c +++ b/usr.sbin/makefs/zfs.c @@ -596,7 +596,7 @@ pool_labels_write(zfs_opt_t *zfs) * checksum is calculated in vdev_label_write(). */ for (size_t uoff = 0; uoff < sizeof(label->vl_uberblock); - uoff += (1 << zfs->ashift)) { + uoff += ASHIFT_UBERBLOCK_SIZE(zfs->ashift)) { ub = (uberblock_t *)(&label->vl_uberblock[0] + uoff); ub->ub_magic = UBERBLOCK_MAGIC; ub->ub_version = SPA_VERSION; diff --git a/usr.sbin/makefs/zfs/vdev.c b/usr.sbin/makefs/zfs/vdev.c index afcce402cb13..a2423e180ca3 100644 --- a/usr.sbin/makefs/zfs/vdev.c +++ b/usr.sbin/makefs/zfs/vdev.c @@ -200,7 +200,7 @@ vdev_label_write(zfs_opt_t *zfs, int ind, const vdev_label_t *labelp) * per sector; for example, with an ashift of 12 we end up with * 128KB/4KB=32 copies of the uberblock in the ring. */ - blksz = 1 << zfs->ashift; + blksz = ASHIFT_UBERBLOCK_SIZE(zfs->ashift); assert(sizeof(label->vl_uberblock) % blksz == 0); for (size_t roff = 0; roff < sizeof(label->vl_uberblock); roff += blksz) { diff --git a/usr.sbin/nvmfd/Makefile b/usr.sbin/nvmfd/Makefile deleted file mode 100644 index dc3dcc5e3a5c..000000000000 --- a/usr.sbin/nvmfd/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.include <src.opts.mk> -.PATH: ${SRCTOP}/sys/libkern - -PACKAGE=nvme-tools -PROG= nvmfd -SRCS= nvmfd.c controller.c ctl.c devices.c discovery.c gsb_crc32.c io.c -CFLAGS+= -I${SRCTOP}/lib/libnvmf -MAN= nvmfd.8 -LIBADD+= nvmf pthread util nv - -.include <bsd.prog.mk> - -CFLAGS.ctl.c= -I${SRCTOP}/sys -CWARNFLAGS.gsb_crc32.c= -Wno-cast-align diff --git a/usr.sbin/nvmfd/Makefile.depend b/usr.sbin/nvmfd/Makefile.depend deleted file mode 100644 index c4c6125c7a7c..000000000000 --- a/usr.sbin/nvmfd/Makefile.depend +++ /dev/null @@ -1,20 +0,0 @@ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/libc \ - lib/libcompiler_rt \ - lib/libnv \ - lib/libnvmf \ - lib/libthr \ - lib/libutil \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/nvmfd/controller.c b/usr.sbin/nvmfd/controller.c deleted file mode 100644 index e9435bce69da..000000000000 --- a/usr.sbin/nvmfd/controller.c +++ /dev/null @@ -1,244 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <err.h> -#include <errno.h> -#include <libnvmf.h> -#include <stdlib.h> - -#include "internal.h" - -struct controller { - struct nvmf_qpair *qp; - - uint64_t cap; - uint32_t vs; - uint32_t cc; - uint32_t csts; - - bool shutdown; - - struct nvme_controller_data cdata; -}; - -static bool -update_cc(struct controller *c, uint32_t new_cc) -{ - uint32_t changes; - - if (c->shutdown) - return (false); - if (!nvmf_validate_cc(c->qp, c->cap, c->cc, new_cc)) - return (false); - - changes = c->cc ^ new_cc; - c->cc = new_cc; - - /* Handle shutdown requests. */ - if (NVMEV(NVME_CC_REG_SHN, changes) != 0 && - NVMEV(NVME_CC_REG_SHN, new_cc) != 0) { - c->csts &= ~NVMEM(NVME_CSTS_REG_SHST); - c->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE); - c->shutdown = true; - } - - if (NVMEV(NVME_CC_REG_EN, changes) != 0) { - if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) { - /* Controller reset. */ - c->csts = 0; - c->shutdown = true; - } else - c->csts |= NVMEF(NVME_CSTS_REG_RDY, 1); - } - return (true); -} - -static void -handle_property_get(const struct controller *c, const struct nvmf_capsule *nc, - const struct nvmf_fabric_prop_get_cmd *pget) -{ - struct nvmf_fabric_prop_get_rsp rsp; - - nvmf_init_cqe(&rsp, nc, 0); - - switch (le32toh(pget->ofst)) { - case NVMF_PROP_CAP: - if (pget->attrib.size != NVMF_PROP_SIZE_8) - goto error; - rsp.value.u64 = htole64(c->cap); - break; - case NVMF_PROP_VS: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->vs); - break; - case NVMF_PROP_CC: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->cc); - break; - case NVMF_PROP_CSTS: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->csts); - break; - default: - goto error; - } - - nvmf_send_response(nc, &rsp); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static void -handle_property_set(struct controller *c, const struct nvmf_capsule *nc, - const struct nvmf_fabric_prop_set_cmd *pset) -{ - switch (le32toh(pset->ofst)) { - case NVMF_PROP_CC: - if (pset->attrib.size != NVMF_PROP_SIZE_4) - goto error; - if (!update_cc(c, le32toh(pset->value.u32.low))) - goto error; - break; - default: - goto error; - } - - nvmf_send_success(nc); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static void -handle_fabrics_command(struct controller *c, - const struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc) -{ - switch (fc->fctype) { - case NVMF_FABRIC_COMMAND_PROPERTY_GET: - handle_property_get(c, nc, - (const struct nvmf_fabric_prop_get_cmd *)fc); - break; - case NVMF_FABRIC_COMMAND_PROPERTY_SET: - handle_property_set(c, nc, - (const struct nvmf_fabric_prop_set_cmd *)fc); - break; - case NVMF_FABRIC_COMMAND_CONNECT: - warnx("CONNECT command on connected queue"); - nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); - break; - case NVMF_FABRIC_COMMAND_DISCONNECT: - warnx("DISCONNECT command on admin queue"); - nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC, - NVMF_FABRIC_SC_INVALID_QUEUE_TYPE); - break; - default: - warnx("Unsupported fabrics command %#x", fc->fctype); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } -} - -static void -handle_identify_command(const struct controller *c, - const struct nvmf_capsule *nc, const struct nvme_command *cmd) -{ - uint8_t cns; - - cns = le32toh(cmd->cdw10) & 0xFF; - switch (cns) { - case 1: - break; - default: - warnx("Unsupported CNS %#x for IDENTIFY", cns); - goto error; - } - - nvmf_send_controller_data(nc, &c->cdata, sizeof(c->cdata)); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -void -controller_handle_admin_commands(struct controller *c, handle_command *cb, - void *cb_arg) -{ - struct nvmf_qpair *qp = c->qp; - const struct nvme_command *cmd; - struct nvmf_capsule *nc; - int error; - - for (;;) { - error = nvmf_controller_receive_capsule(qp, &nc); - if (error != 0) { - if (error != ECONNRESET) - warnc(error, "Failed to read command capsule"); - break; - } - - cmd = nvmf_capsule_sqe(nc); - - /* - * Only permit Fabrics commands while a controller is - * disabled. - */ - if (NVMEV(NVME_CC_REG_EN, c->cc) == 0 && - cmd->opc != NVME_OPC_FABRICS_COMMANDS) { - warnx("Unsupported admin opcode %#x while disabled\n", - cmd->opc); - nvmf_send_generic_error(nc, - NVME_SC_COMMAND_SEQUENCE_ERROR); - nvmf_free_capsule(nc); - continue; - } - - if (cb(nc, cmd, cb_arg)) { - nvmf_free_capsule(nc); - continue; - } - - switch (cmd->opc) { - case NVME_OPC_FABRICS_COMMANDS: - handle_fabrics_command(c, nc, - (const struct nvmf_fabric_cmd *)cmd); - break; - case NVME_OPC_IDENTIFY: - handle_identify_command(c, nc, cmd); - break; - default: - warnx("Unsupported admin opcode %#x", cmd->opc); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } - nvmf_free_capsule(nc); - } -} - -struct controller * -init_controller(struct nvmf_qpair *qp, - const struct nvme_controller_data *cdata) -{ - struct controller *c; - - c = calloc(1, sizeof(*c)); - c->qp = qp; - c->cap = nvmf_controller_cap(c->qp); - c->vs = cdata->ver; - c->cdata = *cdata; - - return (c); -} - -void -free_controller(struct controller *c) -{ - free(c); -} diff --git a/usr.sbin/nvmfd/ctl.c b/usr.sbin/nvmfd/ctl.c deleted file mode 100644 index 73e90e1411bd..000000000000 --- a/usr.sbin/nvmfd/ctl.c +++ /dev/null @@ -1,137 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/param.h> -#include <sys/linker.h> -#include <sys/nv.h> -#include <sys/time.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libnvmf.h> -#include <string.h> - -#include <cam/ctl/ctl.h> -#include <cam/ctl/ctl_io.h> -#include <cam/ctl/ctl_ioctl.h> - -#include "internal.h" - -static int ctl_fd = -1; -static int ctl_port; - -static void -open_ctl(void) -{ - if (ctl_fd > 0) - return; - - ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); - if (ctl_fd == -1 && errno == ENOENT) { - if (kldload("ctl") == -1) - err(1, "Failed to load ctl.ko"); - ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); - } - if (ctl_fd == -1) - err(1, "Failed to open %s", CTL_DEFAULT_DEV); -} - -void -init_ctl_port(const char *subnqn, const struct nvmf_association_params *params) -{ - char result_buf[256]; - struct ctl_port_entry entry; - struct ctl_req req; - nvlist_t *nvl; - - open_ctl(); - - nvl = nvlist_create(0); - - nvlist_add_string(nvl, "subnqn", subnqn); - - /* XXX: Hardcoded in discovery.c */ - nvlist_add_stringf(nvl, "portid", "%u", 1); - - nvlist_add_stringf(nvl, "max_io_qsize", "%u", params->max_io_qsize); - - memset(&req, 0, sizeof(req)); - strlcpy(req.driver, "nvmf", sizeof(req.driver)); - req.reqtype = CTL_REQ_CREATE; - req.args = nvlist_pack(nvl, &req.args_len); - if (req.args == NULL) - errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_CREATE"); - req.result = result_buf; - req.result_len = sizeof(result_buf); - if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) - err(1, "ioctl(CTL_PORT/CTL_REQ_CREATE)"); - if (req.status == CTL_LUN_ERROR) - errx(1, "Failed to create CTL port: %s", req.error_str); - if (req.status != CTL_LUN_OK) - errx(1, "Failed to create CTL port: %d", req.status); - - nvlist_destroy(nvl); - nvl = nvlist_unpack(result_buf, req.result_len, 0); - if (nvl == NULL) - errx(1, "Failed to unpack nvlist from CTL_PORT/CTL_REQ_CREATE"); - - ctl_port = nvlist_get_number(nvl, "port_id"); - nvlist_destroy(nvl); - - memset(&entry, 0, sizeof(entry)); - entry.targ_port = ctl_port; - if (ioctl(ctl_fd, CTL_ENABLE_PORT, &entry) != 0) - errx(1, "ioctl(CTL_ENABLE_PORT)"); -} - -void -shutdown_ctl_port(const char *subnqn) -{ - struct ctl_req req; - nvlist_t *nvl; - - open_ctl(); - - nvl = nvlist_create(0); - - nvlist_add_string(nvl, "subnqn", subnqn); - - memset(&req, 0, sizeof(req)); - strlcpy(req.driver, "nvmf", sizeof(req.driver)); - req.reqtype = CTL_REQ_REMOVE; - req.args = nvlist_pack(nvl, &req.args_len); - if (req.args == NULL) - errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_REMOVE"); - if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) - err(1, "ioctl(CTL_PORT/CTL_REQ_REMOVE)"); - if (req.status == CTL_LUN_ERROR) - errx(1, "Failed to remove CTL port: %s", req.error_str); - if (req.status != CTL_LUN_OK) - errx(1, "Failed to remove CTL port: %d", req.status); - - nvlist_destroy(nvl); -} - -void -ctl_handoff_qpair(struct nvmf_qpair *qp, - const struct nvmf_fabric_connect_cmd *cmd, - const struct nvmf_fabric_connect_data *data) -{ - struct ctl_nvmf req; - int error; - - memset(&req, 0, sizeof(req)); - req.type = CTL_NVMF_HANDOFF; - error = nvmf_handoff_controller_qpair(qp, cmd, data, &req.data.handoff); - if (error != 0) { - warnc(error, "Failed to prepare qpair for handoff"); - return; - } - - if (ioctl(ctl_fd, CTL_NVMF, &req) != 0) - warn("ioctl(CTL_NVMF/CTL_NVMF_HANDOFF)"); -} diff --git a/usr.sbin/nvmfd/devices.c b/usr.sbin/nvmfd/devices.c deleted file mode 100644 index fafc1077f207..000000000000 --- a/usr.sbin/nvmfd/devices.c +++ /dev/null @@ -1,386 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/disk.h> -#include <sys/gsb_crc32.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <net/ieee_oui.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libnvmf.h> -#include <libutil.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -#define RAMDISK_PREFIX "ramdisk:" - -struct backing_device { - enum { RAMDISK, FILE, CDEV } type; - union { - int fd; /* FILE, CDEV */ - void *mem; /* RAMDISK */ - }; - u_int sector_size; - uint64_t nlbas; - uint64_t eui64; -}; - -static struct backing_device *devices; -static u_int ndevices; - -static uint64_t -generate_eui64(uint32_t low) -{ - return (OUI_FREEBSD_NVME_LOW << 16 | low); -} - -static uint32_t -crc32(const void *buf, size_t len) -{ - return (calculate_crc32c(0xffffffff, buf, len) ^ 0xffffffff); -} - -static void -init_ramdisk(const char *config, struct backing_device *dev) -{ - static uint32_t ramdisk_idx = 1; - uint64_t num; - - dev->type = RAMDISK; - dev->sector_size = 512; - if (expand_number(config, &num)) - errx(1, "Invalid ramdisk specification: %s", config); - if ((num % dev->sector_size) != 0) - errx(1, "Invalid ramdisk size %ju", (uintmax_t)num); - dev->mem = calloc(num, 1); - dev->nlbas = num / dev->sector_size; - dev->eui64 = generate_eui64('M' << 24 | ramdisk_idx++); -} - -static void -init_filedevice(const char *config, int fd, struct stat *sb, - struct backing_device *dev) -{ - dev->type = FILE; - dev->fd = fd; - dev->sector_size = 512; - if ((sb->st_size % dev->sector_size) != 0) - errx(1, "File size is not a multiple of 512: %s", config); - dev->nlbas = sb->st_size / dev->sector_size; - dev->eui64 = generate_eui64('F' << 24 | - (crc32(config, strlen(config)) & 0xffffff)); -} - -static void -init_chardevice(const char *config, int fd, struct backing_device *dev) -{ - off_t len; - - dev->type = CDEV; - dev->fd = fd; - if (ioctl(fd, DIOCGSECTORSIZE, &dev->sector_size) != 0) - err(1, "Failed to fetch sector size for %s", config); - if (ioctl(fd, DIOCGMEDIASIZE, &len) != 0) - err(1, "Failed to fetch sector size for %s", config); - dev->nlbas = len / dev->sector_size; - dev->eui64 = generate_eui64('C' << 24 | - (crc32(config, strlen(config)) & 0xffffff)); -} - -static void -init_device(const char *config, struct backing_device *dev) -{ - struct stat sb; - int fd; - - /* Check for a RAM disk. */ - if (strncmp(RAMDISK_PREFIX, config, strlen(RAMDISK_PREFIX)) == 0) { - init_ramdisk(config + strlen(RAMDISK_PREFIX), dev); - return; - } - - fd = open(config, O_RDWR); - if (fd == -1) - err(1, "Failed to open %s", config); - if (fstat(fd, &sb) == -1) - err(1, "fstat"); - switch (sb.st_mode & S_IFMT) { - case S_IFCHR: - init_chardevice(config, fd, dev); - break; - case S_IFREG: - init_filedevice(config, fd, &sb, dev); - break; - default: - errx(1, "Invalid file type for %s", config); - } -} - -void -register_devices(int ac, char **av) -{ - ndevices = ac; - devices = calloc(ndevices, sizeof(*devices)); - - for (int i = 0; i < ac; i++) - init_device(av[i], &devices[i]); -} - -u_int -device_count(void) -{ - return (ndevices); -} - -static struct backing_device * -lookup_device(uint32_t nsid) -{ - if (nsid == 0 || nsid > ndevices) - return (NULL); - return (&devices[nsid - 1]); -} - -void -device_active_nslist(uint32_t nsid, struct nvme_ns_list *nslist) -{ - u_int count; - - memset(nslist, 0, sizeof(*nslist)); - count = 0; - nsid++; - while (nsid <= ndevices) { - nslist->ns[count] = htole32(nsid); - count++; - if (count == nitems(nslist->ns)) - break; - nsid++; - } -} - -bool -device_identification_descriptor(uint32_t nsid, void *buf) -{ - struct backing_device *dev; - char *p; - - dev = lookup_device(nsid); - if (dev == NULL) - return (false); - - memset(buf, 0, 4096); - - p = buf; - - /* EUI64 */ - *p++ = 1; - *p++ = 8; - p += 2; - be64enc(p, dev->eui64); - return (true); -} - -bool -device_namespace_data(uint32_t nsid, struct nvme_namespace_data *nsdata) -{ - struct backing_device *dev; - - dev = lookup_device(nsid); - if (dev == NULL) - return (false); - - memset(nsdata, 0, sizeof(*nsdata)); - nsdata->nsze = htole64(dev->nlbas); - nsdata->ncap = nsdata->nsze; - nsdata->nuse = nsdata->ncap; - nsdata->nlbaf = 1 - 1; - nsdata->flbas = NVMEF(NVME_NS_DATA_FLBAS_FORMAT, 0); - nsdata->lbaf[0] = NVMEF(NVME_NS_DATA_LBAF_LBADS, - ffs(dev->sector_size) - 1); - - be64enc(nsdata->eui64, dev->eui64); - return (true); -} - -static bool -read_buffer(int fd, void *buf, size_t len, off_t offset) -{ - ssize_t nread; - char *dst; - - dst = buf; - while (len > 0) { - nread = pread(fd, dst, len, offset); - if (nread == -1 && errno == EINTR) - continue; - if (nread <= 0) - return (false); - dst += nread; - len -= nread; - offset += nread; - } - return (true); -} - -void -device_read(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - char *p, *src; - off_t off; - size_t len; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - if (lba + nlb < lba || lba + nlb > dev->nlbas) { - nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE); - return; - } - - off = lba * dev->sector_size; - len = nlb * dev->sector_size; - if (nvmf_capsule_data_len(nc) != len) { - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); - return; - } - - if (dev->type == RAMDISK) { - p = NULL; - src = (char *)dev->mem + off; - } else { - p = malloc(len); - if (!read_buffer(dev->fd, p, len, off)) { - free(p); - nvmf_send_generic_error(nc, - NVME_SC_INTERNAL_DEVICE_ERROR); - return; - } - src = p; - } - - nvmf_send_controller_data(nc, src, len); - free(p); -} - -static bool -write_buffer(int fd, const void *buf, size_t len, off_t offset) -{ - ssize_t nwritten; - const char *src; - - src = buf; - while (len > 0) { - nwritten = pwrite(fd, src, len, offset); - if (nwritten == -1 && errno == EINTR) - continue; - if (nwritten <= 0) - return (false); - src += nwritten; - len -= nwritten; - offset += nwritten; - } - return (true); -} - -void -device_write(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - char *p, *dst; - off_t off; - size_t len; - int error; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - if (lba + nlb < lba || lba + nlb > dev->nlbas) { - nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE); - return; - } - - off = lba * dev->sector_size; - len = nlb * dev->sector_size; - if (nvmf_capsule_data_len(nc) != len) { - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); - return; - } - - if (dev->type == RAMDISK) { - p = NULL; - dst = (char *)dev->mem + off; - } else { - p = malloc(len); - dst = p; - } - - error = nvmf_receive_controller_data(nc, 0, dst, len); - if (error != 0) { - nvmf_send_generic_error(nc, NVME_SC_TRANSIENT_TRANSPORT_ERROR); - free(p); - return; - } - - if (dev->type != RAMDISK) { - if (!write_buffer(dev->fd, p, len, off)) { - free(p); - nvmf_send_generic_error(nc, - NVME_SC_INTERNAL_DEVICE_ERROR); - return; - } - } - free(p); - nvmf_send_success(nc); -} - -void -device_flush(uint32_t nsid, const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - switch (dev->type) { - case RAMDISK: - break; - case FILE: - if (fdatasync(dev->fd) == -1) { - nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR, - NVME_SC_WRITE_FAULTS); - return; - } - break; - case CDEV: - if (ioctl(dev->fd, DIOCGFLUSH) == -1) { - nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR, - NVME_SC_WRITE_FAULTS); - return; - } - } - - nvmf_send_success(nc); -} diff --git a/usr.sbin/nvmfd/discovery.c b/usr.sbin/nvmfd/discovery.c deleted file mode 100644 index 2cfe56731d7c..000000000000 --- a/usr.sbin/nvmfd/discovery.c +++ /dev/null @@ -1,342 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <assert.h> -#include <err.h> -#include <libnvmf.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -struct io_controller_data { - struct nvme_discovery_log_entry entry; - bool wildcard; -}; - -struct discovery_controller { - struct nvme_discovery_log *discovery_log; - size_t discovery_log_len; - int s; -}; - -struct discovery_thread_arg { - struct controller *c; - struct nvmf_qpair *qp; - int s; -}; - -static struct io_controller_data *io_controllers; -static struct nvmf_association *discovery_na; -static u_int num_io_controllers; - -static bool -init_discovery_log_entry(struct nvme_discovery_log_entry *entry, int s, - const char *subnqn) -{ - struct sockaddr_storage ss; - socklen_t len; - bool wildcard; - - len = sizeof(ss); - if (getsockname(s, (struct sockaddr *)&ss, &len) == -1) - err(1, "getsockname"); - - memset(entry, 0, sizeof(*entry)); - entry->trtype = NVMF_TRTYPE_TCP; - switch (ss.ss_family) { - case AF_INET: - { - struct sockaddr_in *sin; - - sin = (struct sockaddr_in *)&ss; - entry->adrfam = NVMF_ADRFAM_IPV4; - snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u", - htons(sin->sin_port)); - if (inet_ntop(AF_INET, &sin->sin_addr, entry->traddr, - sizeof(entry->traddr)) == NULL) - err(1, "inet_ntop"); - wildcard = (sin->sin_addr.s_addr == htonl(INADDR_ANY)); - break; - } - case AF_INET6: - { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)&ss; - entry->adrfam = NVMF_ADRFAM_IPV6; - snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u", - htons(sin6->sin6_port)); - if (inet_ntop(AF_INET6, &sin6->sin6_addr, entry->traddr, - sizeof(entry->traddr)) == NULL) - err(1, "inet_ntop"); - wildcard = (memcmp(&sin6->sin6_addr, &in6addr_any, - sizeof(in6addr_any)) == 0); - break; - } - default: - errx(1, "Unsupported address family %u", ss.ss_family); - } - entry->subtype = NVMF_SUBTYPE_NVME; - if (flow_control_disable) - entry->treq |= (1 << 2); - entry->portid = htole16(1); - entry->cntlid = htole16(NVMF_CNTLID_DYNAMIC); - entry->aqsz = NVME_MAX_ADMIN_ENTRIES; - strlcpy(entry->subnqn, subnqn, sizeof(entry->subnqn)); - return (wildcard); -} - -void -init_discovery(void) -{ - struct nvmf_association_params aparams; - - memset(&aparams, 0, sizeof(aparams)); - aparams.sq_flow_control = false; - aparams.dynamic_controller_model = true; - aparams.max_admin_qsize = NVME_MAX_ADMIN_ENTRIES; - aparams.tcp.pda = 0; - aparams.tcp.header_digests = header_digests; - aparams.tcp.data_digests = data_digests; - aparams.tcp.maxh2cdata = maxh2cdata; - discovery_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true, - &aparams); - if (discovery_na == NULL) - err(1, "Failed to create discovery association"); -} - -void -discovery_add_io_controller(int s, const char *subnqn) -{ - struct io_controller_data *icd; - - io_controllers = reallocf(io_controllers, (num_io_controllers + 1) * - sizeof(*io_controllers)); - - icd = &io_controllers[num_io_controllers]; - num_io_controllers++; - - icd->wildcard = init_discovery_log_entry(&icd->entry, s, subnqn); -} - -static void -build_discovery_log_page(struct discovery_controller *dc) -{ - struct sockaddr_storage ss; - socklen_t len; - char traddr[256]; - u_int i, nentries; - uint8_t adrfam; - - if (dc->discovery_log != NULL) - return; - - len = sizeof(ss); - if (getsockname(dc->s, (struct sockaddr *)&ss, &len) == -1) { - warn("build_discovery_log_page: getsockname"); - return; - } - - memset(traddr, 0, sizeof(traddr)); - switch (ss.ss_family) { - case AF_INET: - { - struct sockaddr_in *sin; - - sin = (struct sockaddr_in *)&ss; - adrfam = NVMF_ADRFAM_IPV4; - if (inet_ntop(AF_INET, &sin->sin_addr, traddr, - sizeof(traddr)) == NULL) { - warn("build_discovery_log_page: inet_ntop"); - return; - } - break; - } - case AF_INET6: - { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)&ss; - adrfam = NVMF_ADRFAM_IPV6; - if (inet_ntop(AF_INET6, &sin6->sin6_addr, traddr, - sizeof(traddr)) == NULL) { - warn("build_discovery_log_page: inet_ntop"); - return; - } - break; - } - default: - assert(false); - } - - nentries = 0; - for (i = 0; i < num_io_controllers; i++) { - if (io_controllers[i].wildcard && - io_controllers[i].entry.adrfam != adrfam) - continue; - nentries++; - } - - dc->discovery_log_len = sizeof(*dc->discovery_log) + - nentries * sizeof(struct nvme_discovery_log_entry); - dc->discovery_log = calloc(dc->discovery_log_len, 1); - dc->discovery_log->numrec = nentries; - dc->discovery_log->recfmt = 0; - nentries = 0; - for (i = 0; i < num_io_controllers; i++) { - if (io_controllers[i].wildcard && - io_controllers[i].entry.adrfam != adrfam) - continue; - - dc->discovery_log->entries[nentries] = io_controllers[i].entry; - if (io_controllers[i].wildcard) - memcpy(dc->discovery_log->entries[nentries].traddr, - traddr, sizeof(traddr)); - } -} - -static void -handle_get_log_page_command(const struct nvmf_capsule *nc, - const struct nvme_command *cmd, struct discovery_controller *dc) -{ - uint64_t offset; - uint32_t length; - - switch (nvmf_get_log_page_id(cmd)) { - case NVME_LOG_DISCOVERY: - break; - default: - warnx("Unsupported log page %u for discovery controller", - nvmf_get_log_page_id(cmd)); - goto error; - } - - build_discovery_log_page(dc); - - offset = nvmf_get_log_page_offset(cmd); - if (offset >= dc->discovery_log_len) - goto error; - - length = nvmf_get_log_page_length(cmd); - if (length > dc->discovery_log_len - offset) - length = dc->discovery_log_len - offset; - - nvmf_send_controller_data(nc, (char *)dc->discovery_log + offset, - length); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static bool -discovery_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd, - void *arg) -{ - struct discovery_controller *dc = arg; - - switch (cmd->opc) { - case NVME_OPC_GET_LOG_PAGE: - handle_get_log_page_command(nc, cmd, dc); - return (true); - default: - return (false); - } -} - -static void * -discovery_thread(void *arg) -{ - struct discovery_thread_arg *dta = arg; - struct discovery_controller dc; - - pthread_detach(pthread_self()); - - memset(&dc, 0, sizeof(dc)); - dc.s = dta->s; - - controller_handle_admin_commands(dta->c, discovery_command, &dc); - - free(dc.discovery_log); - free_controller(dta->c); - - nvmf_free_qpair(dta->qp); - - close(dta->s); - free(dta); - return (NULL); -} - -void -handle_discovery_socket(int s) -{ - struct nvmf_fabric_connect_data data; - struct nvme_controller_data cdata; - struct nvmf_qpair_params qparams; - struct discovery_thread_arg *dta; - struct nvmf_capsule *nc; - struct nvmf_qpair *qp; - pthread_t thr; - int error; - - memset(&qparams, 0, sizeof(qparams)); - qparams.tcp.fd = s; - - nc = NULL; - qp = nvmf_accept(discovery_na, &qparams, &nc, &data); - if (qp == NULL) { - warnx("Failed to create discovery qpair: %s", - nvmf_association_error(discovery_na)); - goto error; - } - - if (strcmp(data.subnqn, NVMF_DISCOVERY_NQN) != 0) { - warn("Discovery qpair with invalid SubNQN: %.*s", - (int)sizeof(data.subnqn), data.subnqn); - nvmf_connect_invalid_parameters(nc, true, - offsetof(struct nvmf_fabric_connect_data, subnqn)); - goto error; - } - - /* Just use a controller ID of 1 for all discovery controllers. */ - error = nvmf_finish_accept(nc, 1); - if (error != 0) { - warnc(error, "Failed to send CONNECT reponse"); - goto error; - } - - nvmf_init_discovery_controller_data(qp, &cdata); - - dta = malloc(sizeof(*dta)); - dta->qp = qp; - dta->s = s; - dta->c = init_controller(qp, &cdata); - - error = pthread_create(&thr, NULL, discovery_thread, dta); - if (error != 0) { - warnc(error, "Failed to create discovery thread"); - free_controller(dta->c); - free(dta); - goto error; - } - - nvmf_free_capsule(nc); - return; - -error: - if (nc != NULL) - nvmf_free_capsule(nc); - if (qp != NULL) - nvmf_free_qpair(qp); - close(s); -} diff --git a/usr.sbin/nvmfd/internal.h b/usr.sbin/nvmfd/internal.h deleted file mode 100644 index f70dc78881c6..000000000000 --- a/usr.sbin/nvmfd/internal.h +++ /dev/null @@ -1,66 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#ifndef __INTERNAL_H__ -#define __INTERNAL_H__ - -#include <stdbool.h> - -struct controller; -struct nvme_command; -struct nvme_controller_data; -struct nvme_ns_list; -struct nvmf_capsule; -struct nvmf_qpair; - -typedef bool handle_command(const struct nvmf_capsule *, - const struct nvme_command *, void *); - -extern bool data_digests; -extern bool header_digests; -extern bool flow_control_disable; -extern bool kernel_io; -extern uint32_t maxh2cdata; - -/* controller.c */ -void controller_handle_admin_commands(struct controller *c, - handle_command *cb, void *cb_arg); -struct controller *init_controller(struct nvmf_qpair *qp, - const struct nvme_controller_data *cdata); -void free_controller(struct controller *c); - -/* discovery.c */ -void init_discovery(void); -void handle_discovery_socket(int s); -void discovery_add_io_controller(int s, const char *subnqn); - -/* io.c */ -void init_io(const char *subnqn); -void handle_io_socket(int s); -void shutdown_io(void); - -/* devices.c */ -void register_devices(int ac, char **av); -u_int device_count(void); -void device_active_nslist(uint32_t nsid, struct nvme_ns_list *nslist); -bool device_identification_descriptor(uint32_t nsid, void *buf); -bool device_namespace_data(uint32_t nsid, struct nvme_namespace_data *nsdata); -void device_read(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc); -void device_write(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc); -void device_flush(uint32_t nsid, const struct nvmf_capsule *nc); - -/* ctl.c */ -void init_ctl_port(const char *subnqn, - const struct nvmf_association_params *params); -void ctl_handoff_qpair(struct nvmf_qpair *qp, - const struct nvmf_fabric_connect_cmd *cmd, - const struct nvmf_fabric_connect_data *data); -void shutdown_ctl_port(const char *subnqn); - -#endif /* !__INTERNAL_H__ */ diff --git a/usr.sbin/nvmfd/io.c b/usr.sbin/nvmfd/io.c deleted file mode 100644 index 4407360257a2..000000000000 --- a/usr.sbin/nvmfd/io.c +++ /dev/null @@ -1,676 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/sysctl.h> -#include <err.h> -#include <errno.h> -#include <libnvmf.h> -#include <pthread.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -struct io_controller { - struct controller *c; - - u_int num_io_queues; - u_int active_io_queues; - struct nvmf_qpair **io_qpairs; - int *io_sockets; - - struct nvme_firmware_page fp; - struct nvme_health_information_page hip; - uint16_t partial_dur; - uint16_t partial_duw; - - uint16_t cntlid; - char hostid[16]; - char hostnqn[NVME_NQN_FIELD_SIZE]; -}; - -static struct nvmf_association *io_na; -static pthread_cond_t io_cond; -static pthread_mutex_t io_na_mutex; -static struct io_controller *io_controller; -static const char *nqn; -static char serial[NVME_SERIAL_NUMBER_LENGTH]; - -void -init_io(const char *subnqn) -{ - struct nvmf_association_params aparams; - u_long hostid; - size_t len; - - memset(&aparams, 0, sizeof(aparams)); - aparams.sq_flow_control = !flow_control_disable; - aparams.dynamic_controller_model = true; - aparams.max_admin_qsize = NVME_MAX_ADMIN_ENTRIES; - aparams.max_io_qsize = NVMF_MAX_IO_ENTRIES; - aparams.tcp.pda = 0; - aparams.tcp.header_digests = header_digests; - aparams.tcp.data_digests = data_digests; - aparams.tcp.maxh2cdata = maxh2cdata; - io_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true, - &aparams); - if (io_na == NULL) - err(1, "Failed to create I/O controller association"); - - nqn = subnqn; - - /* Generate a serial number from the kern.hostid node. */ - len = sizeof(hostid); - if (sysctlbyname("kern.hostid", &hostid, &len, NULL, 0) == -1) - err(1, "sysctl: kern.hostid"); - - nvmf_controller_serial(serial, sizeof(serial), hostid); - - pthread_cond_init(&io_cond, NULL); - pthread_mutex_init(&io_na_mutex, NULL); - - if (kernel_io) - init_ctl_port(subnqn, &aparams); -} - -void -shutdown_io(void) -{ - if (kernel_io) - shutdown_ctl_port(nqn); -} - -static void -handle_get_log_page(struct io_controller *ioc, const struct nvmf_capsule *nc, - const struct nvme_command *cmd) -{ - uint64_t offset; - uint32_t numd; - size_t len; - uint8_t lid; - - lid = le32toh(cmd->cdw10) & 0xff; - numd = le32toh(cmd->cdw10) >> 16 | le32toh(cmd->cdw11) << 16; - offset = le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32; - - if (offset % 3 != 0) - goto error; - - len = (numd + 1) * 4; - - switch (lid) { - case NVME_LOG_ERROR: - { - void *buf; - - if (len % sizeof(struct nvme_error_information_entry) != 0) - goto error; - - buf = calloc(1, len); - nvmf_send_controller_data(nc, buf, len); - free(buf); - return; - } - case NVME_LOG_HEALTH_INFORMATION: - if (len != sizeof(ioc->hip)) - goto error; - - nvmf_send_controller_data(nc, &ioc->hip, sizeof(ioc->hip)); - return; - case NVME_LOG_FIRMWARE_SLOT: - if (len != sizeof(ioc->fp)) - goto error; - - nvmf_send_controller_data(nc, &ioc->fp, sizeof(ioc->fp)); - return; - default: - warnx("Unsupported page %#x for GET_LOG_PAGE\n", lid); - goto error; - } - -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static bool -handle_io_identify_command(const struct nvmf_capsule *nc, - const struct nvme_command *cmd) -{ - struct nvme_namespace_data nsdata; - struct nvme_ns_list nslist; - uint32_t nsid; - uint8_t cns; - - cns = le32toh(cmd->cdw10) & 0xFF; - switch (cns) { - case 0: /* Namespace data. */ - if (!device_namespace_data(le32toh(cmd->nsid), &nsdata)) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return (true); - } - - nvmf_send_controller_data(nc, &nsdata, sizeof(nsdata)); - return (true); - case 2: /* Active namespace list. */ - nsid = le32toh(cmd->nsid); - if (nsid >= 0xfffffffe) { - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); - return (true); - } - - device_active_nslist(nsid, &nslist); - nvmf_send_controller_data(nc, &nslist, sizeof(nslist)); - return (true); - case 3: /* Namespace Identification Descriptor list. */ - if (!device_identification_descriptor(le32toh(cmd->nsid), - &nsdata)) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return (true); - } - - nvmf_send_controller_data(nc, &nsdata, sizeof(nsdata)); - return (true); - default: - return (false); - } -} - -static void -handle_set_features(struct io_controller *ioc, const struct nvmf_capsule *nc, - const struct nvme_command *cmd) -{ - struct nvme_completion cqe; - uint8_t fid; - - fid = NVMEV(NVME_FEAT_SET_FID, le32toh(cmd->cdw10)); - switch (fid) { - case NVME_FEAT_NUMBER_OF_QUEUES: - { - uint32_t num_queues; - - if (ioc->num_io_queues != 0) { - nvmf_send_generic_error(nc, - NVME_SC_COMMAND_SEQUENCE_ERROR); - return; - } - - num_queues = le32toh(cmd->cdw11) & 0xffff; - - /* 5.12.1.7: 65535 is invalid. */ - if (num_queues == 65535) - goto error; - - /* Fabrics requires the same number of SQs and CQs. */ - if (le32toh(cmd->cdw11) >> 16 != num_queues) - goto error; - - /* Convert to 1's based */ - num_queues++; - - /* Lock to synchronize with handle_io_qpair. */ - pthread_mutex_lock(&io_na_mutex); - ioc->num_io_queues = num_queues; - ioc->io_qpairs = calloc(num_queues, sizeof(*ioc->io_qpairs)); - ioc->io_sockets = calloc(num_queues, sizeof(*ioc->io_sockets)); - pthread_mutex_unlock(&io_na_mutex); - - nvmf_init_cqe(&cqe, nc, 0); - cqe.cdw0 = cmd->cdw11; - nvmf_send_response(nc, &cqe); - return; - } - case NVME_FEAT_ASYNC_EVENT_CONFIGURATION: - { - uint32_t aer_mask; - - aer_mask = le32toh(cmd->cdw11); - - /* Check for any reserved or unimplemented feature bits. */ - if ((aer_mask & 0xffffc000) != 0) - goto error; - - /* No AERs are generated by this daemon. */ - nvmf_send_success(nc); - return; - } - default: - warnx("Unsupported feature ID %u for SET_FEATURES", fid); - goto error; - } - -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static bool -admin_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd, - void *arg) -{ - struct io_controller *ioc = arg; - - switch (cmd->opc) { - case NVME_OPC_GET_LOG_PAGE: - handle_get_log_page(ioc, nc, cmd); - return (true); - case NVME_OPC_IDENTIFY: - return (handle_io_identify_command(nc, cmd)); - case NVME_OPC_SET_FEATURES: - handle_set_features(ioc, nc, cmd); - return (true); - case NVME_OPC_ASYNC_EVENT_REQUEST: - /* Ignore and never complete. */ - return (true); - case NVME_OPC_KEEP_ALIVE: - nvmf_send_success(nc); - return (true); - default: - return (false); - } -} - -static void -handle_admin_qpair(struct io_controller *ioc) -{ - pthread_setname_np(pthread_self(), "admin queue"); - - controller_handle_admin_commands(ioc->c, admin_command, ioc); - - pthread_mutex_lock(&io_na_mutex); - for (u_int i = 0; i < ioc->num_io_queues; i++) { - if (ioc->io_qpairs[i] == NULL || ioc->io_sockets[i] == -1) - continue; - close(ioc->io_sockets[i]); - ioc->io_sockets[i] = -1; - } - - /* Wait for I/O threads to notice. */ - while (ioc->active_io_queues > 0) - pthread_cond_wait(&io_cond, &io_na_mutex); - - io_controller = NULL; - pthread_mutex_unlock(&io_na_mutex); - - free_controller(ioc->c); - - free(ioc); -} - -static bool -handle_io_fabrics_command(const struct nvmf_capsule *nc, - const struct nvmf_fabric_cmd *fc) -{ - switch (fc->fctype) { - case NVMF_FABRIC_COMMAND_CONNECT: - warnx("CONNECT command on connected queue"); - nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); - break; - case NVMF_FABRIC_COMMAND_DISCONNECT: - { - const struct nvmf_fabric_disconnect_cmd *dis = - (const struct nvmf_fabric_disconnect_cmd *)fc; - if (dis->recfmt != htole16(0)) { - nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC, - NVMF_FABRIC_SC_INCOMPATIBLE_FORMAT); - break; - } - nvmf_send_success(nc); - return (true); - } - default: - warnx("Unsupported fabrics command %#x", fc->fctype); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } - - return (false); -} - -static void -hip_add(uint64_t pair[2], uint64_t addend) -{ - uint64_t old, new; - - old = le64toh(pair[0]); - new = old + addend; - pair[0] = htole64(new); - if (new < old) - pair[1] += htole64(1); -} - -static uint64_t -cmd_lba(const struct nvme_command *cmd) -{ - return ((uint64_t)le32toh(cmd->cdw11) << 32 | le32toh(cmd->cdw10)); -} - -static u_int -cmd_nlb(const struct nvme_command *cmd) -{ - return ((le32toh(cmd->cdw12) & 0xffff) + 1); -} - -static void -handle_read(struct io_controller *ioc, const struct nvmf_capsule *nc, - const struct nvme_command *cmd) -{ - size_t len; - - len = nvmf_capsule_data_len(nc); - device_read(le32toh(cmd->nsid), cmd_lba(cmd), cmd_nlb(cmd), nc); - hip_add(ioc->hip.host_read_commands, 1); - - len /= 512; - len += ioc->partial_dur; - if (len > 1000) - hip_add(ioc->hip.data_units_read, len / 1000); - ioc->partial_dur = len % 1000; -} - -static void -handle_write(struct io_controller *ioc, const struct nvmf_capsule *nc, - const struct nvme_command *cmd) -{ - size_t len; - - len = nvmf_capsule_data_len(nc); - device_write(le32toh(cmd->nsid), cmd_lba(cmd), cmd_nlb(cmd), nc); - hip_add(ioc->hip.host_write_commands, 1); - - len /= 512; - len += ioc->partial_duw; - if (len > 1000) - hip_add(ioc->hip.data_units_written, len / 1000); - ioc->partial_duw = len % 1000; -} - -static void -handle_flush(const struct nvmf_capsule *nc, const struct nvme_command *cmd) -{ - device_flush(le32toh(cmd->nsid), nc); -} - -static bool -handle_io_commands(struct io_controller *ioc, struct nvmf_qpair *qp) -{ - const struct nvme_command *cmd; - struct nvmf_capsule *nc; - int error; - bool disconnect; - - disconnect = false; - - while (!disconnect) { - error = nvmf_controller_receive_capsule(qp, &nc); - if (error != 0) { - if (error != ECONNRESET) - warnc(error, "Failed to read command capsule"); - break; - } - - cmd = nvmf_capsule_sqe(nc); - - switch (cmd->opc) { - case NVME_OPC_FLUSH: - if (cmd->nsid == htole32(0xffffffff)) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - break; - } - handle_flush(nc, cmd); - break; - case NVME_OPC_WRITE: - handle_write(ioc, nc, cmd); - break; - case NVME_OPC_READ: - handle_read(ioc, nc, cmd); - break; - case NVME_OPC_FABRICS_COMMANDS: - disconnect = handle_io_fabrics_command(nc, - (const struct nvmf_fabric_cmd *)cmd); - break; - default: - warnx("Unsupported NVM opcode %#x", cmd->opc); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } - nvmf_free_capsule(nc); - } - - return (disconnect); -} - -static void -handle_io_qpair(struct io_controller *ioc, struct nvmf_qpair *qp, int qid) -{ - char name[64]; - bool disconnect; - - snprintf(name, sizeof(name), "I/O queue %d", qid); - pthread_setname_np(pthread_self(), name); - - disconnect = handle_io_commands(ioc, qp); - - pthread_mutex_lock(&io_na_mutex); - if (disconnect) - ioc->io_qpairs[qid - 1] = NULL; - if (ioc->io_sockets[qid - 1] != -1) { - close(ioc->io_sockets[qid - 1]); - ioc->io_sockets[qid - 1] = -1; - } - ioc->active_io_queues--; - if (ioc->active_io_queues == 0) - pthread_cond_broadcast(&io_cond); - pthread_mutex_unlock(&io_na_mutex); -} - -static void -connect_admin_qpair(int s, struct nvmf_qpair *qp, struct nvmf_capsule *nc, - const struct nvmf_fabric_connect_data *data) -{ - struct nvme_controller_data cdata; - struct io_controller *ioc; - int error; - - /* Can only have one active I/O controller at a time. */ - pthread_mutex_lock(&io_na_mutex); - if (io_controller != NULL) { - pthread_mutex_unlock(&io_na_mutex); - nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC, - NVMF_FABRIC_SC_CONTROLLER_BUSY); - goto error; - } - - error = nvmf_finish_accept(nc, 2); - if (error != 0) { - pthread_mutex_unlock(&io_na_mutex); - warnc(error, "Failed to send CONNECT response"); - goto error; - } - - ioc = calloc(1, sizeof(*ioc)); - ioc->cntlid = 2; - memcpy(ioc->hostid, data->hostid, sizeof(ioc->hostid)); - memcpy(ioc->hostnqn, data->hostnqn, sizeof(ioc->hostnqn)); - - nvmf_init_io_controller_data(qp, serial, nqn, device_count(), - NVMF_IOCCSZ, &cdata); - - ioc->fp.afi = NVMEF(NVME_FIRMWARE_PAGE_AFI_SLOT, 1); - memcpy(ioc->fp.revision[0], cdata.fr, sizeof(cdata.fr)); - - ioc->hip.power_cycles[0] = 1; - - ioc->c = init_controller(qp, &cdata); - - io_controller = ioc; - pthread_mutex_unlock(&io_na_mutex); - - nvmf_free_capsule(nc); - - handle_admin_qpair(ioc); - close(s); - return; - -error: - nvmf_free_capsule(nc); - close(s); -} - -static void -connect_io_qpair(int s, struct nvmf_qpair *qp, struct nvmf_capsule *nc, - const struct nvmf_fabric_connect_data *data, uint16_t qid) -{ - struct io_controller *ioc; - int error; - - pthread_mutex_lock(&io_na_mutex); - if (io_controller == NULL) { - pthread_mutex_unlock(&io_na_mutex); - warnx("Attempt to create I/O qpair without admin qpair"); - nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); - goto error; - } - - if (memcmp(io_controller->hostid, data->hostid, - sizeof(data->hostid)) != 0) { - pthread_mutex_unlock(&io_na_mutex); - warnx("hostid mismatch for I/O qpair CONNECT"); - nvmf_connect_invalid_parameters(nc, true, - offsetof(struct nvmf_fabric_connect_data, hostid)); - goto error; - } - if (le16toh(data->cntlid) != io_controller->cntlid) { - pthread_mutex_unlock(&io_na_mutex); - warnx("cntlid mismatch for I/O qpair CONNECT"); - nvmf_connect_invalid_parameters(nc, true, - offsetof(struct nvmf_fabric_connect_data, cntlid)); - goto error; - } - if (memcmp(io_controller->hostnqn, data->hostnqn, - sizeof(data->hostid)) != 0) { - pthread_mutex_unlock(&io_na_mutex); - warnx("host NQN mismatch for I/O qpair CONNECT"); - nvmf_connect_invalid_parameters(nc, true, - offsetof(struct nvmf_fabric_connect_data, hostnqn)); - goto error; - } - - if (io_controller->num_io_queues == 0) { - pthread_mutex_unlock(&io_na_mutex); - warnx("Attempt to create I/O qpair without enabled queues"); - nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); - goto error; - } - if (qid > io_controller->num_io_queues) { - pthread_mutex_unlock(&io_na_mutex); - warnx("Attempt to create invalid I/O qpair %u", qid); - nvmf_connect_invalid_parameters(nc, false, - offsetof(struct nvmf_fabric_connect_cmd, qid)); - goto error; - } - if (io_controller->io_qpairs[qid - 1] != NULL) { - pthread_mutex_unlock(&io_na_mutex); - warnx("Attempt to re-create I/O qpair %u", qid); - nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR); - goto error; - } - - error = nvmf_finish_accept(nc, io_controller->cntlid); - if (error != 0) { - pthread_mutex_unlock(&io_na_mutex); - warnc(error, "Failed to send CONNECT response"); - goto error; - } - - ioc = io_controller; - ioc->active_io_queues++; - ioc->io_qpairs[qid - 1] = qp; - ioc->io_sockets[qid - 1] = s; - pthread_mutex_unlock(&io_na_mutex); - - nvmf_free_capsule(nc); - - handle_io_qpair(ioc, qp, qid); - return; - -error: - nvmf_free_capsule(nc); - close(s); -} - -static void * -io_socket_thread(void *arg) -{ - struct nvmf_fabric_connect_data data; - struct nvmf_qpair_params qparams; - const struct nvmf_fabric_connect_cmd *cmd; - struct nvmf_capsule *nc; - struct nvmf_qpair *qp; - int s; - - pthread_detach(pthread_self()); - - s = (intptr_t)arg; - memset(&qparams, 0, sizeof(qparams)); - qparams.tcp.fd = s; - - nc = NULL; - qp = nvmf_accept(io_na, &qparams, &nc, &data); - if (qp == NULL) { - warnx("Failed to create I/O qpair: %s", - nvmf_association_error(io_na)); - goto error; - } - - if (kernel_io) { - ctl_handoff_qpair(qp, nvmf_capsule_sqe(nc), &data); - goto error; - } - - if (strcmp(data.subnqn, nqn) != 0) { - warn("I/O qpair with invalid SubNQN: %.*s", - (int)sizeof(data.subnqn), data.subnqn); - nvmf_connect_invalid_parameters(nc, true, - offsetof(struct nvmf_fabric_connect_data, subnqn)); - goto error; - } - - /* Is this an admin or I/O queue pair? */ - cmd = nvmf_capsule_sqe(nc); - if (cmd->qid == 0) - connect_admin_qpair(s, qp, nc, &data); - else - connect_io_qpair(s, qp, nc, &data, le16toh(cmd->qid)); - nvmf_free_qpair(qp); - return (NULL); - -error: - if (nc != NULL) - nvmf_free_capsule(nc); - if (qp != NULL) - nvmf_free_qpair(qp); - close(s); - return (NULL); -} - -void -handle_io_socket(int s) -{ - pthread_t thr; - int error; - - error = pthread_create(&thr, NULL, io_socket_thread, - (void *)(uintptr_t)s); - if (error != 0) { - warnc(error, "Failed to create I/O qpair thread"); - close(s); - } -} diff --git a/usr.sbin/nvmfd/nvmfd.8 b/usr.sbin/nvmfd/nvmfd.8 deleted file mode 100644 index 1076583c417c..000000000000 --- a/usr.sbin/nvmfd/nvmfd.8 +++ /dev/null @@ -1,131 +0,0 @@ -.\" -.\" SPDX-License-Identifier: BSD-2-Clause -.\" -.\" Copyright (c) 2024 Chelsio Communications, Inc. -.\" -.Dd July 25, 2024 -.Dt NVMFD 8 -.Os -.Sh NAME -.Nm nvmfd -.Nd "NVMeoF controller daemon" -.Sh SYNOPSIS -.Nm -.Fl K -.Op Fl dFGg -.Op Fl H Ar MAXH2CDATA -.Op Fl P Ar port -.Op Fl p Ar port -.Op Fl t Ar transport -.Op Fl n Ar subnqn -.Nm -.Op Fl dFGg -.Op Fl H Ar MAXH2CDATA -.Op Fl P Ar port -.Op Fl p Ar port -.Op Fl t Ar transport -.Op Fl n Ar subnqn -.Ar device -.Op Ar device ... -.Sh DESCRIPTION -.Nm -accepts incoming NVMeoF connections for both I/O and discovery controllers. -.Nm -can either implement a single dynamic I/O controller in user mode or hand -off incoming I/O controller connections to -.Xr nvmft 4 . -A dynamic discovery controller service is always provided in user mode. -.Pp -The following options are available: -.Bl -tag -width "-t transport" -.It Fl F -Permit remote hosts to disable SQ flow control. -.It Fl G -Permit remote hosts to enable PDU data digests for the TCP transport. -.It Fl g -Permit remote hosts to enable PDU header digests for the TCP transport. -.It Fl H -Set the MAXH2CDATA value advertised to the remote host for the TCP transport. -This value is in bytes and determines the maximum data payload size for -data PDUs sent by the remote host. -The value must be at least 4096 and defaults to 256KiB. -.It Fl K -Enable kernel mode which hands off incoming I/O controller connections to -.Xr nvmft 4 . -.It Fl P Ar port -Use -.Ar port -as the listen TCP port for the discovery controller service. -The default value is 8009. -.It Fl d -Enable debug mode. -The daemon sends any errors to standard output and does not place -itself in the background. -.It Fl p Ar port -Use -.Ar port -as the listen TCP port for the I/O controller service. -By default an unused ephemeral port will be chosen. -.It Fl n Ar subnqn -The Subsystem NVMe Qualified Name for the I/O controller. -If an explicit NQN is not given, a default value is generated from the -current host's UUID obtained from the -.Vt kern.hostuuid -sysctl. -.It Fl t Ar transport -The transport type to use. -The default transport is -.Dq tcp . -.It Ar device -When implementing a user mode I/O controller, -one or more -.Ar device -arguments must be specified. -Each -.Ar device -describes the backing store for a namespace exported to remote hosts. -Devices can be specified using one of the following syntaxes: -.Bl -tag -width "ramdisk:size" -.It Pa pathname -File or disk device -.It ramdisk : Ns Ar size -Allocate a memory disk with the given -.Ar size . -.Ar size -may use any of the suffixes supported by -.Xr expand_number 3 . -.El -.El -.Sh FILES -.Bl -tag -width "/var/run/nvmfd.pid" -compact -.It Pa /var/run/nvmfd.pid -The default location of the -.Nm -PID file. -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr ctl 4 , -.Xr nvmft 4 , -.Xr ctladm 8 , -.Xr ctld 8 -.Sh HISTORY -The -.Nm -module first appeared in -.Fx 15.0 . -.Sh AUTHORS -The -.Nm -subsystem was developed by -.An John Baldwin Aq Mt jhb@FreeBSD.org -under sponsorship from Chelsio Communications, Inc. -.Sh BUGS -The discovery controller and kernel mode functionality of -.Nm -should be merged into -.Xr ctld 8 . -.Pp -Additional parameters such as -queue sizes should be configurable. diff --git a/usr.sbin/nvmfd/nvmfd.c b/usr.sbin/nvmfd/nvmfd.c deleted file mode 100644 index df6f400b40e5..000000000000 --- a/usr.sbin/nvmfd/nvmfd.c +++ /dev/null @@ -1,271 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/param.h> -#include <sys/event.h> -#include <sys/linker.h> -#include <sys/module.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <assert.h> -#include <err.h> -#include <errno.h> -#include <libnvmf.h> -#include <libutil.h> -#include <netdb.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -bool data_digests = false; -bool header_digests = false; -bool flow_control_disable = false; -bool kernel_io = false; -uint32_t maxh2cdata = 256 * 1024; - -static const char *subnqn; -static volatile bool quit = false; - -static void -usage(void) -{ - fprintf(stderr, "nvmfd -K [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n" - "nvmfd [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n" - "\tdevice [device [...]]\n" - "\n" - "Devices use one of the following syntaxes:\n" - "\tpathame - file or disk device\n" - "\tramdisk:size - memory disk of given size\n"); - exit(1); -} - -static void -handle_sig(int sig __unused) -{ - quit = true; -} - -static void -register_listen_socket(int kqfd, int s, void *udata) -{ - struct kevent kev; - - if (listen(s, -1) != 0) - err(1, "listen"); - - EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, udata); - if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == -1) - err(1, "kevent: failed to add listen socket"); -} - -static void -create_passive_sockets(int kqfd, const char *port, bool discovery) -{ - struct addrinfo hints, *ai, *list; - bool created; - int error, s; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_protocol = IPPROTO_TCP; - error = getaddrinfo(NULL, port, &hints, &list); - if (error != 0) - errx(1, "%s", gai_strerror(error)); - created = false; - - for (ai = list; ai != NULL; ai = ai->ai_next) { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s == -1) - continue; - - if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) { - close(s); - continue; - } - - if (discovery) { - register_listen_socket(kqfd, s, (void *)1); - } else { - register_listen_socket(kqfd, s, (void *)2); - discovery_add_io_controller(s, subnqn); - } - created = true; - } - - freeaddrinfo(list); - if (!created) - err(1, "Failed to create any listen sockets"); -} - -static void -handle_connections(int kqfd) -{ - struct kevent ev; - int s; - - signal(SIGHUP, handle_sig); - signal(SIGINT, handle_sig); - signal(SIGQUIT, handle_sig); - signal(SIGTERM, handle_sig); - - while (!quit) { - if (kevent(kqfd, NULL, 0, &ev, 1, NULL) == -1) { - if (errno == EINTR) - continue; - err(1, "kevent"); - } - - assert(ev.filter == EVFILT_READ); - - s = accept(ev.ident, NULL, NULL); - if (s == -1) { - warn("accept"); - continue; - } - - switch ((uintptr_t)ev.udata) { - case 1: - handle_discovery_socket(s); - break; - case 2: - handle_io_socket(s); - break; - default: - __builtin_unreachable(); - } - } -} - -int -main(int ac, char **av) -{ - struct pidfh *pfh; - const char *dport, *ioport, *transport; - pid_t pid; - uint64_t value; - int ch, error, kqfd; - bool daemonize; - static char nqn[NVMF_NQN_MAX_LEN]; - - /* 7.4.9.3 Default port for discovery */ - dport = "8009"; - - pfh = NULL; - daemonize = true; - ioport = "0"; - subnqn = NULL; - transport = "tcp"; - while ((ch = getopt(ac, av, "dFgGH:Kn:P:p:t:")) != -1) { - switch (ch) { - case 'd': - daemonize = false; - break; - case 'F': - flow_control_disable = true; - break; - case 'G': - data_digests = true; - break; - case 'g': - header_digests = true; - break; - case 'H': - if (expand_number(optarg, &value) != 0) - errx(1, "Invalid MAXH2CDATA value %s", optarg); - if (value < 4096 || value > UINT32_MAX || - value % 4 != 0) - errx(1, "Invalid MAXH2CDATA value %s", optarg); - maxh2cdata = value; - break; - case 'K': - kernel_io = true; - break; - case 'n': - subnqn = optarg; - break; - case 'P': - dport = optarg; - break; - case 'p': - ioport = optarg; - break; - case 't': - transport = optarg; - break; - default: - usage(); - } - } - - av += optind; - ac -= optind; - - if (kernel_io) { - if (ac > 0) - usage(); - if (modfind("nvmft") == -1 && kldload("nvmft") == -1) - warn("couldn't load nvmft"); - } else { - if (ac < 1) - usage(); - } - - if (strcasecmp(transport, "tcp") == 0) { - } else - errx(1, "Invalid transport %s", transport); - - if (subnqn == NULL) { - error = nvmf_nqn_from_hostuuid(nqn); - if (error != 0) - errc(1, error, "Failed to generate NQN"); - subnqn = nqn; - } - - if (!kernel_io) - register_devices(ac, av); - - init_discovery(); - init_io(subnqn); - - if (daemonize) { - pfh = pidfile_open(NULL, 0600, &pid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "Daemon already running, pid: %jd", - (intmax_t)pid); - warn("Cannot open or create pidfile"); - } - - if (daemon(0, 0) != 0) { - pidfile_remove(pfh); - err(1, "Failed to fork into the background"); - } - - pidfile_write(pfh); - } - - kqfd = kqueue(); - if (kqfd == -1) { - pidfile_remove(pfh); - err(1, "kqueue"); - } - - create_passive_sockets(kqfd, dport, true); - create_passive_sockets(kqfd, ioport, false); - - handle_connections(kqfd); - shutdown_io(); - if (pfh != NULL) - pidfile_remove(pfh); - return (0); -} diff --git a/usr.sbin/services_mkdb/services b/usr.sbin/services_mkdb/services index 4a5b6863d92d..c5f950831767 100644 --- a/usr.sbin/services_mkdb/services +++ b/usr.sbin/services_mkdb/services @@ -893,7 +893,7 @@ biff 512/udp comsat #used by mail system to notify users # processes on the same machine login 513/tcp #remote login a la telnet; # automatic authentication performed -# based on priviledged port numbers +# based on privileged port numbers # and distributed data bases which # identify "authentication domains" who 513/udp whod #maintains data bases showing who's diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8 index fa61e78eaf3e..d39d9fdc8f5a 100644 --- a/usr.sbin/syslogd/syslogd.8 +++ b/usr.sbin/syslogd/syslogd.8 @@ -403,7 +403,7 @@ The message can contain a priority code, which should be a preceding decimal number in angle braces, for example, -.Sq Aq 5 . +.Sq <5> . This priority code should map into the priorities defined in the include file .In sys/syslog.h . diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c index fe7427130b78..81bbbbe66be8 100644 --- a/usr.sbin/syslogd/syslogd.c +++ b/usr.sbin/syslogd/syslogd.c @@ -1830,15 +1830,14 @@ fprintlog_write(struct filed *f, struct iovlist *il, int flags) case EHOSTUNREACH: case EHOSTDOWN: case EADDRNOTAVAIL: + case EAGAIN: + case ECONNREFUSED: break; /* case EBADF: */ /* case EACCES: */ /* case ENOTSOCK: */ /* case EFAULT: */ /* case EMSGSIZE: */ - /* case EAGAIN: */ - /* case ENOBUFS: */ - /* case ECONNREFUSED: */ default: dprintf("removing entry: errno=%d\n", e); f->f_type = F_UNUSED; diff --git a/usr.sbin/unbound/setup/local-unbound-setup.sh b/usr.sbin/unbound/setup/local-unbound-setup.sh index d52534b46fa3..d57d74952fc7 100755 --- a/usr.sbin/unbound/setup/local-unbound-setup.sh +++ b/usr.sbin/unbound/setup/local-unbound-setup.sh @@ -259,7 +259,7 @@ gen_unbound_conf() { echo " pidfile: ${pidfile}" echo " auto-trust-anchor-file: ${anchor}" if [ "${use_tls}" = "yes" ] ; then - echo " tls-system-cert: yes" + echo " tls-cert-bundle: /etc/ssl/cert.pem" fi echo "" if [ -f "${forward_conf}" ] ; then diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index 88b467486da1..27123f2143d0 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -63,25 +63,25 @@ static long fetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok); static void parseargs(int, char *[]); -static int seconds_to_pow2ns(int); static void sighandler(int); static void watchdog_loop(void); static int watchdog_init(void); static int watchdog_onoff(int onoff); -static int watchdog_patpat(u_int timeout); +static int watchdog_patpat(sbintime_t); static void usage(void); -static int tstotv(struct timeval *tv, struct timespec *ts); static int tvtohz(struct timeval *tv); static int debugging = 0; static int end_program = 0; static const char *pidfile = _PATH_VARRUN "watchdogd.pid"; -static u_int timeout = WD_TO_128SEC; +static sbintime_t timeout = 128 * SBT_1S; static u_int exit_timeout = WD_TO_NEVER; static u_int pretimeout = 0; static u_int timeout_sec; static u_int nap = 10; +#ifdef notyet static int passive = 0; +#endif static int is_daemon = 0; static int is_dry_run = 0; /* do not arm the watchdog, only report on timing of the watch @@ -174,38 +174,23 @@ main(int argc, char *argv[]) pidfile_remove(pfh); return (EX_OK); } else { - if (passive) - timeout |= WD_PASSIVE; - else - timeout |= WD_ACTIVE; if (watchdog_patpat(timeout) < 0) err(EX_OSERR, "patting the dog"); return (EX_OK); } } -static void -pow2ns_to_ts(int pow2ns, struct timespec *ts) -{ - uint64_t ns; - - ns = 1ULL << pow2ns; - ts->tv_sec = ns / 1000000000ULL; - ts->tv_nsec = ns % 1000000000ULL; -} - /* * Convert a timeout in seconds to N where 2^N nanoseconds is close to * "seconds". * * The kernel expects the timeouts for watchdogs in "2^N nanosecond format". */ -static u_int -parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) +static sbintime_t +parse_timeout_to_sbt(char opt, const char *longopt, const char *myoptarg) { - double a; - u_int rv; - struct timespec ts; + long a; + sbintime_t rv; struct timeval tv; int ticks; char shortopt[] = "- "; @@ -216,19 +201,17 @@ parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) a = fetchtimeout(opt, longopt, myoptarg, 1); if (a == 0) - rv = WD_TO_NEVER; + rv = 0; else - rv = seconds_to_pow2ns(a); - pow2ns_to_ts(rv, &ts); - tstotv(&tv, &ts); + rv = a * SBT_1S; + tv = sbttotv(rv); ticks = tvtohz(&tv); if (debugging) { printf("Timeout for %s%s " - "is 2^%d nanoseconds " - "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n", + "is " + "(in: %s sec -> out: %jd sec %ld us -> %d ticks)\n", longopt ? "-" : "", longopt ? longopt : shortopt, - rv, - myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks); + myoptarg, (intmax_t)tv.tv_sec, tv.tv_usec, ticks); } if (ticks <= 0) { errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt); @@ -364,7 +347,7 @@ watchdog_loop(void) } if (failed == 0) - watchdog_patpat(timeout|WD_ACTIVE); + watchdog_patpat(timeout); waited = watchdog_check_dogfunction_time(&ts_start, &ts_end); if (nap - waited > 0) @@ -387,13 +370,22 @@ try_end: * to keep the watchdog from firing. */ static int -watchdog_patpat(u_int t) +watchdog_patpat(sbintime_t sbt) { if (is_dry_run) return 0; - return ioctl(fd, WDIOCPATPAT, &t); + return ioctl(fd, WDIOC_SETTIMEOUT, &sbt); +} + +static int +watchdog_control(u_int control) +{ + if (is_dry_run) + return (0); + + return ioctl(fd, WDIOC_CONTROL, &control); } /* @@ -420,7 +412,7 @@ watchdog_onoff(int onoff) warn("setting WDIOC_SETSOFT %d", softtimeout_set); return (error); } - error = watchdog_patpat((timeout|WD_ACTIVE)); + error = watchdog_patpat(timeout); if (error) { warn("watchdog_patpat failed"); goto failsafe; @@ -452,12 +444,12 @@ watchdog_onoff(int onoff) } } /* pat one more time for good measure */ - return watchdog_patpat((timeout|WD_ACTIVE)); + return watchdog_patpat(timeout); } else { - return watchdog_patpat(exit_timeout); + return watchdog_control(WD_CTRL_DISABLE); } failsafe: - watchdog_patpat(exit_timeout); + watchdog_control(WD_CTRL_DISABLE); return (error); } @@ -567,15 +559,6 @@ timeout_act_str2int(const char *lopt, const char *acts) return rv; } -int -tstotv(struct timeval *tv, struct timespec *ts) -{ - - tv->tv_sec = ts->tv_sec; - tv->tv_usec = ts->tv_nsec / 1000; - return 0; -} - /* * Convert a timeval to a number of ticks. * Mostly copied from the kernel. @@ -647,30 +630,6 @@ tvtohz(struct timeval *tv) return ((int)ticks); } -static int -seconds_to_pow2ns(int seconds) -{ - uint64_t power; - uint64_t ns; - uint64_t shifted; - - if (seconds <= 0) - errx(1, "seconds %d < 0", seconds); - ns = ((uint64_t)seconds) * 1000000000ULL; - power = flsll(ns); - shifted = 1ULL << power; - if (shifted <= ns) { - power++; - } - if (debugging) { - printf("shifted %lld\n", (long long)shifted); - printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n", - seconds, (long long)ns, (int)power); - } - return (power); -} - - /* * Handle the few command line arguments supported. */ @@ -683,8 +642,7 @@ parseargs(int argc, char *argv[]) const char *lopt; /* Get the default value of timeout_sec from the default timeout. */ - pow2ns_to_ts(timeout, &ts); - timeout_sec = ts.tv_sec; + timeout_sec = sbintime_getsec(timeout); /* * if we end with a 'd' aka 'watchdogd' then we are the daemon program, @@ -727,10 +685,10 @@ parseargs(int argc, char *argv[]) break; case 't': timeout_sec = atoi(optarg); - timeout = parse_timeout_to_pow2ns(c, NULL, optarg); + timeout = parse_timeout_to_sbt(c, NULL, optarg); if (debugging) - printf("Timeout is 2^%d nanoseconds\n", - timeout); + printf("Timeout is %d\n", + (int)(timeout / SBT_1S)); break; case 'T': carp_thresh_seconds = @@ -740,7 +698,7 @@ parseargs(int argc, char *argv[]) do_timedog = 1; break; case 'x': - exit_timeout = parse_timeout_to_pow2ns(c, NULL, optarg); + exit_timeout = parse_timeout_to_sbt(c, NULL, optarg); if (exit_timeout != 0) exit_timeout |= WD_ACTIVE; break; |