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