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 <sys/types.h> 27 #include <sys/stat.h> 28 #include <fcntl.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <unistd.h> 32 #include <locale.h> 33 #include <dlfcn.h> 34 #include <errno.h> 35 #include "_crle.h" 36 #include "conv.h" 37 #include "msg.h" 38 39 40 /* 41 * crle(1) entry point and argument processing. 42 * 43 * Two passes of the arguments are carried out; the first collects any single 44 * instance options and establishes defaults that might be appropriate for 45 * other arguments: 46 * 47 * -64 operate on, or apply, 64-bit objects (default is 32-bit). 48 * 49 * -c file defines the output configuration file. 50 * 51 * -f flag flags for dldump(3dl). 52 * 53 * -o dir defines the output directory for any dldump(3dl) objects 54 * that follow. For backward compatibility (RTC_VER_ONE only 55 * allowed one output directory) allow the first occurrence of this 56 * specification to catch any previous files. If not specified, 57 * the configuration files parent directory is used). 58 * 59 * -u update any existing configuration file. Any additional 60 * arguments supplied will be added to the new configuration 61 * information. 62 * 63 * -v verbose mode. 64 * 65 * The second pass collects all other options and constructs an internal 66 * string table which will be used to create the eventual configuration file. 67 * 68 * -a name add the individual name, with an alternative to the 69 * configuration cache. No alternative is created via dldump(3dl), 70 * it is the users responsibility to furnish the alternative. 71 * 72 * -A name add the individual name, with an optional alternative to the 73 * configuration cache. No alternative is created via dldump(3dl), 74 * it is the users responsibility to furnish the alternative. 75 * 76 * -e envar replaceable environment variable 77 * 78 * -E envar permanent environment variable 79 * 80 * -i name add the individual name to the configuration cache. If name 81 * is a directory each shared object within the directory is added 82 * to the cache. 83 * 84 * -I name same as -i, but in addition any ELF objects are dldump(3dl)'ed. 85 * 86 * -g name add the group name to the configuration cache. Each object is 87 * expanded to determine its dependencies and these are added to 88 * the cache. If name is a directory each shared object within the 89 * directory and its dependencies are added to the cache. 90 * 91 * -G app same as -g, but in addition any ELF objects are dldump(3dl)'ed. 92 * 93 * -l dir library search directory 94 * 95 * -s dir trusted (secure) directory 96 * 97 * -t type search directory type (ELF or AOUT). 98 */ 99 100 /* 101 * Establish a structure for maintaining current object directory attributes. 102 * We wish to validate the access of any object directory that will be written 103 * to (dldump(3dl), and thus by maintaining a current object directory and its 104 * intended use we can perform this validation later. 105 */ 106 typedef struct { 107 char *o_objdir; 108 uint_t o_flags; 109 } Objdir; 110 111 /*ARGSUSED2*/ 112 int 113 main(int argc, char **argv, char **envp) 114 { 115 Crle_desc crle = { 0 }; 116 int c, error = 0; 117 char **lib; 118 Alist *objdirs = NULL; 119 Objdir *objdir, *iobjdir; 120 struct stat ostatus, nstatus; 121 int c_class; 122 123 if ((objdir = iobjdir = alist_append(&objdirs, NULL, sizeof (Objdir), 124 AL_CNT_CRLE)) == NULL) 125 return (1); 126 127 /* 128 * Establish locale. 129 */ 130 (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); 131 (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); 132 133 /* 134 * Initialization configuration information. 135 */ 136 crle.c_name = argv[0]; 137 crle.c_flags |= CRLE_ADDID; 138 crle.c_strbkts = 503; 139 crle.c_inobkts = 251; 140 c_class = M_CLASS; 141 142 /* 143 * First argument pass. 144 */ 145 while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) { 146 switch (c) { 147 148 case '6': /* operate on 64-bit objects */ 149 if (optarg[0] != '4') { 150 (void) fprintf(stderr, 151 MSG_INTL(MSG_ARG_ILLEGAL), crle.c_name, 152 MSG_ORIG(MSG_ARG_6), optarg); 153 error = 1; 154 } 155 156 c_class = ELFCLASS64; 157 break; 158 159 case 'A': /* create optional */ 160 /* FALLTHROUGH */ /* alternative */ 161 case 'a': /* create alternative */ 162 crle.c_flags |= (CRLE_CREAT | CRLE_ALTER); 163 objdir->o_flags |= (CRLE_CREAT | CRLE_ALTER); 164 break; 165 166 case 'c': /* define the config file */ 167 if (crle.c_confil) { 168 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT), 169 crle.c_name, MSG_ORIG(MSG_ARG_C)); 170 error = 1; 171 } 172 crle.c_confil = optarg; 173 break; 174 175 case 'e': /* replaceable env variable */ 176 crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT); 177 break; 178 179 case 'E': /* permanent env variable */ 180 crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT); 181 break; 182 183 case 'f': /* dldump(3dl) flags */ 184 if (crle.c_dlflags) { 185 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT), 186 crle.c_name, MSG_ORIG(MSG_ARG_F)); 187 error = 1; 188 } 189 if ((crle.c_dlflags = dlflags(&crle, 190 (const char *)optarg)) == 0) 191 error = 1; 192 break; 193 194 case 'G': /* group object */ 195 crle.c_flags |= (CRLE_DUMP | CRLE_ALTER); 196 objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER); 197 /* FALLTHROUGH */ 198 case 'g': 199 crle.c_flags |= CRLE_CREAT; 200 objdir->o_flags |= CRLE_CREAT; 201 break; 202 203 case 'I': /* individual object */ 204 crle.c_flags |= (CRLE_DUMP | CRLE_ALTER); 205 objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER); 206 /* FALLTHROUGH */ 207 case 'i': 208 crle.c_flags |= CRLE_CREAT; 209 objdir->o_flags |= CRLE_CREAT; 210 break; 211 212 case 'l': /* library search path */ 213 if (crle.c_flags & CRLE_AOUT) 214 crle.c_flags |= CRLE_ADLIB; 215 else 216 crle.c_flags |= CRLE_EDLIB; 217 crle.c_flags |= CRLE_CREAT; 218 break; 219 220 case 'o': /* define an object directory */ 221 if (objdir->o_objdir) { 222 if ((objdir = alist_append(&objdirs, NULL, 223 sizeof (Objdir), AL_CNT_CRLE)) == NULL) 224 return (1); 225 } 226 objdir->o_objdir = optarg; 227 break; 228 229 case 's': /* trusted (secure) path */ 230 if (crle.c_flags & CRLE_AOUT) 231 crle.c_flags |= CRLE_ASLIB; 232 else 233 crle.c_flags |= CRLE_ESLIB; 234 crle.c_flags |= CRLE_CREAT; 235 break; 236 237 case 't': /* search path type */ 238 if (strcmp((const char *)optarg, 239 MSG_ORIG(MSG_STR_ELF)) == 0) 240 crle.c_flags &= ~CRLE_AOUT; 241 else if (strcmp((const char *)optarg, 242 MSG_ORIG(MSG_STR_AOUT)) == 0) 243 crle.c_flags |= CRLE_AOUT; 244 else { 245 (void) fprintf(stderr, MSG_INTL(MSG_ARG_TYPE), 246 crle.c_name, optarg); 247 error = 1; 248 } 249 break; 250 251 case 'u': /* update mode */ 252 crle.c_flags |= (CRLE_CREAT | CRLE_UPDATE); 253 break; 254 255 case 'v': /* verbose mode */ 256 crle.c_flags |= CRLE_VERBOSE; 257 break; 258 259 default: 260 error = 2; 261 } 262 } 263 264 if (optind != argc) 265 error = 2; 266 267 /* 268 * Determine the configuration file, which in the case of an existing 269 * error condition is required in the final error message. 270 */ 271 if (crle.c_confil == NULL) { 272 crle.c_flags |= CRLE_CONFDEF; 273 if (c_class == ELFCLASS32) { 274 crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG); 275 } else { 276 crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG_64); 277 } 278 } 279 280 /* 281 * Now that we've generated as many file/directory processing errors 282 * as we can, return if any fatal error conditions occurred. 283 */ 284 if (error) { 285 if (error == 2) { 286 (void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE), 287 crle.c_name); 288 } else if (crle.c_flags & CRLE_CREAT) { 289 (void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE), 290 crle.c_name, crle.c_confil); 291 } 292 return (1); 293 } 294 295 /* 296 * Apply any additional defaults. 297 */ 298 if (crle.c_dlflags == 0) 299 crle.c_dlflags = RTLD_REL_RELATIVE; 300 301 crle.c_audit = (char *)MSG_ORIG(MSG_ENV_LD_AUDIT); 302 303 (void) elf_version(EV_CURRENT); 304 305 /* 306 * If we're updating an existing file or not creating a configuration 307 * file at all, investigate the original. 308 */ 309 if ((crle.c_flags & CRLE_UPDATE) || 310 ((crle.c_flags & CRLE_CREAT) == 0)) { 311 switch (inspectconfig(&crle, c_class)) { 312 case INSCFG_RET_OK: 313 if ((crle.c_flags & CRLE_UPDATE) == 0) 314 return (0); 315 break; 316 case INSCFG_RET_FAIL: 317 return (1); 318 case INSCFG_RET_NEED64: 319 c_class = ELFCLASS64; 320 break; 321 } 322 } 323 324 /* 325 * Ensure that the right version (32 or 64-bit) of this program 326 * is running. The 32 and 64-bit compilers may align fields within 327 * structures differently. Using the right version of crle for 328 * the config file ensures that all linker components will see 329 * the same layout, without the need for special code. 330 */ 331 #ifdef _ELF64 332 if (c_class == ELFCLASS32) { 333 (void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS), 334 crle.c_name, crle.c_confil); 335 return (1); 336 } 337 #else 338 if (c_class == ELFCLASS64) { 339 (void) conv_check_native(argv, envp); 340 341 /* 342 * conv_check_native() should not return, as we expect 343 * the 64-bit version to have executed on top of us. 344 * If it does, it means there is no 64-bit support 345 * available on this system. 346 */ 347 (void) fprintf(stderr, MSG_INTL(MSG_ISA32_NO64SUP), 348 crle.c_name); 349 return (1); 350 } 351 #endif 352 353 if (crle.c_flags & CRLE_VERBOSE) 354 (void) printf(MSG_INTL(MSG_DIA_CONFILE), crle.c_confil); 355 356 /* 357 * Make sure the configuration file is accessible. Stat the file to 358 * determine its dev number - this is used to determine whether the 359 * temporary configuration file we're about to build can be renamed or 360 * must be copied to its final destination. 361 */ 362 (void) umask(022); 363 if (access(crle.c_confil, (R_OK | W_OK)) == 0) { 364 crle.c_flags |= CRLE_EXISTS; 365 366 if (stat(crle.c_confil, &ostatus) != 0) { 367 int err = errno; 368 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 369 crle.c_name, crle.c_confil, strerror(err)); 370 return (1); 371 } 372 } else if (errno != ENOENT) { 373 int err = errno; 374 (void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name, 375 crle.c_confil, strerror(err)); 376 return (1); 377 } else { 378 int fd; 379 380 /* 381 * Try opening the file now, if it works delete it, there may 382 * be a lot of processing ahead of us, so we'll come back and 383 * create the real thing later. 384 */ 385 if ((fd = open(crle.c_confil, (O_RDWR | O_CREAT | O_TRUNC), 386 0666)) == -1) { 387 int err = errno; 388 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 389 crle.c_name, crle.c_confil, strerror(err)); 390 return (1); 391 } 392 if (fstat(fd, &ostatus) != 0) { 393 int err = errno; 394 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 395 crle.c_name, crle.c_confil, strerror(err)); 396 return (1); 397 } 398 (void) close(fd); 399 (void) unlink(crle.c_confil); 400 } 401 402 /* 403 * If an object directory is required to hold dldump(3dl) output assign 404 * a default if necessary and insure we're able to write there. 405 */ 406 if (crle.c_flags & CRLE_ALTER) { 407 if (objdir->o_objdir == NULL) { 408 char *str; 409 410 /* 411 * Use the configuration files directory. 412 */ 413 if ((str = strrchr(crle.c_confil, '/')) == NULL) 414 objdir->o_objdir = 415 (char *)MSG_ORIG(MSG_DIR_DOT); 416 else { 417 int len = str - crle.c_confil; 418 419 if ((objdir->o_objdir = 420 malloc(len + 1)) == NULL) { 421 int err = errno; 422 (void) fprintf(stderr, 423 MSG_INTL(MSG_SYS_MALLOC), 424 crle.c_name, strerror(err)); 425 return (1); 426 } 427 (void) strncpy(objdir->o_objdir, 428 crle.c_confil, len); 429 objdir->o_objdir[len] = '\0'; 430 } 431 } 432 433 /* 434 * If we're going to dldump(3dl) images ourself make sure we 435 * can access any directories. 436 */ 437 if (crle.c_flags & CRLE_DUMP) { 438 Objdir *objdir; 439 Aliste idx; 440 int err = 0; 441 442 for (ALIST_TRAVERSE(objdirs, idx, objdir)) { 443 if (crle.c_flags & CRLE_VERBOSE) 444 (void) printf(MSG_INTL(MSG_DIA_OBJDIR), 445 objdir->o_objdir); 446 447 if ((objdir->o_flags & CRLE_DUMP) == 0) 448 continue; 449 450 if (access(objdir->o_objdir, 451 (R_OK | W_OK)) != 0) { 452 err = errno; 453 (void) fprintf(stderr, 454 MSG_INTL(MSG_SYS_ACCESS), 455 crle.c_name, objdir->o_objdir, 456 strerror(err)); 457 } 458 } 459 if (err) 460 return (1); 461 } 462 } 463 464 /* 465 * Establish any initial object directory. 466 */ 467 crle.c_objdir = iobjdir->o_objdir; 468 469 /* 470 * Create a temporary file name in which to build the configuration 471 * information. 472 */ 473 if ((crle.c_tempname = tempnam(MSG_ORIG(MSG_TMP_DIR), 474 MSG_ORIG(MSG_TMP_PFX))) == NULL) { 475 int err = errno; 476 (void) fprintf(stderr, MSG_INTL(MSG_SYS_TEMPNAME), 477 crle.c_name, strerror(err)); 478 return (1); 479 } 480 if ((crle.c_tempfd = open(crle.c_tempname, (O_RDWR | O_CREAT), 481 0666)) == -1) { 482 int err = errno; 483 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 484 crle.c_name, crle.c_tempname, strerror(err)); 485 return (1); 486 } 487 if (stat(crle.c_tempname, &nstatus) != 0) { 488 int err = errno; 489 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 490 crle.c_name, crle.c_tempname, strerror(err)); 491 return (1); 492 } 493 if (ostatus.st_dev != nstatus.st_dev) 494 crle.c_flags |= CRLE_DIFFDEV; 495 496 /* 497 * Second pass. 498 */ 499 error = 0; 500 optind = 1; 501 while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) { 502 const char *str; 503 int flag = 0; 504 505 switch (c) { 506 507 case '6': 508 break; 509 510 case 'A': /* alternative is optional */ 511 flag = RTC_OBJ_OPTINAL; 512 /* FALLTHROUGH */ 513 case 'a': /* alternative required */ 514 flag |= (RTC_OBJ_ALTER | RTC_OBJ_CMDLINE); 515 if (inspect(&crle, (const char *)optarg, flag) != 0) 516 error = 1; 517 break; 518 519 case 'c': 520 break; 521 522 case 'e': 523 if ((flag = addenv(&crle, (const char *)optarg, 524 RTC_ENV_REPLACE)) == 0) 525 error = 1; 526 else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1)) 527 (void) printf(MSG_INTL(MSG_DIA_RPLENV), 528 (const char *)optarg); 529 break; 530 531 case 'E': 532 if ((flag = addenv(&crle, (const char *)optarg, 533 RTC_ENV_PERMANT)) == 0) 534 error = 1; 535 else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1)) 536 (void) printf(MSG_INTL(MSG_DIA_PRMENV), 537 (const char *)optarg); 538 break; 539 540 case 'f': 541 break; 542 543 case 'G': /* group object */ 544 flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER); 545 /* FALLTHROUGH */ 546 case 'g': 547 flag |= (RTC_OBJ_GROUP | RTC_OBJ_CMDLINE); 548 if (inspect(&crle, (const char *)optarg, flag) != 0) 549 error = 1; 550 break; 551 552 case 'I': /* individual object */ 553 flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER); 554 /* FALLTHROUGH */ 555 case 'i': 556 flag |= RTC_OBJ_CMDLINE; 557 if (inspect(&crle, (const char *)optarg, flag) != 0) 558 error = 1; 559 break; 560 561 case 'l': /* library search path */ 562 if (crle.c_flags & CRLE_AOUT) { 563 str = MSG_ORIG(MSG_STR_AOUT); 564 lib = &crle.c_adlibpath; 565 } else { 566 str = MSG_ORIG(MSG_STR_ELF); 567 lib = &crle.c_edlibpath; 568 } 569 if (addlib(&crle, lib, (const char *)optarg) != 0) 570 error = 1; 571 else if (crle.c_flags & CRLE_VERBOSE) 572 (void) printf(MSG_INTL(MSG_DIA_DLIBPTH), 573 str, (const char *)optarg); 574 break; 575 576 case 'o': 577 crle.c_objdir = optarg; 578 break; 579 580 case 's': /* trusted (secure) path */ 581 if (crle.c_flags & CRLE_AOUT) { 582 str = MSG_ORIG(MSG_STR_AOUT); 583 lib = &crle.c_aslibpath; 584 } else { 585 str = MSG_ORIG(MSG_STR_ELF); 586 lib = &crle.c_eslibpath; 587 } 588 if (addlib(&crle, lib, (const char *)optarg) != 0) 589 error = 1; 590 else if (crle.c_flags & CRLE_VERBOSE) 591 (void) printf(MSG_INTL(MSG_DIA_TLIBPTH), 592 str, (const char *)optarg); 593 break; 594 595 case 't': /* search path type */ 596 if (strcmp((const char *)optarg, 597 MSG_ORIG(MSG_STR_ELF)) == 0) 598 crle.c_flags &= ~CRLE_AOUT; 599 else 600 crle.c_flags |= CRLE_AOUT; 601 break; 602 603 case 'u': 604 break; 605 606 case 'v': 607 break; 608 } 609 } 610 611 /* 612 * Now that we've generated as many file/directory processing errors 613 * as we can, return if any fatal error conditions occurred. 614 */ 615 if (error) { 616 (void) unlink(crle.c_tempname); 617 if (crle.c_flags & CRLE_CREAT) { 618 (void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE), 619 crle.c_name, crle.c_confil); 620 } 621 return (1); 622 } 623 624 /* 625 * Create a temporary configuration file. 626 */ 627 if (genconfig(&crle) != 0) { 628 (void) unlink(crle.c_tempname); 629 return (1); 630 } 631 632 /* 633 * If dldump(3dl) images are required spawn a process to create them. 634 */ 635 if (crle.c_flags & CRLE_DUMP) { 636 if (dump(&crle) != 0) { 637 (void) unlink(crle.c_tempname); 638 return (1); 639 } 640 } 641 642 /* 643 * Copy the finished temporary configuration file to its final home. 644 */ 645 if (updateconfig(&crle) != 0) 646 return (1); 647 648 return (0); 649 } 650