xref: /illumos-gate/usr/src/cmd/modload/rem_drv.c (revision 7e485317)
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  */
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <errno.h>
30 #include <libintl.h>
31 #include <string.h>
32 #include <fcntl.h>
33 #include <sys/buf.h>
34 #include <sys/stat.h>
35 #include <sys/wait.h>
36 #include <limits.h>
37 #include <malloc.h>
38 #include <locale.h>
39 #include <ftw.h>
40 #include <sys/types.h>
41 #include <sys/mkdev.h>
42 #include <sys/modctl.h>
43 #include <sys/instance.h>
44 #include <libdevinfo.h>
45 
46 #include "addrem.h"
47 #include "errmsg.h"
48 
49 #define	FT_DEPTH	15	/* device tree depth for nftw() */
50 
51 static void usage(void);
52 static void cleanup_devfs_attributes(char *, char *);
53 
54 int
55 main(int argc, char *argv[])
56 {
57 	int opt;
58 	char *basedir = NULL, *driver_name = NULL;
59 	int server = 0, mod_unloaded = 0;
60 	int modid, found;
61 	char maj_num[MAX_STR_MAJOR + 1];
62 	int cleanup = 0;
63 	int err;
64 	int n_flag = 0;
65 
66 	(void) setlocale(LC_ALL, "");
67 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
68 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
69 #endif
70 	(void) textdomain(TEXT_DOMAIN);
71 
72 	/*  must be run by root */
73 
74 	if (getuid() != 0) {
75 		(void) fprintf(stderr, gettext(ERR_NOT_ROOT));
76 		exit(1);
77 	}
78 
79 	while ((opt = getopt(argc, argv, "b:Cn")) != -1) {
80 		switch (opt) {
81 		case 'b' :
82 			server = 1;
83 			basedir = calloc(strlen(optarg) + 1, 1);
84 			if (basedir == NULL) {
85 				(void) fprintf(stderr, gettext(ERR_NO_MEM));
86 				exit(1);
87 			}
88 			(void) strcat(basedir, optarg);
89 			break;
90 		case 'C':
91 			cleanup = 1;
92 			break;
93 		case 'n':
94 			n_flag = 1;
95 			break;
96 		case '?' :
97 			usage();
98 			exit(1);
99 		}
100 	}
101 
102 	if (argv[optind] != NULL) {
103 		driver_name = calloc(strlen(argv[optind]) + 1, 1);
104 		if (driver_name == NULL) {
105 			(void) fprintf(stderr, gettext(ERR_NO_MEM));
106 			exit(1);
107 
108 		}
109 		(void) strcat(driver_name, argv[optind]);
110 		/*
111 		 * check for extra args
112 		 */
113 		if ((optind + 1) != argc) {
114 			usage();
115 			exit(1);
116 		}
117 
118 	} else {
119 		usage();
120 		exit(1);
121 	}
122 
123 	/* set up add_drv filenames */
124 	if ((build_filenames(basedir)) == ERROR) {
125 		exit(1);
126 	}
127 
128 	/* must be only running version of add_drv/mod_drv/rem_drv */
129 	enter_lock();
130 
131 	if ((check_perms_aliases(1, 1)) == ERROR)
132 		err_exit();
133 
134 	if ((check_name_to_major(R_OK | W_OK)) == ERROR)
135 		err_exit();
136 
137 	/* look up the major number of the driver being removed. */
138 	if ((found = get_major_no(driver_name, name_to_major)) == ERROR) {
139 		(void) fprintf(stderr, gettext(ERR_MAX_MAJOR), name_to_major);
140 		err_exit();
141 	}
142 	if (found == UNIQUE) {
143 		(void) fprintf(stderr, gettext(ERR_NOT_INSTALLED),
144 		    driver_name);
145 		err_exit();
146 	}
147 
148 	if (n_flag == 0 && !server) {
149 		mod_unloaded = 1;
150 
151 		/* get the module id for this driver */
152 		get_modid(driver_name, &modid);
153 
154 		/* module is installed */
155 		if (modid != -1) {
156 			if (modctl(MODUNLOAD, modid) < 0) {
157 				perror(NULL);
158 				(void) fprintf(stderr, gettext(ERR_MODUN),
159 				    driver_name);
160 				mod_unloaded = 0;
161 			}
162 		}
163 		/* unload driver.conf file */
164 		if (modctl(MODUNLOADDRVCONF, (major_t)found) < 0) {
165 			perror(NULL);
166 			(void) fprintf(stderr,
167 			    gettext("cannot unload %s.conf\n"), driver_name);
168 		}
169 	}
170 
171 	if (mod_unloaded && (modctl(MODREMMAJBIND, (major_t)found) < 0)) {
172 		perror(NULL);
173 		(void) fprintf(stderr, gettext(ERR_MODREMMAJ), found);
174 	}
175 	/*
176 	 * add driver to rem_name_to_major; if this fails, don`t
177 	 * delete from name_to_major
178 	 */
179 	(void) sprintf(maj_num, "%d", found);
180 
181 	if (append_to_file(driver_name, maj_num,
182 	    rem_name_to_major, ' ', " ", 0) == ERROR) {
183 		(void) fprintf(stderr, gettext(ERR_NO_UPDATE),
184 		    rem_name_to_major);
185 		err_exit();
186 	}
187 
188 	/*
189 	 * If removing the driver from the running system, notify
190 	 * kernel dynamically to remove minor perm entries.
191 	 */
192 	if ((n_flag == 0) &&
193 	    (basedir == NULL || (strcmp(basedir, "/") == 0))) {
194 		err = devfs_rm_minor_perm(driver_name, log_minorperm_error);
195 		if (err != 0) {
196 			(void) fprintf(stderr, gettext(ERR_UPDATE_PERM),
197 			    driver_name, err);
198 		}
199 	}
200 
201 	/*
202 	 * delete references to driver in add_drv/rem_drv database
203 	 */
204 	remove_entry(CLEAN_ALL, driver_name);
205 
206 	/*
207 	 * Optionally clean up any dangling devfs shadow nodes for
208 	 * this driver so that, in the event the driver is re-added
209 	 * to the system, newly created nodes won't incorrectly
210 	 * pick up these stale shadow node permissions.
211 	 */
212 	if ((n_flag == 0) && cleanup) {
213 		if ((basedir == NULL || (strcmp(basedir, "/") == 0))) {
214 			err = modctl(MODREMDRVCLEANUP, driver_name, 0, NULL);
215 			if (err != 0) {
216 				(void) fprintf(stderr,
217 				    gettext(ERR_REMDRV_CLEANUP),
218 				    driver_name, err);
219 			}
220 		} else if (strcmp(basedir, "/") != 0) {
221 			cleanup_devfs_attributes(basedir, driver_name);
222 		}
223 	}
224 
225 	exit_unlock();
226 
227 	return (NOERR);
228 }
229 
230 /*
231  * Optionally remove attribute nodes for a driver when
232  * removing drivers on a mounted root image.  Useful
233  * when reprovisioning a machine to return to default
234  * permission/ownership settings if the driver is
235  * re-installed.
236  */
237 typedef struct cleanup_arg {
238 	char	*ca_basedir;
239 	char	*ca_drvname;
240 } cleanup_arg_t;
241 
242 
243 /*
244  * Callback to remove a minor node for a device
245  */
246 /*ARGSUSED*/
247 static int
248 cleanup_minor_walker(void *cb_arg, const char *minor_path)
249 {
250 	if (unlink(minor_path) == -1) {
251 		(void) fprintf(stderr, "rem_drv: error removing %s\n",
252 		    minor_path, strerror(errno));
253 	}
254 	return (DI_WALK_CONTINUE);
255 }
256 
257 /*
258  * Callback for each device registered in the binding file (path_to_inst)
259  */
260 static int
261 cleanup_device_walker(void *cb_arg, const char *inst_path,
262     int inst_number, const char *inst_driver)
263 {
264 	char path[MAXPATHLEN];
265 	cleanup_arg_t *arg = (cleanup_arg_t *)cb_arg;
266 	int rv = DI_WALK_CONTINUE;
267 
268 	if (strcmp(inst_driver, arg->ca_drvname) == 0) {
269 		if (snprintf(path, MAXPATHLEN, "%s/devices%s",
270 		    arg->ca_basedir, inst_path) < MAXPATHLEN) {
271 			rv = devfs_walk_minor_nodes(path,
272 			    cleanup_minor_walker, NULL);
273 		}
274 	}
275 	return (rv);
276 }
277 
278 static void
279 cleanup_devfs_attributes(char *basedir, char *driver_name)
280 {
281 	int rv;
282 	cleanup_arg_t arg;
283 	char binding_path[MAXPATHLEN+1];
284 
285 	(void) snprintf(binding_path, MAXPATHLEN,
286 	    "%s%s", basedir, INSTANCE_FILE);
287 
288 	arg.ca_basedir = basedir;
289 	arg.ca_drvname = driver_name;
290 	(void) devfs_parse_binding_file(binding_path,
291 	    cleanup_device_walker, (void *)&arg);
292 }
293 
294 static void
295 usage()
296 {
297 	(void) fprintf(stderr, gettext(REM_USAGE1));
298 }
299