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