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