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