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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 31 #pragma ident "%Z%%M% %I% %E% SMI" 32 33 /* 34 * Implements the "putdev" command. 35 */ 36 #include <sys/types.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <errno.h> 41 #include <unistd.h> 42 #include <fmtmsg.h> 43 #include <devmgmt.h> 44 #include <devtab.h> 45 46 47 /* 48 * General Purpose Constants 49 * TRUE Boolean TRUE (if not already defined) 50 * FALSE Boolean FALSE (if not already defined) 51 * NULL Null address (if not already defined) 52 */ 53 54 #ifndef TRUE 55 #define TRUE (1) 56 #endif 57 58 #ifndef FALSE 59 #define FALSE (0) 60 #endif 61 62 /* 63 * Exit codes 64 * EX_OK All went well 65 * EX_ERROR Usage or internal error 66 * EX_DEVTAB Had trouble accessing/reading/writing the device table 67 * EX_EXISTS The specified alias already exists 68 * EX_ATTRIB One or more attributes requested for removal was not 69 * defined for the device 70 * EX_RELPATH Pathname supplied for cdevice, bdevice or pathname 71 * attributes was not a full pathname 72 */ 73 74 #define EX_OK 0 75 #define EX_ERROR 1 76 #define EX_DEVTAB 2 77 #define EX_EXISTS 3 78 #define EX_ATTRIB 4 79 #define EX_RELPATH 4 80 81 82 /* 83 * Error messages 84 */ 85 86 #define E_USAGE "usage: putdev -a alias [attribute=value [...]]\n putdev -m device attribute=value [attribute=value [...]]\n putdev -d device [attribute [...]]" 87 #define E_ALIASIS "Alias already exists in table: %s" 88 #define E_NODEV "Device does not exist in table: %s" 89 #define E_NOALIAS "Cannot use \"alias\" as an attribute" 90 #define E_NOATTR "Attribute not found: %s" 91 #define E_NODEVTAB "Cannot open the device table: %s" 92 #define E_NOMKDTAB "Cannot create a new device table: %s" 93 #define E_INVALIAS "Not a valid device alias: %s" 94 #define E_MULTIPLE "Multiple definitions of an attribute are not allowed." 95 #define E_INTERNAL "Internal error, errno=%d" 96 #define E_RELPATH "Full pathname required for cdevice,bdevice and pathname attributes." 97 98 99 /* 100 * Macros 101 * stdmsg(r,l,s,t) Using fmtmsg(), write a standard message to the 102 * standard error stream. 103 * Where: 104 * r The recoverability of the error 105 * l The label-component 106 * s The severity-component 107 * t The text-component 108 */ 109 110 #define stdmsg(r,l,s,t) (void) fmtmsg(MM_PRINT|MM_UTIL|r,l,s,t,MM_NULLACT,MM_NULLTAG) 111 112 113 /* 114 * Static data 115 * msg Space for message's text-component 116 */ 117 118 static char msg[256]; /* Space for text of message */ 119 120 /* 121 * char *mklbl(cmd) 122 * char *cmd 123 * 124 * This function builds a standard label from the command used to invoke 125 * this process and the standard label prefix ("UX:") 126 * 127 * Arguments: 128 * char *cmd The command used to invoke this process. 129 * 130 * Returns: char * 131 * Pointer to malloc()ed space containing the standard label, 132 * or (char *) NULL if an error occurred. 133 */ 134 135 static char * 136 mklbl(cmd) 137 char *cmd; 138 { 139 /* Automatic data */ 140 char *rtn; /* Value to return */ 141 char *p; /* Temporary */ 142 143 /* Find the 1st char of the basename of the command */ 144 if (p = strrchr(cmd, '/')) p++; 145 else p = cmd; 146 147 /* Allocate and build the string value to return */ 148 if (rtn = (char *) malloc(strlen("UX:")+strlen(p)+1)) { 149 (void) strcpy(rtn, "UX:"); 150 (void) strcat(rtn, p); 151 } 152 153 154 /* Now that we've done all of that work, change the environment 155 * so that only the text-component is written by fmtmsg(). 156 * (This should go away in SVR4.1) 157 */ 158 159 (void) putenv("MSGVERB=text"); 160 161 162 /* Done */ 163 return(rtn); 164 } 165 166 /* 167 * putdev -a alias [attribute=value [...]] 168 * putdev -m alias attribute=value [attribute=value [...]] 169 * putdev -d alias [attribute [...]] 170 * 171 * Modify the device-table. If -a specified, add a record for <alias> 172 * to the table. If -m specified, modify the attributes specified for 173 * the <device> specified. If -d specified, remove the specified 174 * attributes from the specified device or remove the specified device. 175 * 176 * Options: 177 * -a Add an alias description to the device table 178 * -m Modify an existing device description 179 * -d (if no attributes specified) remove the specified 180 * device from the device table, or (if attributes 181 * specified) remove the specified attributes from 182 * the specified device. 183 * 184 * Exit values: 185 * 0 All went well 186 * 1 Usage error (includes specifying "alias" as an 187 * <attribute>) 188 * 2 The device table file could not be opened, read 189 * or modified 190 * 3 If -a, the alias already exists. Otherwise, the 191 * specified device does not exist in the table 192 * 4 One of the specified attributes did not exist 193 * for the device and therefore wasn't removed 194 */ 195 196 main(argc, argv) 197 int argc; /* Argument count */ 198 char *argv[]; /* Argument list */ 199 { 200 /* Automatic data */ 201 char **plist; /* Ptr to list of undef'nd attrs */ 202 char *lbl; /* Ptr to label for messages */ 203 char *alias; /* Ptr to <alias> on command-line */ 204 char *device; /* Ptr to <device> on command-line */ 205 char *p; /* Temp ptr to char */ 206 int noerr; /* FLAG, TRUE if all's well */ 207 int a_seen; /* TRUE if -a seen on command-line */ 208 int m_seen; /* TRUE if -m seen on command-line */ 209 int d_seen; /* TRUE if -a seen on command-line */ 210 int optchar; /* Option extracted */ 211 int exitcd; /* Value to return at exit */ 212 int nattrs; /* Number of attributes on command */ 213 214 215 /* Generate the label for messages */ 216 lbl = mklbl(argv[0]); 217 218 /* Extract arguments - validate usage */ 219 noerr = TRUE; 220 a_seen = FALSE; 221 m_seen = FALSE; 222 d_seen = FALSE; 223 opterr = FALSE; 224 while ((optchar = getopt(argc, argv, "a:d:m:")) != EOF) switch (optchar) { 225 226 case 'a': 227 if (!(a_seen || m_seen || d_seen)) { 228 a_seen = TRUE; 229 alias = optarg; 230 } 231 else noerr = FALSE; 232 break; 233 234 case 'd': 235 if (!(a_seen || m_seen || d_seen)) { 236 d_seen = TRUE; 237 device = optarg; 238 } 239 else noerr = FALSE; 240 break; 241 242 case 'm': 243 if (!(a_seen || m_seen || d_seen)) { 244 m_seen = TRUE; 245 device = optarg; 246 } 247 else noerr = FALSE; 248 break; 249 250 case '?': 251 default: 252 noerr = FALSE; 253 } 254 255 256 /* Write a usage message if we've seen a blatant error */ 257 if (!(a_seen || m_seen || d_seen) || !noerr) { 258 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 259 exit(EX_ERROR); 260 } 261 262 263 /* Set up */ 264 exitcd = EX_OK; 265 nattrs = argc - optind; 266 267 268 /* putdev -a alias [attr=value [...]] */ 269 270 if (a_seen) { 271 272 /* Syntax check */ 273 if (nattrs < 0) { 274 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 275 exitcd = EX_ERROR; 276 } else { 277 278 /* Attempt to add the new alias */ 279 if (!(_adddevtabrec(alias, &argv[optind]))) { 280 281 /* Attempt failed. Write appropriate error message. */ 282 283 switch(errno) { 284 285 /* 286 * EINVAL indicates that <alias> is not valid or "alias" 287 * was mentioned as <attr> in <attr>=<value> pair. If the 288 * alias is a valid alias, assume that's the problem. 289 */ 290 291 case EINVAL: 292 if (_validalias(alias)) 293 p = E_NOALIAS; 294 else (void) snprintf(p=msg, sizeof(msg), E_INVALIAS, alias); 295 stdmsg(MM_NRECOV, lbl, MM_ERROR, p); 296 exitcd = EX_ERROR; 297 break; 298 299 /* 300 * EEXIST indicates that the alias <alias> already exists 301 * in the device table. 302 */ 303 304 case EEXIST: 305 (void) snprintf(msg, sizeof(msg), E_ALIASIS, alias); 306 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 307 exitcd = EX_EXISTS; 308 break; 309 310 /* 311 * EACCES and ENOENT indicate problems reading or writing 312 * the device table. 313 */ 314 315 case EACCES: 316 case ENOENT: 317 p = _devtabpath(); 318 if (access(p, R_OK) == 0) 319 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 320 else 321 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 322 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 323 exitcd = EX_DEVTAB; 324 break; 325 326 /* 327 * EAGAIN indicates that an attribute was defined on the 328 * command line more than once. 329 */ 330 331 case EAGAIN: 332 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE); 333 exitcd = EX_ERROR; 334 break; 335 336 /* 337 * ENXIO indicates that a relative pathname was supplied 338 * for the cdevice, bdevice or pathname attributes. Full 339 * pathnames are required for these attributes. 340 */ 341 case ENXIO: 342 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH); 343 exitcd = EX_RELPATH; 344 break; 345 346 /* 347 * Some other problem (odd?) 348 */ 349 350 default: 351 (void) sprintf(msg, E_INTERNAL, errno); 352 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 353 exitcd = EX_ERROR; 354 } 355 } 356 } 357 } /* End -a case */ 358 359 360 /* putdev -m device attr=value [...] */ 361 362 else if (m_seen) { 363 364 /* Check usage */ 365 366 if (nattrs <= 0) { 367 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 368 exitcd = EX_ERROR; 369 } else { 370 371 /* Attempt to modify a device's record */ 372 if (!(_moddevtabrec(device, &argv[optind]))) { 373 374 /* Modification attempt failed */ 375 376 switch(errno) { 377 378 /* 379 * EINVAL indicates that "alias" was used as an attribute 380 * in an <attr>=<value> pair. 381 */ 382 383 case EINVAL: 384 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS); 385 exitcd = EX_ERROR; 386 break; 387 388 /* 389 * ENODEV indicates that the device that was to 390 * be modified doesn't exist. 391 */ 392 393 case ENODEV: 394 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 395 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 396 exitcd = EX_EXISTS; 397 break; 398 399 /* 400 * ENOENT indicates that the device-table doesn't exist. 401 */ 402 403 case ENOENT: 404 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 405 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 406 exitcd = EX_DEVTAB; 407 break; 408 409 /* 410 * EACCES indicates that there was a problem reading the 411 * old device table or creating the new table. If the 412 * old table is readable, assume that we can't create the 413 * new table. Otherwise, assume that the old table isn't 414 * accessible. 415 */ 416 417 case EACCES: 418 p = _devtabpath(); 419 if (access(p, R_OK) == 0) 420 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 421 else 422 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 423 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 424 exitcd = EX_DEVTAB; 425 break; 426 427 /* 428 * EAGAIN indicates that an attribute was specified more than 429 * once on the command line. 430 */ 431 432 case EAGAIN: 433 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE); 434 exitcd = EX_ERROR; 435 break; 436 437 /* 438 * ENXIO indicates that a relative pathname was supplied 439 * for the cdevice, bdevice or pathname attributes. Full 440 * pathnames are required for these attributes. 441 */ 442 case ENXIO: 443 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH); 444 exitcd = EX_RELPATH; 445 break; 446 447 /* 448 * Some strange problem... 449 */ 450 451 default: 452 (void) sprintf(msg, E_INTERNAL, errno); 453 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 454 exitcd = EX_ERROR; 455 } 456 } 457 } 458 } /* End -m case */ 459 460 else if (d_seen) { 461 462 /* putdev -d device [attr [...]] */ 463 464 /* Check usage */ 465 if (nattrs < 0) { 466 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 467 exitcd = EX_ERROR; 468 } else { 469 470 /* 471 * Determine case (removing a device or attributes 472 * to a device. 473 */ 474 475 if (nattrs == 0) { 476 477 /* putdev -d device */ 478 479 /* Attempt to remove the specified device */ 480 if (!(_rmdevtabrec(device))) switch(errno) { 481 482 /* 483 * ENODEV indicates that the named device is not 484 * defined in the device table. 485 */ 486 487 case ENODEV: 488 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 489 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 490 exitcd = EX_EXISTS; 491 break; 492 493 /* 494 * ENOENT indicates that the device table can't 495 * be found. 496 */ 497 498 case ENOENT: 499 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 500 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 501 exitcd = EX_DEVTAB; 502 break; 503 504 /* 505 * EACCES indicates that there was a problem reading the 506 * old device table or creating the new table. If the 507 * old table is readable, assume that we can't create the 508 * new table. Otherwise, assume that the old table isn't 509 * accessible. 510 */ 511 512 case EACCES: 513 p = _devtabpath(); 514 if (access(p, R_OK) == 0) 515 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 516 else 517 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 518 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 519 exitcd = EX_DEVTAB; 520 break; 521 522 /* 523 * Some strange problem... 524 */ 525 526 default: 527 (void) sprintf(msg, E_INTERNAL, errno); 528 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 529 exitcd = EX_ERROR; 530 531 } /* End switch */ 532 } 533 else { 534 535 /* putdev -d device attr [attr [...]] */ 536 537 /* 538 * Attempt to remove the specified attributes from the 539 * specified device. 540 */ 541 if (!(_rmdevtabattrs(device, &argv[optind], &plist))) switch(errno) { 542 543 /* 544 * EINVAL indicates that a named attribute was not 545 * defined for the specified device or "alias" was 546 * requested. If "plist" points to a list of attrs, 547 * the former is the problem. Otherwise, the latter 548 * is the problem. 549 */ 550 551 case EINVAL: 552 if (plist) { 553 exitcd = EX_ATTRIB; 554 for (; *plist; plist++) { 555 (void) snprintf(msg, sizeof(msg), E_NOATTR, *plist); 556 stdmsg(MM_RECOVER, lbl, MM_WARNING, msg); 557 } 558 } else { 559 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS); 560 exitcd = EX_ERROR; 561 } 562 break; 563 564 /* 565 * ENODEV indicates that the named device is not 566 * defined in the device table. 567 */ 568 569 case ENODEV: 570 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 571 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 572 exitcd = EX_EXISTS; 573 break; 574 575 /* 576 * ENOENT indicates that the device table can't 577 * be found. 578 */ 579 580 case ENOENT: 581 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 582 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 583 exitcd = EX_DEVTAB; 584 break; 585 586 /* 587 * EACCES indicates that there was a problem reading the 588 * old device table or creating the new table. If the 589 * old table is readable, assume that we can't create the 590 * new table. Otherwise, assume that the old table isn't 591 * accessible. 592 */ 593 594 case EACCES: 595 p = _devtabpath(); 596 if (access(p, R_OK) == 0) 597 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 598 else 599 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 600 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 601 exitcd = EX_DEVTAB; 602 break; 603 604 /* 605 * Some strange problem... 606 */ 607 608 default: 609 (void) sprintf(msg, E_INTERNAL, errno); 610 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 611 exitcd = EX_ERROR; 612 613 } /* End switch */ 614 615 } /* End "putdev -d device attr [...]" case */ 616 617 } /* End passes usage-check case */ 618 619 } /* End -d case */ 620 621 622 /* Done. Return exit code (determined above) */ 623 return(exitcd); 624 } /* main() */ 625