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