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