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