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