xref: /titanic_52/usr/src/cmd/modload/update_drv.c (revision 41efec2219526a9b3ecce26f97aba761ef1e1d0d)
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 <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 
324 				return (error);
325 			}
326 
327 			/* paranoia - if we crash whilst configuring */
328 			sync();
329 
330 			cleanup_flag |= CLEAN_DRV_ALIAS;
331 			if (config_driver(driver_name, major_num, aliases, NULL,
332 			    cleanup_flag, verbose_flag) == ERROR) {
333 				err_exit();
334 			}
335 
336 		}
337 		if (update_conf && (i_flag || policy != NULL))
338 			/* load the driver */
339 			load_driver(driver_name, verbose_flag);
340 
341 		exit_unlock();
342 
343 		return (0);
344 	}
345 
346 
347 	/*
348 	 * DELETE: -d option
349 	 * i_flag: update /etc/driver_aliases
350 	 * m_flag: update /etc/minor_perm
351 	 * -p: update /etc/security/device_policy
352 	 * -P: update /etc/security/extra_privs
353 	 */
354 	if (d_flag) {
355 		int err = NOERR;
356 
357 		if (m_flag) {
358 			/*
359 			 * On a running system, we first need to
360 			 * remove devfs's idea of the minor perms.
361 			 * We don't have any ability to do this singly
362 			 * at this point.
363 			 */
364 			if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
365 				rval = devfs_rm_minor_perm(driver_name,
366 				    log_minorperm_error);
367 				if (rval) {
368 					(void) fprintf(stderr,
369 					    gettext(ERR_UPDATE_PERM),
370 					    driver_name);
371 				}
372 			}
373 
374 			if ((error = delete_entry(minor_perm,
375 			    driver_name, ":", perms)) != NOERR) {
376 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
377 				    driver_name, minor_perm);
378 				err = error;
379 			}
380 			/*
381 			 * Notify running system of new minor perm state
382 			 */
383 			if (basedir == NULL || (strcmp(basedir, "/") == 0)) {
384 				rval = devfs_add_minor_perm(driver_name,
385 				    log_minorperm_error);
386 				if (rval) {
387 					(void) fprintf(stderr,
388 					    gettext(ERR_UPDATE_PERM),
389 					    driver_name);
390 				}
391 			}
392 		}
393 
394 		if (i_flag) {
395 			if ((error = delete_entry(driver_aliases,
396 			    driver_name, ":", aliases)) != NOERR) {
397 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
398 				    driver_name, driver_aliases);
399 				if (err != NOERR)
400 					err = error;
401 			}
402 		}
403 
404 		if (priv != NULL) {
405 			if ((error = delete_entry(extra_privs, driver_name, ":",
406 			    priv)) != NOERR) {
407 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
408 				    driver_name, extra_privs);
409 				if (err != NOERR)
410 					err = error;
411 			}
412 		}
413 
414 		if (policy != NULL) {
415 			if ((error = delete_plcy_entry(device_policy,
416 				policy)) != NOERR) {
417 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
418 				    driver_name, device_policy);
419 				if (err != NOERR)
420 					err = error;
421 			}
422 		}
423 
424 		if (err == NOERR && update_conf) {
425 			if (i_flag || m_flag) {
426 				/* try to unload the driver */
427 				(void) unload_drv(driver_name,
428 				    force_flag, verbose_flag);
429 			}
430 			/* reload the policy */
431 			if (policy != NULL)
432 				load_driver(driver_name, verbose_flag);
433 		}
434 		exit_unlock();
435 
436 		return (err);
437 	}
438 
439 	/* driver name must exist (for update_conf stuff) */
440 	major = get_major_no(driver_name, name_to_major);
441 	if (major == ERROR) {
442 		err_exit();
443 	}
444 
445 	/*
446 	 * Update driver.conf file:
447 	 *	First try to unload driver module. If it fails, there may
448 	 *	be attached devices using the old driver.conf properties,
449 	 *	so we cannot safely update driver.conf
450 	 *
451 	 *	The user may specify -f to force a driver.conf update.
452 	 *	In this case, we will update driver.conf cache. All attached
453 	 *	devices still reference old driver.conf properties, including
454 	 *	driver global properties. Devices attached in the future will
455 	 *	referent properties in the updated driver.conf file.
456 	 */
457 	if (update_conf) {
458 		(void) unload_drv(driver_name, force_flag, verbose_flag);
459 
460 		if ((modctl(MODUNLOADDRVCONF, major) != 0) ||
461 		    (modctl(MODLOADDRVCONF, major) != 0)) {
462 			(void) fprintf(stderr, gettext(ERR_DRVCONF),
463 			    driver_name);
464 			err_exit();
465 		}
466 
467 		if (verbose_flag) {
468 			(void) fprintf(stderr, gettext(DRVCONF_UPDATED),
469 			    driver_name);
470 		}
471 	}
472 
473 	/* rebuild /devices & /dev */
474 	load_driver(driver_name, verbose_flag);
475 
476 	exit_unlock();
477 
478 	return (NOERR);
479 }
480