xref: /illumos-gate/usr/src/cmd/modload/update_drv.c (revision a38ddfee9c8c6b6c5a2947ff52fd2338362a4444)
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 2008 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 <locale.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <string.h>
34 #include "addrem.h"
35 #include "errmsg.h"
36 #include "plcysubr.h"
37 
38 /* function prototypes */
39 static void	usage();
40 static int	unload_drv(char *, int, int);
41 
42 
43 /*
44  * try to modunload driver.
45  * return -1 on failure and 0 on success
46  */
47 static int
48 unload_drv(char *driver_name, int force_flag, int verbose_flag)
49 {
50 	int modid;
51 
52 	get_modid(driver_name, &modid);
53 	if (modid != -1) {
54 		if (modctl(MODUNLOAD, modid) < 0) {
55 			(void) fprintf(stderr, gettext(ERR_MODUN), driver_name);
56 			if (force_flag == 0) { /* no force flag */
57 				if (verbose_flag) {
58 					(void) fprintf(stderr,
59 					    gettext(NOUPDATE), driver_name);
60 				}
61 				/* clean up and exit. remove lock file */
62 				err_exit();
63 			}
64 			(void) fprintf(stderr, gettext(FORCE_UPDATE),
65 			    driver_name);
66 
67 			return (-1);
68 		}
69 	}
70 
71 	return (0);
72 }
73 
74 
75 static void
76 usage()
77 {
78 	(void) fprintf(stderr, gettext(UPD_DRV_USAGE));
79 	exit(1);
80 }
81 
82 
83 int
84 main(int argc, char *argv[])
85 {
86 	int	error, opt, major;
87 	int	cleanup_flag = 0;
88 	int	update_conf = 1;	/* reload driver.conf by default */
89 	int	verbose_flag = 0;	/* -v option */
90 	int	force_flag = 0;		/* -f option */
91 	int	a_flag = 0;		/* -a option */
92 	int	d_flag = 0;		/* -d option */
93 	int	i_flag = 0;		/* -i option */
94 	int	l_flag = 0;		/* -l option */
95 	int	m_flag = 0;		/* -m option */
96 	char	*perms = NULL;
97 	char	*aliases = 0;
98 	char	*basedir = NULL;
99 	char	*policy = NULL;
100 	char	*priv = NULL;
101 	char	*driver_name;
102 	int	found;
103 	major_t major_num;
104 	int	rval;
105 
106 	(void) setlocale(LC_ALL, "");
107 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
108 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
109 #endif
110 	(void) textdomain(TEXT_DOMAIN);
111 
112 	/*  must be run by root */
113 	if (getuid() != 0) {
114 		(void) fprintf(stderr, gettext(ERR_NOT_ROOT));
115 		exit(1);
116 	}
117 
118 	while ((opt = getopt(argc, argv, "m:i:b:p:adlfuvP:")) != EOF) {
119 		switch (opt) {
120 		case 'a':
121 			a_flag++;
122 			break;
123 		case 'b':
124 			update_conf = 0;	/* don't update .conf file */
125 			basedir = optarg;
126 			break;
127 		case 'd':
128 			d_flag++;
129 			break;
130 		case 'f':
131 			force_flag++;
132 			break;
133 		case 'i':
134 			i_flag++;
135 			aliases = optarg;
136 			if (check_space_within_quote(aliases) == ERROR) {
137 				(void) fprintf(stderr, gettext(ERR_NO_SPACE),
138 				    aliases);
139 				exit(1);
140 			}
141 			break;
142 		case 'l':	/* private option */
143 			l_flag++;
144 			break;
145 		case 'm':
146 			m_flag++;
147 			perms = optarg;
148 			break;
149 		case 'p':
150 			policy = optarg;
151 			break;
152 		case 'v':
153 			verbose_flag++;
154 			break;
155 		case 'P':
156 			priv = optarg;
157 			break;
158 		case '?' :
159 		default:
160 			usage();
161 		}
162 	}
163 
164 	/*
165 	 * check for flags and extra args
166 	 */
167 	if ((argv[optind] == NULL) || (optind + 1 != argc)) {
168 		usage();
169 	}
170 
171 	/*
172 	 * - cannot be adding and removing at the same time
173 	 * - if -a or -d is specified, it's an error if none of
174 	 *   -i/-m/-p/-P is specified.
175 	 */
176 	if ((a_flag && d_flag) ||
177 	    ((a_flag || d_flag) &&
178 	    !m_flag && !i_flag && priv == NULL && policy == NULL)) {
179 		usage();
180 	}
181 
182 	/*
183 	 * - with -d option or -a option either -i 'identify_name',
184 	 *	-m 'permission',  -p 'policy' or -P 'priv' should be specified
185 	 */
186 	if (m_flag || i_flag || policy != NULL || priv != NULL) {
187 		if (!(a_flag || d_flag))
188 			usage();
189 	}
190 
191 	driver_name = argv[optind];
192 
193 	/* set up update_drv filenames */
194 	if ((build_filenames(basedir)) == ERROR) {
195 		exit(1);
196 	}
197 
198 	/* no lock is needed for listing minor perm entry */
199 	if (l_flag) {
200 		list_entry(minor_perm, driver_name, ":");
201 
202 		return (NOERR);
203 	}
204 
205 	/* must be only running version of add_drv/update_drv/rem_drv */
206 	enter_lock();
207 
208 	if ((check_perms_aliases(m_flag, i_flag)) == ERROR) {
209 		err_exit();
210 	}
211 
212 	/* update_drv doesn't modify /etc/name_to_major file */
213 	if ((check_name_to_major(R_OK)) == ERROR)
214 		err_exit();
215 
216 	if (priv != NULL && check_priv_entry(priv, a_flag) != 0)
217 		err_exit();
218 
219 	if (policy != NULL && (policy = check_plcy_entry(policy, driver_name,
220 	    d_flag ? B_TRUE : B_FALSE)) == NULL)
221 		err_exit();
222 
223 	/*
224 	 * ADD: -a option
225 	 * i_flag: update /etc/driver_aliases
226 	 * m_flag: update /etc/minor_perm
227 	 * -p: update /etc/security/device_policy
228 	 * -P: update /etc/security/extra_privs
229 	 * if force_flag is specified continue w/ the next operation
230 	 */
231 	if (a_flag) {
232 		if (m_flag) {
233 			/* check if the permissions are valid */
234 			if ((error = check_perm_opts(perms)) == ERROR) {
235 				if (force_flag == 0) { /* no force flag */
236 					exit_unlock();
237 
238 					return (error);
239 				}
240 			}
241 
242 			/*
243 			 * update the file, if and only if
244 			 * we didn't run into error earlier.
245 			 */
246 			if ((error != ERROR) &&
247 			    (error = update_minor_entry(driver_name, perms))) {
248 				if (force_flag == 0) { /* no force flag */
249 					exit_unlock();
250 
251 					return (error);
252 				}
253 			}
254 			cleanup_flag |= CLEAN_NAM_MAJ;
255 
256 			/*
257 			 * Notify running system of minor perm change
258 			 */
259 			if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
260 				rval = devfs_add_minor_perm(driver_name,
261 				    log_minorperm_error);
262 				if (rval) {
263 					(void) fprintf(stderr,
264 					    gettext(ERR_UPDATE_PERM),
265 					    driver_name);
266 				}
267 			}
268 		}
269 
270 		if (priv != NULL) {
271 			(void) append_to_file(driver_name, priv, extra_privs,
272 			    ',', ":", 0);
273 			cleanup_flag |= CLEAN_DRV_PRIV;
274 		}
275 
276 		if (policy != NULL) {
277 			if ((error = update_device_policy(device_policy,
278 			    policy, B_TRUE)) != 0) {
279 				exit_unlock();
280 				return (error);
281 			}
282 			cleanup_flag |= CLEAN_DEV_POLICY;
283 		}
284 
285 		if (i_flag) {
286 			found = get_major_no(driver_name, name_to_major);
287 			if (found == ERROR) {
288 				(void) fprintf(stderr, gettext(ERR_MAX_MAJOR),
289 				    name_to_major);
290 				err_exit();
291 			}
292 
293 			if (found == UNIQUE) {
294 				(void) fprintf(stderr,
295 				    gettext(ERR_NOT_INSTALLED), driver_name);
296 				err_exit();
297 			}
298 
299 			major_num = (major_t)found;
300 
301 			/* check if the alias is unique */
302 			if ((error = aliases_unique(aliases)) == ERROR) {
303 				exit_unlock();
304 
305 				return (error);
306 			}
307 
308 			/*
309 			 * unless force_flag is specified check that
310 			 * path-oriented aliases we are adding exist
311 			 */
312 			if ((force_flag == 0) &&
313 			    ((error = aliases_paths_exist(aliases)) == ERROR)) {
314 				exit_unlock();
315 
316 				return (error);
317 			}
318 
319 			/* update the file */
320 			if ((error = update_driver_aliases(driver_name,
321 			    aliases)) == ERROR) {
322 				exit_unlock();
323 				return (error);
324 			}
325 
326 			/* paranoia - if we crash whilst configuring */
327 			sync();
328 
329 			/* optionally update the running system - not -b */
330 			if (update_conf) {
331 				cleanup_flag |= CLEAN_DRV_ALIAS;
332 				if (config_driver(driver_name, major_num,
333 				    aliases, NULL, cleanup_flag,
334 				    verbose_flag) == ERROR) {
335 					err_exit();
336 				}
337 			}
338 
339 		}
340 		if (update_conf && (i_flag || policy != NULL)) {
341 			/* load the driver */
342 			load_driver(driver_name, verbose_flag);
343 		}
344 
345 		exit_unlock();
346 
347 		return (0);
348 	}
349 
350 
351 	/*
352 	 * DELETE: -d option
353 	 * i_flag: update /etc/driver_aliases
354 	 * m_flag: update /etc/minor_perm
355 	 * -p: update /etc/security/device_policy
356 	 * -P: update /etc/security/extra_privs
357 	 */
358 	if (d_flag) {
359 		int err = NOERR;
360 
361 		if (m_flag) {
362 			/*
363 			 * On a running system, we first need to
364 			 * remove devfs's idea of the minor perms.
365 			 * We don't have any ability to do this singly
366 			 * at this point.
367 			 */
368 			if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
369 				rval = devfs_rm_minor_perm(driver_name,
370 				    log_minorperm_error);
371 				if (rval) {
372 					(void) fprintf(stderr,
373 					    gettext(ERR_UPDATE_PERM),
374 					    driver_name);
375 				}
376 			}
377 
378 			if ((error = delete_entry(minor_perm,
379 			    driver_name, ":", perms)) != NOERR) {
380 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
381 				    driver_name, minor_perm);
382 				err = error;
383 			}
384 			/*
385 			 * Notify running system of new minor perm state
386 			 */
387 			if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
388 				rval = devfs_add_minor_perm(driver_name,
389 				    log_minorperm_error);
390 				if (rval) {
391 					(void) fprintf(stderr,
392 					    gettext(ERR_UPDATE_PERM),
393 					    driver_name);
394 				}
395 			}
396 		}
397 
398 		if (i_flag) {
399 			if ((error = delete_entry(driver_aliases,
400 			    driver_name, ":", aliases)) != NOERR) {
401 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
402 				    driver_name, driver_aliases);
403 				if (err != NOERR)
404 					err = error;
405 			}
406 		}
407 
408 		if (priv != NULL) {
409 			if ((error = delete_entry(extra_privs, driver_name, ":",
410 			    priv)) != NOERR) {
411 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
412 				    driver_name, extra_privs);
413 				if (err != NOERR)
414 					err = error;
415 			}
416 		}
417 
418 		if (policy != NULL) {
419 			if ((error = delete_plcy_entry(device_policy,
420 			    policy)) != NOERR) {
421 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
422 				    driver_name, device_policy);
423 				if (err != NOERR)
424 					err = error;
425 			}
426 		}
427 
428 		if (err == NOERR && update_conf) {
429 			if (i_flag || m_flag) {
430 				/* try to unload the driver */
431 				(void) unload_drv(driver_name,
432 				    force_flag, verbose_flag);
433 			}
434 			/* reload the policy */
435 			if (policy != NULL)
436 				load_driver(driver_name, verbose_flag);
437 		}
438 		exit_unlock();
439 
440 		return (err);
441 	}
442 
443 	/* driver name must exist (for update_conf stuff) */
444 	major = get_major_no(driver_name, name_to_major);
445 	if (major == ERROR) {
446 		err_exit();
447 	}
448 
449 	/*
450 	 * Update driver.conf file:
451 	 *	First try to unload driver module. If it fails, there may
452 	 *	be attached devices using the old driver.conf properties,
453 	 *	so we cannot safely update driver.conf
454 	 *
455 	 *	The user may specify -f to force a driver.conf update.
456 	 *	In this case, we will update driver.conf cache. All attached
457 	 *	devices still reference old driver.conf properties, including
458 	 *	driver global properties. Devices attached in the future will
459 	 *	referent properties in the updated driver.conf file.
460 	 */
461 	if (update_conf) {
462 		(void) unload_drv(driver_name, force_flag, verbose_flag);
463 
464 		if ((modctl(MODUNLOADDRVCONF, major) != 0) ||
465 		    (modctl(MODLOADDRVCONF, major) != 0)) {
466 			(void) fprintf(stderr, gettext(ERR_DRVCONF),
467 			    driver_name);
468 			err_exit();
469 		}
470 
471 		if (verbose_flag) {
472 			(void) fprintf(stderr, gettext(DRVCONF_UPDATED),
473 			    driver_name);
474 		}
475 		load_driver(driver_name, verbose_flag);
476 	}
477 
478 
479 	exit_unlock();
480 
481 	return (NOERR);
482 }
483