1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2025 Oxide Computer Company
25 */
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/wait.h>
30 #include <strings.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <sys/modctl.h>
36 #include <zone.h>
37 #include <spawn.h>
38 #include <err.h>
39 #include <stdbool.h>
40
41
42 static void
usage(void)43 usage(void)
44 {
45 (void) fprintf(stderr,
46 "usage: modunload [-e <exec_file>] -i <id> | <name>\n");
47 exit(EXIT_FAILURE);
48 }
49
50 /*
51 * Execute the user file.
52 */
53 static void
exec_userfile(char * execfile,const struct modinfo * modinfo,char ** envp)54 exec_userfile(char *execfile, const struct modinfo *modinfo, char **envp)
55 {
56 char modid[8], mod0[8];
57 char *child_args[] = {execfile, modid, mod0, NULL};
58
59 (void) snprintf(modid, sizeof (modid), "%d", modinfo->mi_id);
60 (void) snprintf(mod0, sizeof (mod0), "%d",
61 modinfo->mi_msinfo[0].msi_p0);
62
63 const short desired_attrs =
64 POSIX_SPAWN_NOSIGCHLD_NP | POSIX_SPAWN_WAITPID_NP;
65 posix_spawnattr_t attr;
66 int res;
67 if ((res = posix_spawnattr_init(&attr)) != 0 ||
68 (res = posix_spawnattr_setflags(&attr, desired_attrs)) != 0) {
69 errc(EXIT_FAILURE, res, "could not set spawn attrs");
70 }
71
72 pid_t child;
73 if (posix_spawn(&child, execfile, NULL, &attr, child_args, envp) != 0) {
74 err(EXIT_FAILURE, "could not exec %s", execfile);
75 }
76 (void) posix_spawnattr_destroy(&attr);
77
78 int status, error;
79 do {
80 error = waitpid(child, &status, 0);
81 } while (error == -1 && errno == EINTR);
82 if (error < 0) {
83 err(EXIT_FAILURE, "error while waiting for child");
84 }
85
86 if (WEXITSTATUS(status) != 0) {
87 errx(WEXITSTATUS(status),
88 "%s returned error %d.", execfile, status);
89 }
90 }
91
92 /*
93 * Unload a loaded module.
94 */
95 int
main(int argc,char * argv[],char * envp[])96 main(int argc, char *argv[], char *envp[])
97 {
98 int id = -1;
99 char *execfile = NULL;
100 char *unload_name = NULL;
101 int opt;
102 bool has_mod_id = false;
103
104 if (argc < 2)
105 usage();
106
107 while ((opt = getopt(argc, argv, "i:e:")) != -1) {
108 switch (opt) {
109 case 'i':
110 if (has_mod_id) {
111 errx(EXIT_FAILURE,
112 "Only one module id can be specified");
113 }
114 if (sscanf(optarg, "%d", &id) != 1 || id < 0)
115 errx(EXIT_FAILURE, "Invalid id %s", optarg);
116 has_mod_id = true;
117 break;
118 case 'e':
119 execfile = optarg;
120 break;
121 case '?':
122 usage();
123 break;
124 }
125 }
126 argc -= optind;
127 argv += optind;
128
129 if (argc == 1) {
130 unload_name = argv[0];
131 } else if (argc > 1) {
132 errx(EXIT_FAILURE, "Only one module name can be specified");
133 }
134
135 /* One must specify a name (x)or an ID, not both */
136 if (unload_name == NULL && !has_mod_id) {
137 (void) fprintf(stderr,
138 "missing required module id or name\n");
139 usage();
140 } else if (unload_name != NULL && has_mod_id) {
141 (void) fprintf(stderr,
142 "invalid to specify both module id and name\n");
143 usage();
144 }
145
146 if (getzoneid() != GLOBAL_ZONEID) {
147 errx(EXIT_FAILURE,
148 "modunload can only be run from the global zone");
149 }
150
151 struct modinfo modinfo;
152 if (execfile != NULL || unload_name != NULL) {
153 modinfo.mi_id = modinfo.mi_nextid = id;
154 modinfo.mi_info = MI_INFO_ONE;
155 if (unload_name != NULL) {
156 (void) strlcpy(modinfo.mi_name, unload_name,
157 sizeof (modinfo.mi_name));
158 modinfo.mi_info |= MI_INFO_BY_NAME;
159 }
160 if (modctl(MODINFO, id, &modinfo) < 0) {
161 err(EXIT_FAILURE, "can't get module information");
162 }
163 if (unload_name != NULL) {
164 id = modinfo.mi_id;
165 }
166 }
167
168 if (execfile) {
169 exec_userfile(execfile, &modinfo, envp);
170 }
171
172 /*
173 * Unload the module.
174 */
175 if (modctl(MODUNLOAD, id) < 0) {
176 if (errno == EPERM) {
177 errx(EXIT_FAILURE,
178 "Insufficient privileges to unload a module");
179 } else if (id != 0) {
180 err(EXIT_FAILURE, "can't unload the module");
181 }
182 }
183
184 return (EXIT_SUCCESS);
185 }
186