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 2005 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 int 197 main(int argc, char *argv[]) 198 { 199 /* Automatic data */ 200 char **plist; /* Ptr to list of undef'nd attrs */ 201 char *lbl; /* Ptr to label for messages */ 202 char *alias; /* Ptr to <alias> on command-line */ 203 char *device; /* Ptr to <device> on command-line */ 204 char *p; /* Temp ptr to char */ 205 int noerr; /* FLAG, TRUE if all's well */ 206 int a_seen; /* TRUE if -a seen on command-line */ 207 int m_seen; /* TRUE if -m seen on command-line */ 208 int d_seen; /* TRUE if -a seen on command-line */ 209 int optchar; /* Option extracted */ 210 int exitcd; /* Value to return at exit */ 211 int nattrs; /* Number of attributes on command */ 212 213 214 /* Generate the label for messages */ 215 lbl = mklbl(argv[0]); 216 217 /* Extract arguments - validate usage */ 218 noerr = TRUE; 219 a_seen = FALSE; 220 m_seen = FALSE; 221 d_seen = FALSE; 222 opterr = FALSE; 223 while ((optchar = getopt(argc, argv, "a:d:m:")) != EOF) switch (optchar) { 224 225 case 'a': 226 if (!(a_seen || m_seen || d_seen)) { 227 a_seen = TRUE; 228 alias = optarg; 229 } 230 else noerr = FALSE; 231 break; 232 233 case 'd': 234 if (!(a_seen || m_seen || d_seen)) { 235 d_seen = TRUE; 236 device = optarg; 237 } 238 else noerr = FALSE; 239 break; 240 241 case 'm': 242 if (!(a_seen || m_seen || d_seen)) { 243 m_seen = TRUE; 244 device = optarg; 245 } 246 else noerr = FALSE; 247 break; 248 249 case '?': 250 default: 251 noerr = FALSE; 252 } 253 254 255 /* Write a usage message if we've seen a blatant error */ 256 if (!(a_seen || m_seen || d_seen) || !noerr) { 257 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 258 exit(EX_ERROR); 259 } 260 261 262 /* Set up */ 263 exitcd = EX_OK; 264 nattrs = argc - optind; 265 266 267 /* putdev -a alias [attr=value [...]] */ 268 269 if (a_seen) { 270 271 /* Syntax check */ 272 if (nattrs < 0) { 273 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 274 exitcd = EX_ERROR; 275 } else { 276 277 /* Attempt to add the new alias */ 278 if (!(_adddevtabrec(alias, &argv[optind]))) { 279 280 /* Attempt failed. Write appropriate error message. */ 281 282 switch(errno) { 283 284 /* 285 * EINVAL indicates that <alias> is not valid or "alias" 286 * was mentioned as <attr> in <attr>=<value> pair. If the 287 * alias is a valid alias, assume that's the problem. 288 */ 289 290 case EINVAL: 291 if (_validalias(alias)) 292 p = E_NOALIAS; 293 else (void) snprintf(p=msg, sizeof(msg), E_INVALIAS, alias); 294 stdmsg(MM_NRECOV, lbl, MM_ERROR, p); 295 exitcd = EX_ERROR; 296 break; 297 298 /* 299 * EEXIST indicates that the alias <alias> already exists 300 * in the device table. 301 */ 302 303 case EEXIST: 304 (void) snprintf(msg, sizeof(msg), E_ALIASIS, alias); 305 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 306 exitcd = EX_EXISTS; 307 break; 308 309 /* 310 * EACCES and ENOENT indicate problems reading or writing 311 * the device table. 312 */ 313 314 case EACCES: 315 case ENOENT: 316 p = _devtabpath(); 317 if (access(p, R_OK) == 0) 318 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 319 else 320 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 321 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 322 exitcd = EX_DEVTAB; 323 break; 324 325 /* 326 * EAGAIN indicates that an attribute was defined on the 327 * command line more than once. 328 */ 329 330 case EAGAIN: 331 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE); 332 exitcd = EX_ERROR; 333 break; 334 335 /* 336 * ENXIO indicates that a relative pathname was supplied 337 * for the cdevice, bdevice or pathname attributes. Full 338 * pathnames are required for these attributes. 339 */ 340 case ENXIO: 341 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH); 342 exitcd = EX_RELPATH; 343 break; 344 345 /* 346 * Some other problem (odd?) 347 */ 348 349 default: 350 (void) sprintf(msg, E_INTERNAL, errno); 351 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 352 exitcd = EX_ERROR; 353 } 354 } 355 } 356 } /* End -a case */ 357 358 359 /* putdev -m device attr=value [...] */ 360 361 else if (m_seen) { 362 363 /* Check usage */ 364 365 if (nattrs <= 0) { 366 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 367 exitcd = EX_ERROR; 368 } else { 369 370 /* Attempt to modify a device's record */ 371 if (!(_moddevtabrec(device, &argv[optind]))) { 372 373 /* Modification attempt failed */ 374 375 switch(errno) { 376 377 /* 378 * EINVAL indicates that "alias" was used as an attribute 379 * in an <attr>=<value> pair. 380 */ 381 382 case EINVAL: 383 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS); 384 exitcd = EX_ERROR; 385 break; 386 387 /* 388 * ENODEV indicates that the device that was to 389 * be modified doesn't exist. 390 */ 391 392 case ENODEV: 393 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 394 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 395 exitcd = EX_EXISTS; 396 break; 397 398 /* 399 * ENOENT indicates that the device-table doesn't exist. 400 */ 401 402 case ENOENT: 403 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 404 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 405 exitcd = EX_DEVTAB; 406 break; 407 408 /* 409 * EACCES indicates that there was a problem reading the 410 * old device table or creating the new table. If the 411 * old table is readable, assume that we can't create the 412 * new table. Otherwise, assume that the old table isn't 413 * accessible. 414 */ 415 416 case EACCES: 417 p = _devtabpath(); 418 if (access(p, R_OK) == 0) 419 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 420 else 421 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 422 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 423 exitcd = EX_DEVTAB; 424 break; 425 426 /* 427 * EAGAIN indicates that an attribute was specified more than 428 * once on the command line. 429 */ 430 431 case EAGAIN: 432 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_MULTIPLE); 433 exitcd = EX_ERROR; 434 break; 435 436 /* 437 * ENXIO indicates that a relative pathname was supplied 438 * for the cdevice, bdevice or pathname attributes. Full 439 * pathnames are required for these attributes. 440 */ 441 case ENXIO: 442 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_RELPATH); 443 exitcd = EX_RELPATH; 444 break; 445 446 /* 447 * Some strange problem... 448 */ 449 450 default: 451 (void) sprintf(msg, E_INTERNAL, errno); 452 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 453 exitcd = EX_ERROR; 454 } 455 } 456 } 457 } /* End -m case */ 458 459 else if (d_seen) { 460 461 /* putdev -d device [attr [...]] */ 462 463 /* Check usage */ 464 if (nattrs < 0) { 465 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_USAGE); 466 exitcd = EX_ERROR; 467 } else { 468 469 /* 470 * Determine case (removing a device or attributes 471 * to a device. 472 */ 473 474 if (nattrs == 0) { 475 476 /* putdev -d device */ 477 478 /* Attempt to remove the specified device */ 479 if (!(_rmdevtabrec(device))) switch(errno) { 480 481 /* 482 * ENODEV indicates that the named device is not 483 * defined in the device table. 484 */ 485 486 case ENODEV: 487 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 488 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 489 exitcd = EX_EXISTS; 490 break; 491 492 /* 493 * ENOENT indicates that the device table can't 494 * be found. 495 */ 496 497 case ENOENT: 498 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 499 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 500 exitcd = EX_DEVTAB; 501 break; 502 503 /* 504 * EACCES indicates that there was a problem reading the 505 * old device table or creating the new table. If the 506 * old table is readable, assume that we can't create the 507 * new table. Otherwise, assume that the old table isn't 508 * accessible. 509 */ 510 511 case EACCES: 512 p = _devtabpath(); 513 if (access(p, R_OK) == 0) 514 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 515 else 516 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 517 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 518 exitcd = EX_DEVTAB; 519 break; 520 521 /* 522 * Some strange problem... 523 */ 524 525 default: 526 (void) sprintf(msg, E_INTERNAL, errno); 527 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 528 exitcd = EX_ERROR; 529 530 } /* End switch */ 531 } 532 else { 533 534 /* putdev -d device attr [attr [...]] */ 535 536 /* 537 * Attempt to remove the specified attributes from the 538 * specified device. 539 */ 540 if (!(_rmdevtabattrs(device, &argv[optind], &plist))) switch(errno) { 541 542 /* 543 * EINVAL indicates that a named attribute was not 544 * defined for the specified device or "alias" was 545 * requested. If "plist" points to a list of attrs, 546 * the former is the problem. Otherwise, the latter 547 * is the problem. 548 */ 549 550 case EINVAL: 551 if (plist) { 552 exitcd = EX_ATTRIB; 553 for (; *plist; plist++) { 554 (void) snprintf(msg, sizeof(msg), E_NOATTR, *plist); 555 stdmsg(MM_RECOVER, lbl, MM_WARNING, msg); 556 } 557 } else { 558 stdmsg(MM_NRECOV, lbl, MM_ERROR, E_NOALIAS); 559 exitcd = EX_ERROR; 560 } 561 break; 562 563 /* 564 * ENODEV indicates that the named device is not 565 * defined in the device table. 566 */ 567 568 case ENODEV: 569 (void) snprintf(msg, sizeof(msg), E_NODEV, device); 570 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 571 exitcd = EX_EXISTS; 572 break; 573 574 /* 575 * ENOENT indicates that the device table can't 576 * be found. 577 */ 578 579 case ENOENT: 580 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, _devtabpath()); 581 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 582 exitcd = EX_DEVTAB; 583 break; 584 585 /* 586 * EACCES indicates that there was a problem reading the 587 * old device table or creating the new table. If the 588 * old table is readable, assume that we can't create the 589 * new table. Otherwise, assume that the old table isn't 590 * accessible. 591 */ 592 593 case EACCES: 594 p = _devtabpath(); 595 if (access(p, R_OK) == 0) 596 (void) snprintf(msg, sizeof(msg), E_NOMKDTAB, p); 597 else 598 (void) snprintf(msg, sizeof(msg), E_NODEVTAB, p); 599 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 600 exitcd = EX_DEVTAB; 601 break; 602 603 /* 604 * Some strange problem... 605 */ 606 607 default: 608 (void) sprintf(msg, E_INTERNAL, errno); 609 stdmsg(MM_NRECOV, lbl, MM_ERROR, msg); 610 exitcd = EX_ERROR; 611 612 } /* End switch */ 613 614 } /* End "putdev -d device attr [...]" case */ 615 616 } /* End passes usage-check case */ 617 618 } /* End -d case */ 619 620 621 /* Done. Return exit code (determined above) */ 622 return(exitcd); 623 } /* main() */ 624