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