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 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:ni: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 'n': 150 n_flag++; 151 update_conf = 0; 152 break; 153 case 'p': 154 policy = optarg; 155 break; 156 case 'v': 157 verbose_flag++; 158 break; 159 case 'P': 160 priv = optarg; 161 break; 162 case '?' : 163 default: 164 usage(); 165 } 166 } 167 168 /* 169 * check for flags and extra args 170 */ 171 if ((argv[optind] == NULL) || (optind + 1 != argc)) { 172 usage(); 173 } 174 175 /* 176 * - cannot be adding and removing at the same time 177 * - if -a or -d is specified, it's an error if none of 178 * -i/-m/-p/-P is specified. 179 */ 180 if ((a_flag && d_flag) || 181 ((a_flag || d_flag) && 182 !m_flag && !i_flag && priv == NULL && policy == NULL)) { 183 usage(); 184 } 185 186 /* 187 * - with -d option or -a option either -i 'identify_name', 188 * -m 'permission', -p 'policy' or -P 'priv' should be specified 189 */ 190 if (m_flag || i_flag || policy != NULL || priv != NULL) { 191 if (!(a_flag || d_flag)) 192 usage(); 193 } 194 195 driver_name = argv[optind]; 196 197 /* set up update_drv filenames */ 198 if ((build_filenames(basedir)) == ERROR) { 199 exit(1); 200 } 201 202 /* no lock is needed for listing minor perm entry */ 203 if (l_flag) { 204 list_entry(minor_perm, driver_name, ":"); 205 206 return (NOERR); 207 } 208 209 /* must be only running version of add_drv/update_drv/rem_drv */ 210 enter_lock(); 211 212 if ((check_perms_aliases(m_flag, i_flag)) == ERROR) { 213 err_exit(); 214 } 215 216 /* update_drv doesn't modify /etc/name_to_major file */ 217 if ((check_name_to_major(R_OK)) == ERROR) 218 err_exit(); 219 220 if ((n_flag == 0) && 221 (basedir == NULL || (strcmp(basedir, "/") == 0)) && 222 (priv != NULL) && check_priv_entry(priv, a_flag) != 0) 223 err_exit(); 224 225 if (policy != NULL && (policy = check_plcy_entry(policy, driver_name, 226 d_flag ? B_TRUE : B_FALSE)) == NULL) 227 err_exit(); 228 229 /* 230 * ADD: -a option 231 * i_flag: update /etc/driver_aliases 232 * m_flag: update /etc/minor_perm 233 * -p: update /etc/security/device_policy 234 * -P: update /etc/security/extra_privs 235 * if force_flag is specified continue w/ the next operation 236 */ 237 if (a_flag) { 238 if (m_flag) { 239 /* check if the permissions are valid */ 240 if ((error = check_perm_opts(perms)) == ERROR) { 241 if (force_flag == 0) { /* no force flag */ 242 exit_unlock(); 243 return (error); 244 } 245 } 246 247 /* 248 * update the file, if and only if 249 * we didn't run into error earlier. 250 */ 251 if ((error != ERROR) && 252 (error = update_minor_entry(driver_name, perms))) { 253 if (force_flag == 0) { /* no force flag */ 254 exit_unlock(); 255 return (error); 256 } 257 } 258 cleanup_flag |= CLEAN_NAM_MAJ; 259 260 /* 261 * Notify running system of minor perm change 262 */ 263 if ((n_flag == 0) && 264 (basedir == NULL || (strcmp(basedir, "/") == 0))) { 265 rval = devfs_add_minor_perm(driver_name, 266 log_minorperm_error); 267 if (rval) { 268 (void) fprintf(stderr, 269 gettext(ERR_UPDATE_PERM), 270 driver_name); 271 } 272 } 273 } 274 275 if (priv != NULL) { 276 (void) append_to_file(driver_name, priv, extra_privs, 277 ',', ":", 0); 278 cleanup_flag |= CLEAN_DRV_PRIV; 279 } 280 281 if (policy != NULL) { 282 if ((error = update_device_policy(device_policy, 283 policy, B_TRUE)) != 0) { 284 exit_unlock(); 285 return (error); 286 } 287 cleanup_flag |= CLEAN_DEV_POLICY; 288 } 289 290 if (i_flag) { 291 found = get_major_no(driver_name, name_to_major); 292 if (found == ERROR) { 293 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR), 294 name_to_major); 295 err_exit(); 296 } 297 298 if (found == UNIQUE) { 299 (void) fprintf(stderr, 300 gettext(ERR_NOT_INSTALLED), driver_name); 301 err_exit(); 302 } 303 304 major_num = (major_t)found; 305 306 /* 307 * To ease the nuisance of using update_drv 308 * in packaging scripts, do not require that 309 * existing driver aliases be trimmed from 310 * the command line. If an invocation asks 311 * to add an alias and it's already there, 312 * drive on. We implement this by removing 313 * duplicates now and add the remainder. 314 */ 315 error = trim_duplicate_aliases(driver_name, 316 aliases, &aliases2); 317 if (error == ERROR) { 318 exit_unlock(); 319 return (error); 320 } 321 322 /* 323 * if the list of aliases to be added is 324 * now empty, we're done. 325 */ 326 if (aliases2 == NULL) 327 goto done; 328 329 /* 330 * unless force_flag is specified check that 331 * path-oriented aliases we are adding exist 332 */ 333 if ((force_flag == 0) && ((error = 334 aliases_paths_exist(aliases2)) == ERROR)) { 335 exit_unlock(); 336 return (error); 337 } 338 339 /* update the file */ 340 if ((error = update_driver_aliases(driver_name, 341 aliases2)) == ERROR) { 342 exit_unlock(); 343 return (error); 344 } 345 346 347 /* optionally update the running system - not -b */ 348 if (update_conf) { 349 /* paranoia - if we crash whilst configuring */ 350 sync(); 351 cleanup_flag |= CLEAN_DRV_ALIAS; 352 if (config_driver(driver_name, major_num, 353 aliases2, NULL, cleanup_flag, 354 verbose_flag) == ERROR) { 355 err_exit(); 356 } 357 } 358 359 } 360 361 done: 362 if (update_conf && (i_flag || policy != NULL)) { 363 /* load the driver */ 364 load_driver(driver_name, verbose_flag); 365 } 366 367 exit_unlock(); 368 369 return (0); 370 } 371 372 373 /* 374 * DELETE: -d option 375 * i_flag: update /etc/driver_aliases 376 * m_flag: update /etc/minor_perm 377 * -p: update /etc/security/device_policy 378 * -P: update /etc/security/extra_privs 379 */ 380 if (d_flag) { 381 int err = NOERR; 382 383 if (m_flag) { 384 /* 385 * On a running system, we first need to 386 * remove devfs's idea of the minor perms. 387 * We don't have any ability to do this singly 388 * at this point. 389 */ 390 if ((n_flag == 0) && 391 (basedir == NULL || (strcmp(basedir, "/") == 0))) { 392 rval = devfs_rm_minor_perm(driver_name, 393 log_minorperm_error); 394 if (rval) { 395 (void) fprintf(stderr, 396 gettext(ERR_UPDATE_PERM), 397 driver_name); 398 } 399 } 400 401 if ((error = delete_entry(minor_perm, 402 driver_name, ":", perms)) != NOERR) { 403 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 404 driver_name, minor_perm); 405 err = error; 406 } 407 /* 408 * Notify running system of new minor perm state 409 */ 410 if ((n_flag == 0) && 411 (basedir == NULL || (strcmp(basedir, "/") == 0))) { 412 rval = devfs_add_minor_perm(driver_name, 413 log_minorperm_error); 414 if (rval) { 415 (void) fprintf(stderr, 416 gettext(ERR_UPDATE_PERM), 417 driver_name); 418 } 419 } 420 } 421 422 if (i_flag) { 423 found = get_major_no(driver_name, name_to_major); 424 if (found == ERROR) { 425 (void) fprintf(stderr, gettext(ERR_MAX_MAJOR), 426 name_to_major); 427 err_exit(); 428 } 429 430 if (found == UNIQUE) { 431 (void) fprintf(stderr, 432 gettext(ERR_NOT_INSTALLED), driver_name); 433 err_exit(); 434 } 435 436 major_num = (major_t)found; 437 438 /* 439 * verify that the aliases to be deleted exist 440 * before removal. With -f, failing to 441 * remove an alias is not an error so we 442 * can continue on to update the kernel. 443 */ 444 error = NOERR; 445 rval = aliases_exist(aliases); 446 if (rval == ERROR && (force_flag == 0)) { 447 (void) fprintf(stderr, 448 gettext(ERR_ALIAS_NOT_BOUND), 449 driver_name); 450 if (err != NOERR) 451 err = rval; 452 } 453 if (rval == NOERR) 454 error = delete_entry(driver_aliases, 455 driver_name, ":", aliases); 456 if (error != NOERR && (force_flag == 0)) { 457 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 458 driver_name, driver_aliases); 459 if (err != NOERR) 460 err = error; 461 } 462 463 /* 464 * optionally update the running system - not -b. 465 * Unless -f is specified, error if one or more 466 * devices remain bound to the alias. 467 */ 468 if (err == NOERR && update_conf) { 469 /* paranoia - if we crash whilst configuring */ 470 sync(); 471 error = unconfig_driver(driver_name, major_num, 472 aliases, verbose_flag, force_flag); 473 if (error == ERROR && force_flag == 0) { 474 (void) fprintf(stderr, 475 gettext(ERR_DEV_IN_USE), 476 driver_name); 477 if (err != NOERR) 478 err = error; 479 } 480 } 481 } 482 483 if (priv != NULL) { 484 if ((error = delete_entry(extra_privs, driver_name, ":", 485 priv)) != NOERR) { 486 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 487 driver_name, extra_privs); 488 if (err != NOERR) 489 err = error; 490 } 491 } 492 493 if (policy != NULL) { 494 if ((error = delete_plcy_entry(device_policy, 495 policy)) != NOERR) { 496 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 497 driver_name, device_policy); 498 if (err != NOERR) 499 err = error; 500 } 501 } 502 503 if (err == NOERR && update_conf) { 504 if (i_flag || m_flag) { 505 /* try to unload the driver */ 506 (void) unload_drv(driver_name, 507 force_flag, verbose_flag); 508 } 509 /* reload the policy */ 510 if (policy != NULL) 511 load_driver(driver_name, verbose_flag); 512 } 513 exit_unlock(); 514 515 return (err); 516 } 517 518 /* driver name must exist (for update_conf stuff) */ 519 major = get_major_no(driver_name, name_to_major); 520 if (major == ERROR) { 521 err_exit(); 522 } 523 524 /* 525 * Update driver.conf file: 526 * First try to unload driver module. If it fails, there may 527 * be attached devices using the old driver.conf properties, 528 * so we cannot safely update driver.conf 529 * 530 * The user may specify -f to force a driver.conf update. 531 * In this case, we will update driver.conf cache. All attached 532 * devices still reference old driver.conf properties, including 533 * driver global properties. Devices attached in the future will 534 * referent properties in the updated driver.conf file. 535 */ 536 if (update_conf) { 537 (void) unload_drv(driver_name, force_flag, verbose_flag); 538 539 if ((modctl(MODUNLOADDRVCONF, major) != 0) || 540 (modctl(MODLOADDRVCONF, major) != 0)) { 541 (void) fprintf(stderr, gettext(ERR_DRVCONF), 542 driver_name); 543 err_exit(); 544 } 545 546 if (verbose_flag) { 547 (void) fprintf(stderr, gettext(DRVCONF_UPDATED), 548 driver_name); 549 } 550 load_driver(driver_name, verbose_flag); 551 } 552 553 exit_unlock(); 554 555 return (NOERR); 556 } 557