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 return (error); 324 } 325 326 /* paranoia - if we crash whilst configuring */ 327 sync(); 328 329 /* optionally update the running system - not -b */ 330 if (update_conf) { 331 cleanup_flag |= CLEAN_DRV_ALIAS; 332 if (config_driver(driver_name, major_num, 333 aliases, NULL, cleanup_flag, 334 verbose_flag) == ERROR) { 335 err_exit(); 336 } 337 } 338 339 } 340 if (update_conf && (i_flag || policy != NULL)) 341 /* load the driver */ 342 load_driver(driver_name, verbose_flag); 343 344 exit_unlock(); 345 346 return (0); 347 } 348 349 350 /* 351 * DELETE: -d option 352 * i_flag: update /etc/driver_aliases 353 * m_flag: update /etc/minor_perm 354 * -p: update /etc/security/device_policy 355 * -P: update /etc/security/extra_privs 356 */ 357 if (d_flag) { 358 int err = NOERR; 359 360 if (m_flag) { 361 /* 362 * On a running system, we first need to 363 * remove devfs's idea of the minor perms. 364 * We don't have any ability to do this singly 365 * at this point. 366 */ 367 if (basedir == NULL || (strcmp(basedir, "/") == 0)) { 368 rval = devfs_rm_minor_perm(driver_name, 369 log_minorperm_error); 370 if (rval) { 371 (void) fprintf(stderr, 372 gettext(ERR_UPDATE_PERM), 373 driver_name); 374 } 375 } 376 377 if ((error = delete_entry(minor_perm, 378 driver_name, ":", perms)) != NOERR) { 379 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 380 driver_name, minor_perm); 381 err = error; 382 } 383 /* 384 * Notify running system of new minor perm state 385 */ 386 if (basedir == NULL || (strcmp(basedir, "/") == 0)) { 387 rval = devfs_add_minor_perm(driver_name, 388 log_minorperm_error); 389 if (rval) { 390 (void) fprintf(stderr, 391 gettext(ERR_UPDATE_PERM), 392 driver_name); 393 } 394 } 395 } 396 397 if (i_flag) { 398 if ((error = delete_entry(driver_aliases, 399 driver_name, ":", aliases)) != NOERR) { 400 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 401 driver_name, driver_aliases); 402 if (err != NOERR) 403 err = error; 404 } 405 } 406 407 if (priv != NULL) { 408 if ((error = delete_entry(extra_privs, driver_name, ":", 409 priv)) != NOERR) { 410 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 411 driver_name, extra_privs); 412 if (err != NOERR) 413 err = error; 414 } 415 } 416 417 if (policy != NULL) { 418 if ((error = delete_plcy_entry(device_policy, 419 policy)) != NOERR) { 420 (void) fprintf(stderr, gettext(ERR_NO_ENTRY), 421 driver_name, device_policy); 422 if (err != NOERR) 423 err = error; 424 } 425 } 426 427 if (err == NOERR && update_conf) { 428 if (i_flag || m_flag) { 429 /* try to unload the driver */ 430 (void) unload_drv(driver_name, 431 force_flag, verbose_flag); 432 } 433 /* reload the policy */ 434 if (policy != NULL) 435 load_driver(driver_name, verbose_flag); 436 } 437 exit_unlock(); 438 439 return (err); 440 } 441 442 /* driver name must exist (for update_conf stuff) */ 443 major = get_major_no(driver_name, name_to_major); 444 if (major == ERROR) { 445 err_exit(); 446 } 447 448 /* 449 * Update driver.conf file: 450 * First try to unload driver module. If it fails, there may 451 * be attached devices using the old driver.conf properties, 452 * so we cannot safely update driver.conf 453 * 454 * The user may specify -f to force a driver.conf update. 455 * In this case, we will update driver.conf cache. All attached 456 * devices still reference old driver.conf properties, including 457 * driver global properties. Devices attached in the future will 458 * referent properties in the updated driver.conf file. 459 */ 460 if (update_conf) { 461 (void) unload_drv(driver_name, force_flag, verbose_flag); 462 463 if ((modctl(MODUNLOADDRVCONF, major) != 0) || 464 (modctl(MODLOADDRVCONF, major) != 0)) { 465 (void) fprintf(stderr, gettext(ERR_DRVCONF), 466 driver_name); 467 err_exit(); 468 } 469 470 if (verbose_flag) { 471 (void) fprintf(stderr, gettext(DRVCONF_UPDATED), 472 driver_name); 473 } 474 } 475 476 /* rebuild /devices & /dev */ 477 load_driver(driver_name, verbose_flag); 478 479 exit_unlock(); 480 481 return (NOERR); 482 } 483