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