1*dde6d513SAndy Fiddaman /*
2*dde6d513SAndy Fiddaman * This file and its contents are supplied under the terms of the
3*dde6d513SAndy Fiddaman * Common Development and Distribution License ("CDDL"), version 1.0.
4*dde6d513SAndy Fiddaman * You may only use this file in accordance with the terms of version
5*dde6d513SAndy Fiddaman * 1.0 of the CDDL.
6*dde6d513SAndy Fiddaman *
7*dde6d513SAndy Fiddaman * A full copy of the text of the CDDL should have accompanied this
8*dde6d513SAndy Fiddaman * source. A copy of the CDDL is also available via the Internet at
9*dde6d513SAndy Fiddaman * http://www.illumos.org/license/CDDL.
10*dde6d513SAndy Fiddaman */
11*dde6d513SAndy Fiddaman
12*dde6d513SAndy Fiddaman /*
13*dde6d513SAndy Fiddaman * Copyright 2026 Oxide Computer Company
14*dde6d513SAndy Fiddaman */
15*dde6d513SAndy Fiddaman
16*dde6d513SAndy Fiddaman /*
17*dde6d513SAndy Fiddaman * Utility functions common to amdzen commands.
18*dde6d513SAndy Fiddaman */
19*dde6d513SAndy Fiddaman
20*dde6d513SAndy Fiddaman #include <err.h>
21*dde6d513SAndy Fiddaman #include <errno.h>
22*dde6d513SAndy Fiddaman #include <fcntl.h>
23*dde6d513SAndy Fiddaman #include <libdevinfo.h>
24*dde6d513SAndy Fiddaman #include <limits.h>
25*dde6d513SAndy Fiddaman #include <stdio.h>
26*dde6d513SAndy Fiddaman #include <stdlib.h>
27*dde6d513SAndy Fiddaman #include <string.h>
28*dde6d513SAndy Fiddaman #include <unistd.h>
29*dde6d513SAndy Fiddaman #include <sys/types.h>
30*dde6d513SAndy Fiddaman #include <sys/stat.h>
31*dde6d513SAndy Fiddaman
32*dde6d513SAndy Fiddaman #include "amdzen_common.h"
33*dde6d513SAndy Fiddaman
34*dde6d513SAndy Fiddaman /*
35*dde6d513SAndy Fiddaman * Traditionally we expected to be given the path to a character device file
36*dde6d513SAndy Fiddaman * somewhere under /devices, where the device minor referred to a specific
37*dde6d513SAndy Fiddaman * data fabric (DF) instance. It is often more convenient to be able to give
38*dde6d513SAndy Fiddaman * a higher-level identifier instead, such as a DF number, optionally
39*dde6d513SAndy Fiddaman * prefixed with the driver name. This function distinguishes between the
40*dde6d513SAndy Fiddaman * two cases by attempting to parse the argument as a DF identifier. If that
41*dde6d513SAndy Fiddaman * succeeds, we map it to the corresponding minor of the named driver and
42*dde6d513SAndy Fiddaman * open that, otherwise the argument is treated as a device path and opened
43*dde6d513SAndy Fiddaman * directly. Either way the caller receives an open file descriptor, or -1
44*dde6d513SAndy Fiddaman * after a warning has been issued.
45*dde6d513SAndy Fiddaman */
46*dde6d513SAndy Fiddaman int
amdzen_open_device(const char * driver,const char * arg,int oflag)47*dde6d513SAndy Fiddaman amdzen_open_device(const char *driver, const char *arg, int oflag)
48*dde6d513SAndy Fiddaman {
49*dde6d513SAndy Fiddaman struct stat st;
50*dde6d513SAndy Fiddaman di_node_t root, node;
51*dde6d513SAndy Fiddaman di_minor_t minor;
52*dde6d513SAndy Fiddaman const char *errstr, *id;
53*dde6d513SAndy Fiddaman char mname[64];
54*dde6d513SAndy Fiddaman char *bpath, *path;
55*dde6d513SAndy Fiddaman size_t drvlen;
56*dde6d513SAndy Fiddaman int df, fd;
57*dde6d513SAndy Fiddaman
58*dde6d513SAndy Fiddaman /* If there is a driver prefix provided, accept and remove it */
59*dde6d513SAndy Fiddaman id = arg;
60*dde6d513SAndy Fiddaman drvlen = strlen(driver);
61*dde6d513SAndy Fiddaman if (strncmp(id, driver, drvlen) == 0)
62*dde6d513SAndy Fiddaman id += drvlen;
63*dde6d513SAndy Fiddaman
64*dde6d513SAndy Fiddaman df = (int)strtonumx(id, 0, INT_MAX, &errstr, 0);
65*dde6d513SAndy Fiddaman if (errstr != NULL) {
66*dde6d513SAndy Fiddaman /* Not a DF identifier, so treat it as a device path */
67*dde6d513SAndy Fiddaman if ((fd = open(arg, oflag)) < 0) {
68*dde6d513SAndy Fiddaman warn("failed to open %s", arg);
69*dde6d513SAndy Fiddaman return (-1);
70*dde6d513SAndy Fiddaman }
71*dde6d513SAndy Fiddaman if (fstat(fd, &st) != 0) {
72*dde6d513SAndy Fiddaman warn("failed to stat %s", arg);
73*dde6d513SAndy Fiddaman (void) close(fd);
74*dde6d513SAndy Fiddaman return (-1);
75*dde6d513SAndy Fiddaman }
76*dde6d513SAndy Fiddaman if (!S_ISCHR(st.st_mode)) {
77*dde6d513SAndy Fiddaman warnx("%s is not a character device", arg);
78*dde6d513SAndy Fiddaman (void) close(fd);
79*dde6d513SAndy Fiddaman return (-1);
80*dde6d513SAndy Fiddaman }
81*dde6d513SAndy Fiddaman return (fd);
82*dde6d513SAndy Fiddaman }
83*dde6d513SAndy Fiddaman
84*dde6d513SAndy Fiddaman root = di_init_driver(driver, DINFOSUBTREE | DINFOMINOR);
85*dde6d513SAndy Fiddaman if (root == DI_NODE_NIL) {
86*dde6d513SAndy Fiddaman warn("failed to take a devinfo snapshot with %s attached",
87*dde6d513SAndy Fiddaman driver);
88*dde6d513SAndy Fiddaman return (-1);
89*dde6d513SAndy Fiddaman }
90*dde6d513SAndy Fiddaman
91*dde6d513SAndy Fiddaman /* These are all single-instance drivers with one minor per DF */
92*dde6d513SAndy Fiddaman node = di_drv_first_node(driver, root);
93*dde6d513SAndy Fiddaman if (node == DI_NODE_NIL) {
94*dde6d513SAndy Fiddaman warnx("failed to find a devinfo node for %s", driver);
95*dde6d513SAndy Fiddaman di_fini(root);
96*dde6d513SAndy Fiddaman return (-1);
97*dde6d513SAndy Fiddaman }
98*dde6d513SAndy Fiddaman
99*dde6d513SAndy Fiddaman (void) snprintf(mname, sizeof (mname), "%s.%d", driver, df);
100*dde6d513SAndy Fiddaman minor = DI_MINOR_NIL;
101*dde6d513SAndy Fiddaman while ((minor = di_minor_next(node, minor)) != DI_MINOR_NIL) {
102*dde6d513SAndy Fiddaman if (strcmp(di_minor_name(minor), mname) == 0)
103*dde6d513SAndy Fiddaman break;
104*dde6d513SAndy Fiddaman }
105*dde6d513SAndy Fiddaman if (minor == DI_MINOR_NIL) {
106*dde6d513SAndy Fiddaman warnx("failed to find minor %s on %s%d -- no such DF?",
107*dde6d513SAndy Fiddaman mname, di_driver_name(node), di_instance(node));
108*dde6d513SAndy Fiddaman di_fini(root);
109*dde6d513SAndy Fiddaman return (-1);
110*dde6d513SAndy Fiddaman }
111*dde6d513SAndy Fiddaman
112*dde6d513SAndy Fiddaman bpath = di_devfs_minor_path(minor);
113*dde6d513SAndy Fiddaman if (bpath == NULL) {
114*dde6d513SAndy Fiddaman warn("failed to get minor path for %s%d:%s",
115*dde6d513SAndy Fiddaman di_driver_name(node), di_instance(node),
116*dde6d513SAndy Fiddaman di_minor_name(minor));
117*dde6d513SAndy Fiddaman di_fini(root);
118*dde6d513SAndy Fiddaman return (-1);
119*dde6d513SAndy Fiddaman }
120*dde6d513SAndy Fiddaman if (asprintf(&path, "/devices%s", bpath) < 0) {
121*dde6d513SAndy Fiddaman warn("failed to construct full path for %s", bpath);
122*dde6d513SAndy Fiddaman di_devfs_path_free(bpath);
123*dde6d513SAndy Fiddaman di_fini(root);
124*dde6d513SAndy Fiddaman return (-1);
125*dde6d513SAndy Fiddaman }
126*dde6d513SAndy Fiddaman di_devfs_path_free(bpath);
127*dde6d513SAndy Fiddaman di_fini(root);
128*dde6d513SAndy Fiddaman
129*dde6d513SAndy Fiddaman if ((fd = open(path, oflag)) < 0)
130*dde6d513SAndy Fiddaman warn("failed to open %s", path);
131*dde6d513SAndy Fiddaman free(path);
132*dde6d513SAndy Fiddaman
133*dde6d513SAndy Fiddaman return (fd);
134*dde6d513SAndy Fiddaman }
135