Home | History | Annotate | Download | only in modload
      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 <locale.h>
     28 #include <stdlib.h>
     29 #include <unistd.h>
     30 #include <sys/types.h>
     31 #include <string.h>
     32 #include "addrem.h"
     33 #include "errmsg.h"
     34 #include "plcysubr.h"
     35 
     36 /* function prototypes */
     37 static void	usage();
     38 static int	unload_drv(char *, int, int);
     39 
     40 
     41 /*
     42  * try to modunload driver.
     43  * return -1 on failure and 0 on success
     44  */
     45 static int
     46 unload_drv(char *driver_name, int force_flag, int verbose_flag)
     47 {
     48 	int modid;
     49 
     50 	get_modid(driver_name, &modid);
     51 	if (modid != -1) {
     52 		if (modctl(MODUNLOAD, modid) < 0) {
     53 			(void) fprintf(stderr, gettext(ERR_MODUN), driver_name);
     54 			if (force_flag == 0) { /* no force flag */
     55 				if (verbose_flag) {
     56 					(void) fprintf(stderr,
     57 					    gettext(NOUPDATE), driver_name);
     58 				}
     59 				/* clean up and exit. remove lock file */
     60 				err_exit();
     61 			}
     62 			(void) fprintf(stderr, gettext(FORCE_UPDATE),
     63 			    driver_name);
     64 
     65 			return (-1);
     66 		}
     67 	}
     68 
     69 	return (0);
     70 }
     71 
     72 
     73 static void
     74 usage()
     75 {
     76 	(void) fprintf(stderr, gettext(UPD_DRV_USAGE));
     77 	exit(1);
     78 }
     79 
     80 
     81 int
     82 main(int argc, char *argv[])
     83 {
     84 	int	error, opt, major;
     85 	int	cleanup_flag = 0;
     86 	int	update_conf = 1;	/* reload driver.conf by default */
     87 	int	verbose_flag = 0;	/* -v option */
     88 	int	force_flag = 0;		/* -f option */
     89 	int	a_flag = 0;		/* -a option */
     90 	int	d_flag = 0;		/* -d option */
     91 	int	i_flag = 0;		/* -i option */
     92 	int	l_flag = 0;		/* -l option */
     93 	int	m_flag = 0;		/* -m option */
     94 	int	n_flag = 0;		/* -n option */
     95 	char	*perms = NULL;
     96 	char	*aliases = NULL;
     97 	char	*basedir = NULL;
     98 	char	*policy = NULL;
     99 	char	*aliases2 = NULL;
    100 	char	*priv = NULL;
    101 	char	*driver_name;
    102 	int	found;
    103 	major_t major_num;
    104 	int	rval;
    105 	int	config_flags;
    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:ni: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 'n':
    151 			n_flag++;
    152 			update_conf = 0;
    153 			break;
    154 		case 'p':
    155 			policy = optarg;
    156 			break;
    157 		case 'v':
    158 			verbose_flag++;
    159 			break;
    160 		case 'P':
    161 			priv = optarg;
    162 			break;
    163 		case '?' :
    164 		default:
    165 			usage();
    166 		}
    167 	}
    168 
    169 	/*
    170 	 * check for flags and extra args
    171 	 */
    172 	if ((argv[optind] == NULL) || (optind + 1 != argc)) {
    173 		usage();
    174 	}
    175 
    176 	/*
    177 	 * - cannot be adding and removing at the same time
    178 	 * - if -a or -d is specified, it's an error if none of
    179 	 *   -i/-m/-p/-P is specified.
    180 	 */
    181 	if ((a_flag && d_flag) ||
    182 	    ((a_flag || d_flag) &&
    183 	    !m_flag && !i_flag && priv == NULL && policy == NULL)) {
    184 		usage();
    185 	}
    186 
    187 	/*
    188 	 * - with -d option or -a option either -i 'identify_name',
    189 	 *	-m 'permission',  -p 'policy' or -P 'priv' should be specified
    190 	 */
    191 	if (m_flag || i_flag || policy != NULL || priv != NULL) {
    192 		if (!(a_flag || d_flag))
    193 			usage();
    194 	}
    195 
    196 	driver_name = argv[optind];
    197 
    198 	/* set up update_drv filenames */
    199 	if ((build_filenames(basedir)) == ERROR) {
    200 		exit(1);
    201 	}
    202 
    203 	/* no lock is needed for listing minor perm entry */
    204 	if (l_flag) {
    205 		list_entry(minor_perm, driver_name, ":");
    206 
    207 		return (NOERR);
    208 	}
    209 
    210 	/* must be only running version of add_drv/update_drv/rem_drv */
    211 	enter_lock();
    212 
    213 	if ((check_perms_aliases(m_flag, i_flag)) == ERROR) {
    214 		err_exit();
    215 	}
    216 
    217 	/* update_drv doesn't modify /etc/name_to_major file */
    218 	if ((check_name_to_major(R_OK)) == ERROR)
    219 		err_exit();
    220 
    221 	if ((n_flag == 0) &&
    222 	    (basedir == NULL || (strcmp(basedir, "/") == 0)) &&
    223 	    (priv != NULL) && check_priv_entry(priv, a_flag) != 0)
    224 		err_exit();
    225 
    226 	if (policy != NULL && (policy = check_plcy_entry(policy, driver_name,
    227 	    d_flag ? B_TRUE : B_FALSE)) == NULL)
    228 		err_exit();
    229 
    230 	/*
    231 	 * ADD: -a option
    232 	 * i_flag: update /etc/driver_aliases
    233 	 * m_flag: update /etc/minor_perm
    234 	 * -p: update /etc/security/device_policy
    235 	 * -P: update /etc/security/extra_privs
    236 	 * if force_flag is specified continue w/ the next operation
    237 	 */
    238 	if (a_flag) {
    239 		if (m_flag) {
    240 			/* check if the permissions are valid */
    241 			if ((error = check_perm_opts(perms)) == ERROR) {
    242 				if (force_flag == 0) { /* no force flag */
    243 					exit_unlock();
    244 					return (error);
    245 				}
    246 			}
    247 
    248 			/*
    249 			 * update the file, if and only if
    250 			 * we didn't run into error earlier.
    251 			 */
    252 			if ((error != ERROR) &&
    253 			    (error = update_minor_entry(driver_name, perms))) {
    254 				if (force_flag == 0) { /* no force flag */
    255 					exit_unlock();
    256 					return (error);
    257 				}
    258 			}
    259 			cleanup_flag |= CLEAN_NAM_MAJ;
    260 
    261 			/*
    262 			 * Notify running system of minor perm change
    263 			 */
    264 			if ((n_flag == 0) &&
    265 			    (basedir == NULL || (strcmp(basedir, "/") == 0))) {
    266 				rval = devfs_add_minor_perm(driver_name,
    267 				    log_minorperm_error);
    268 				if (rval) {
    269 					(void) fprintf(stderr,
    270 					    gettext(ERR_UPDATE_PERM),
    271 					    driver_name);
    272 				}
    273 			}
    274 		}
    275 
    276 		if (priv != NULL) {
    277 			(void) append_to_file(driver_name, priv, extra_privs,
    278 			    ',', ":", 0);
    279 			cleanup_flag |= CLEAN_DRV_PRIV;
    280 		}
    281 
    282 		if (policy != NULL) {
    283 			if ((error = update_device_policy(device_policy,
    284 			    policy, B_TRUE)) != 0) {
    285 				exit_unlock();
    286 				return (error);
    287 			}
    288 			cleanup_flag |= CLEAN_DEV_POLICY;
    289 		}
    290 
    291 		if (i_flag) {
    292 			found = get_major_no(driver_name, name_to_major);
    293 			if (found == ERROR) {
    294 				(void) fprintf(stderr, gettext(ERR_MAX_MAJOR),
    295 				    name_to_major);
    296 				err_exit();
    297 			}
    298 
    299 			if (found == UNIQUE) {
    300 				(void) fprintf(stderr,
    301 				    gettext(ERR_NOT_INSTALLED), driver_name);
    302 				err_exit();
    303 			}
    304 
    305 			major_num = (major_t)found;
    306 
    307 			/*
    308 			 * To ease the nuisance of using update_drv
    309 			 * in packaging scripts, do not require that
    310 			 * existing driver aliases be trimmed from
    311 			 * the command line.  If an invocation asks
    312 			 * to add an alias and it's already there,
    313 			 * drive on.  We implement this by removing
    314 			 * duplicates now and add the remainder.
    315 			 */
    316 			error = trim_duplicate_aliases(driver_name,
    317 			    aliases, &aliases2);
    318 			if (error == ERROR) {
    319 				exit_unlock();
    320 				return (error);
    321 			}
    322 
    323 			/*
    324 			 * if the list of aliases to be added is
    325 			 * now empty, we're done.
    326 			 */
    327 			if (aliases2 == NULL)
    328 				goto done;
    329 
    330 			/*
    331 			 * unless force_flag is specified check that
    332 			 * path-oriented aliases we are adding exist
    333 			 */
    334 			if ((force_flag == 0) && ((error =
    335 			    aliases_paths_exist(aliases2)) == ERROR)) {
    336 				exit_unlock();
    337 				return (error);
    338 			}
    339 
    340 			/* update the file */
    341 			if ((error = update_driver_aliases(driver_name,
    342 			    aliases2)) == ERROR) {
    343 				exit_unlock();
    344 				return (error);
    345 			}
    346 
    347 
    348 			/* optionally update the running system - not -b */
    349 			if (update_conf) {
    350 				/* paranoia - if we crash whilst configuring */
    351 				sync();
    352 				config_flags = (verbose_flag) ?
    353 				    CONFIG_DRV_VERBOSE : 0;
    354 				cleanup_flag |= CLEAN_DRV_ALIAS;
    355 				if (config_driver(driver_name, major_num,
    356 				    aliases2, NULL, cleanup_flag,
    357 				    config_flags) == ERROR) {
    358 					err_exit();
    359 				}
    360 			}
    361 
    362 		}
    363 
    364 done:
    365 		if (update_conf && (i_flag || policy != NULL)) {
    366 			/* load the driver */
    367 			load_driver(driver_name, verbose_flag);
    368 		}
    369 
    370 		exit_unlock();
    371 
    372 		return (0);
    373 	}
    374 
    375 
    376 	/*
    377 	 * DELETE: -d option
    378 	 * i_flag: update /etc/driver_aliases
    379 	 * m_flag: update /etc/minor_perm
    380 	 * -p: update /etc/security/device_policy
    381 	 * -P: update /etc/security/extra_privs
    382 	 * if force_flag is specified continue w/ the next operation
    383 	 */
    384 	if (d_flag) {
    385 		int err = NOERR;
    386 
    387 		if (m_flag) {
    388 			/*
    389 			 * On a running system, we first need to
    390 			 * remove devfs's idea of the minor perms.
    391 			 * We don't have any ability to do this singly
    392 			 * at this point.
    393 			 */
    394 			if ((n_flag == 0) &&
    395 			    (basedir == NULL || (strcmp(basedir, "/") == 0))) {
    396 				rval = devfs_rm_minor_perm(driver_name,
    397 				    log_minorperm_error);
    398 				if (rval) {
    399 					(void) fprintf(stderr,
    400 					    gettext(ERR_UPDATE_PERM),
    401 					    driver_name);
    402 				}
    403 			}
    404 
    405 			if ((error = delete_entry(minor_perm,
    406 			    driver_name, ":", perms)) != NOERR) {
    407 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
    408 				    driver_name, minor_perm);
    409 				err = error;
    410 			}
    411 			/*
    412 			 * Notify running system of new minor perm state
    413 			 */
    414 			if ((n_flag == 0) &&
    415 			    (basedir == NULL || (strcmp(basedir, "/") == 0))) {
    416 				rval = devfs_add_minor_perm(driver_name,
    417 				    log_minorperm_error);
    418 				if (rval) {
    419 					(void) fprintf(stderr,
    420 					    gettext(ERR_UPDATE_PERM),
    421 					    driver_name);
    422 				}
    423 			}
    424 		}
    425 
    426 		if (i_flag) {
    427 			found = get_major_no(driver_name, name_to_major);
    428 			if (found == ERROR) {
    429 				(void) fprintf(stderr, gettext(ERR_MAX_MAJOR),
    430 				    name_to_major);
    431 				err_exit();
    432 			}
    433 
    434 			if (found == UNIQUE) {
    435 				(void) fprintf(stderr,
    436 				    gettext(ERR_NOT_INSTALLED), driver_name);
    437 				err_exit();
    438 			}
    439 
    440 			major_num = (major_t)found;
    441 
    442 			/*
    443 			 * verify that the aliases to be deleted exist
    444 			 * before removal.  With -f, failing to
    445 			 * remove an alias is not an error so we
    446 			 * can continue on to update the kernel.
    447 			 */
    448 			error = NOERR;
    449 			rval = aliases_exist(aliases);
    450 			if (rval == ERROR && (force_flag == 0)) {
    451 				(void) fprintf(stderr,
    452 				    gettext(ERR_ALIAS_NOT_BOUND),
    453 				    driver_name);
    454 				if (err != NOERR)
    455 					err = rval;
    456 			}
    457 			if (rval == NOERR)
    458 				error = delete_entry(driver_aliases,
    459 				    driver_name, ":", aliases);
    460 			if (error != NOERR && (force_flag == 0)) {
    461 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
    462 				    driver_name, driver_aliases);
    463 				if (err != NOERR)
    464 					err = error;
    465 			}
    466 
    467 			/*
    468 			 * optionally update the running system - not -b.
    469 			 * Unless -f is specified, error if one or more
    470 			 * devices remain bound to the alias.
    471 			 */
    472 			if (err == NOERR && update_conf) {
    473 				/* paranoia - if we crash whilst configuring */
    474 				sync();
    475 
    476 				config_flags = 0;
    477 				if (verbose_flag)
    478 					config_flags |= CONFIG_DRV_VERBOSE;
    479 				if (force_flag)
    480 					config_flags |= CONFIG_DRV_FORCE;
    481 				error = unconfig_driver(driver_name, major_num,
    482 				    aliases, config_flags);
    483 				if (error == ERROR && force_flag == 0) {
    484 					(void) fprintf(stderr,
    485 					    gettext(ERR_DEV_IN_USE),
    486 					    driver_name);
    487 					if (err != NOERR)
    488 						err = error;
    489 				}
    490 			}
    491 		}
    492 
    493 		if (priv != NULL) {
    494 			if ((error = delete_entry(extra_privs, driver_name, ":",
    495 			    priv)) != NOERR) {
    496 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
    497 				    driver_name, extra_privs);
    498 				if (err != NOERR)
    499 					err = error;
    500 			}
    501 		}
    502 
    503 		if (policy != NULL) {
    504 			if ((error = delete_plcy_entry(device_policy,
    505 			    policy)) != NOERR) {
    506 				(void) fprintf(stderr, gettext(ERR_NO_ENTRY),
    507 				    driver_name, device_policy);
    508 				if (err != NOERR)
    509 					err = error;
    510 			}
    511 		}
    512 
    513 		if (err == NOERR && update_conf) {
    514 			if (i_flag || m_flag) {
    515 				/* try to unload the driver */
    516 				(void) unload_drv(driver_name,
    517 				    force_flag, verbose_flag);
    518 			}
    519 			/* reload the policy */
    520 			if (policy != NULL)
    521 				load_driver(driver_name, verbose_flag);
    522 		}
    523 		exit_unlock();
    524 
    525 		return (err);
    526 	}
    527 
    528 	/* driver name must exist (for update_conf stuff) */
    529 	major = get_major_no(driver_name, name_to_major);
    530 	if (major == ERROR) {
    531 		err_exit();
    532 	}
    533 
    534 	/*
    535 	 * Update driver.conf file:
    536 	 *	First try to unload driver module. If it fails, there may
    537 	 *	be attached devices using the old driver.conf properties,
    538 	 *	so we cannot safely update driver.conf
    539 	 *
    540 	 *	The user may specify -f to force a driver.conf update.
    541 	 *	In this case, we will update driver.conf cache. All attached
    542 	 *	devices still reference old driver.conf properties, including
    543 	 *	driver global properties. Devices attached in the future will
    544 	 *	referent properties in the updated driver.conf file.
    545 	 */
    546 	if (update_conf) {
    547 		(void) unload_drv(driver_name, force_flag, verbose_flag);
    548 
    549 		if ((modctl(MODUNLOADDRVCONF, major) != 0) ||
    550 		    (modctl(MODLOADDRVCONF, major, 0) != 0)) {
    551 			(void) fprintf(stderr, gettext(ERR_DRVCONF),
    552 			    driver_name);
    553 			err_exit();
    554 		}
    555 
    556 		if (verbose_flag) {
    557 			(void) fprintf(stderr, gettext(DRVCONF_UPDATED),
    558 			    driver_name);
    559 		}
    560 		load_driver(driver_name, verbose_flag);
    561 	}
    562 
    563 	exit_unlock();
    564 
    565 	return (NOERR);
    566 }
    567