/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <locale.h> #include <dlfcn.h> #include <errno.h> #include "_crle.h" #include "conv.h" #include "msg.h" /* * crle(1) entry point and argument processing. * * Two passes of the arguments are carried out; the first collects any single * instance options and establishes defaults that might be appropriate for * other arguments: * * -64 operate on, or apply, 64-bit objects (default is 32-bit). * * -c file defines the output configuration file. * * -f flag flags for dldump(3dl). * * -o dir defines the output directory for any dldump(3dl) objects * that follow. For backward compatibility (RTC_VER_ONE only * allowed one output directory) allow the first occurrence of this * specification to catch any previous files. If not specified, * the configuration files parent directory is used). * * -u update any existing configuration file. Any additional * arguments supplied will be added to the new configuration * information. * * -v verbose mode. * * The second pass collects all other options and constructs an internal * string table which will be used to create the eventual configuration file. * * -a name add the individual name, with an alternative to the * configuration cache. No alternative is created via dldump(3dl), * it is the users responsibility to furnish the alternative. * * -A name add the individual name, with an optional alternative to the * configuration cache. No alternative is created via dldump(3dl), * it is the users responsibility to furnish the alternative. * * -e envar replaceable environment variable * * -E envar permanent environment variable * * -i name add the individual name to the configuration cache. If name * is a directory each shared object within the directory is added * to the cache. * * -I name same as -i, but in addition any ELF objects are dldump(3dl)'ed. * * -g name add the group name to the configuration cache. Each object is * expanded to determine its dependencies and these are added to * the cache. If name is a directory each shared object within the * directory and its dependencies are added to the cache. * * -G app same as -g, but in addition any ELF objects are dldump(3dl)'ed. * * -l dir library search directory * * -s dir trusted (secure) directory * * -t type search directory type (ELF or AOUT). */ /* * Establish a structure for maintaining current object directory attributes. * We wish to validate the access of any object directory that will be written * to (dldump(3dl), and thus by maintaining a current object directory and its * intended use we can perform this validation later. */ typedef struct { char *o_objdir; uint_t o_flags; } Objdir; /*ARGSUSED2*/ int main(int argc, char **argv, char **envp) { Crle_desc crle = { 0 }; int c, error = 0; char **lib; Alist *objdirs = NULL; Objdir *objdir, *iobjdir; struct stat ostatus, nstatus; int c_class; if ((objdir = iobjdir = alist_append(&objdirs, NULL, sizeof (Objdir), AL_CNT_CRLE)) == NULL) return (1); /* * Establish locale. */ (void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY)); (void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS)); /* * Initialization configuration information. */ crle.c_name = argv[0]; crle.c_flags |= CRLE_ADDID; crle.c_strbkts = 503; crle.c_inobkts = 251; c_class = M_CLASS; /* * First argument pass. */ while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) { switch (c) { case '6': /* operate on 64-bit objects */ if (optarg[0] != '4') { (void) fprintf(stderr, MSG_INTL(MSG_ARG_ILLEGAL), crle.c_name, MSG_ORIG(MSG_ARG_6), optarg); error = 1; } c_class = ELFCLASS64; break; case 'A': /* create optional */ /* FALLTHROUGH */ /* alternative */ case 'a': /* create alternative */ crle.c_flags |= (CRLE_CREAT | CRLE_ALTER); objdir->o_flags |= (CRLE_CREAT | CRLE_ALTER); break; case 'c': /* define the config file */ if (crle.c_confil) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT), crle.c_name, MSG_ORIG(MSG_ARG_C)); error = 1; } crle.c_confil = optarg; break; case 'e': /* replaceable env variable */ crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT); break; case 'E': /* permanent env variable */ crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT); break; case 'f': /* dldump(3dl) flags */ if (crle.c_dlflags) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT), crle.c_name, MSG_ORIG(MSG_ARG_F)); error = 1; } if ((crle.c_dlflags = dlflags(&crle, (const char *)optarg)) == 0) error = 1; break; case 'G': /* group object */ crle.c_flags |= (CRLE_DUMP | CRLE_ALTER); objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER); /* FALLTHROUGH */ case 'g': crle.c_flags |= CRLE_CREAT; objdir->o_flags |= CRLE_CREAT; break; case 'I': /* individual object */ crle.c_flags |= (CRLE_DUMP | CRLE_ALTER); objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER); /* FALLTHROUGH */ case 'i': crle.c_flags |= CRLE_CREAT; objdir->o_flags |= CRLE_CREAT; break; case 'l': /* library search path */ if (crle.c_flags & CRLE_AOUT) crle.c_flags |= CRLE_ADLIB; else crle.c_flags |= CRLE_EDLIB; crle.c_flags |= CRLE_CREAT; break; case 'o': /* define an object directory */ if (objdir->o_objdir) { if ((objdir = alist_append(&objdirs, NULL, sizeof (Objdir), AL_CNT_CRLE)) == NULL) return (1); } objdir->o_objdir = optarg; break; case 's': /* trusted (secure) path */ if (crle.c_flags & CRLE_AOUT) crle.c_flags |= CRLE_ASLIB; else crle.c_flags |= CRLE_ESLIB; crle.c_flags |= CRLE_CREAT; break; case 't': /* search path type */ if (strcmp((const char *)optarg, MSG_ORIG(MSG_STR_ELF)) == 0) crle.c_flags &= ~CRLE_AOUT; else if (strcmp((const char *)optarg, MSG_ORIG(MSG_STR_AOUT)) == 0) crle.c_flags |= CRLE_AOUT; else { (void) fprintf(stderr, MSG_INTL(MSG_ARG_TYPE), crle.c_name, optarg); error = 1; } break; case 'u': /* update mode */ crle.c_flags |= (CRLE_CREAT | CRLE_UPDATE); break; case 'v': /* verbose mode */ crle.c_flags |= CRLE_VERBOSE; break; default: error = 2; } } if (optind != argc) error = 2; /* * Determine the configuration file, which in the case of an existing * error condition is required in the final error message. */ if (crle.c_confil == NULL) { crle.c_flags |= CRLE_CONFDEF; if (c_class == ELFCLASS32) { crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG); } else { crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG_64); } } /* * Now that we've generated as many file/directory processing errors * as we can, return if any fatal error conditions occurred. */ if (error) { if (error == 2) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE), crle.c_name); } else if (crle.c_flags & CRLE_CREAT) { (void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE), crle.c_name, crle.c_confil); } return (1); } /* * Apply any additional defaults. */ if (crle.c_dlflags == 0) crle.c_dlflags = RTLD_REL_RELATIVE; crle.c_audit = (char *)MSG_ORIG(MSG_ENV_LD_AUDIT); (void) elf_version(EV_CURRENT); /* * If we're updating an existing file or not creating a configuration * file at all, investigate the original. */ if ((crle.c_flags & CRLE_UPDATE) || ((crle.c_flags & CRLE_CREAT) == 0)) { switch (inspectconfig(&crle, c_class)) { case INSCFG_RET_OK: if ((crle.c_flags & CRLE_UPDATE) == 0) return (0); break; case INSCFG_RET_FAIL: return (1); case INSCFG_RET_NEED64: c_class = ELFCLASS64; break; } } /* * Ensure that the right version (32 or 64-bit) of this program * is running. The 32 and 64-bit compilers may align fields within * structures differently. Using the right version of crle for * the config file ensures that all linker components will see * the same layout, without the need for special code. */ #ifdef _ELF64 if (c_class == ELFCLASS32) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS), crle.c_name, crle.c_confil); return (1); } #else if (c_class == ELFCLASS64) { (void) conv_check_native(argv, envp); /* * conv_check_native() should not return, as we expect * the 64-bit version to have executed on top of us. * If it does, it means there is no 64-bit support * available on this system. */ (void) fprintf(stderr, MSG_INTL(MSG_ISA32_NO64SUP), crle.c_name); return (1); } #endif if (crle.c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_CONFILE), crle.c_confil); /* * Make sure the configuration file is accessible. Stat the file to * determine its dev number - this is used to determine whether the * temporary configuration file we're about to build can be renamed or * must be copied to its final destination. */ (void) umask(022); if (access(crle.c_confil, (R_OK | W_OK)) == 0) { crle.c_flags |= CRLE_EXISTS; if (stat(crle.c_confil, &ostatus) != 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle.c_name, crle.c_confil, strerror(err)); return (1); } } else if (errno != ENOENT) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name, crle.c_confil, strerror(err)); return (1); } else { int fd; /* * Try opening the file now, if it works delete it, there may * be a lot of processing ahead of us, so we'll come back and * create the real thing later. */ if ((fd = open(crle.c_confil, (O_RDWR | O_CREAT | O_TRUNC), 0666)) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle.c_name, crle.c_confil, strerror(err)); return (1); } if (fstat(fd, &ostatus) != 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle.c_name, crle.c_confil, strerror(err)); return (1); } (void) close(fd); (void) unlink(crle.c_confil); } /* * If an object directory is required to hold dldump(3dl) output assign * a default if necessary and insure we're able to write there. */ if (crle.c_flags & CRLE_ALTER) { if (objdir->o_objdir == NULL) { char *str; /* * Use the configuration files directory. */ if ((str = strrchr(crle.c_confil, '/')) == NULL) objdir->o_objdir = (char *)MSG_ORIG(MSG_DIR_DOT); else { int len = str - crle.c_confil; if ((objdir->o_objdir = malloc(len + 1)) == NULL) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), crle.c_name, strerror(err)); return (1); } (void) strncpy(objdir->o_objdir, crle.c_confil, len); objdir->o_objdir[len] = '\0'; } } /* * If we're going to dldump(3dl) images ourself make sure we * can access any directories. */ if (crle.c_flags & CRLE_DUMP) { Objdir *objdir; Aliste idx; int err = 0; for (ALIST_TRAVERSE(objdirs, idx, objdir)) { if (crle.c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_OBJDIR), objdir->o_objdir); if ((objdir->o_flags & CRLE_DUMP) == 0) continue; if (access(objdir->o_objdir, (R_OK | W_OK)) != 0) { err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name, objdir->o_objdir, strerror(err)); } } if (err) return (1); } } /* * Establish any initial object directory. */ crle.c_objdir = iobjdir->o_objdir; /* * Create a temporary file name in which to build the configuration * information. */ if ((crle.c_tempname = tempnam(MSG_ORIG(MSG_TMP_DIR), MSG_ORIG(MSG_TMP_PFX))) == NULL) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_TEMPNAME), crle.c_name, strerror(err)); return (1); } if ((crle.c_tempfd = open(crle.c_tempname, (O_RDWR | O_CREAT), 0666)) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle.c_name, crle.c_tempname, strerror(err)); return (1); } if (stat(crle.c_tempname, &nstatus) != 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle.c_name, crle.c_tempname, strerror(err)); return (1); } if (ostatus.st_dev != nstatus.st_dev) crle.c_flags |= CRLE_DIFFDEV; /* * Second pass. */ error = 0; optind = 1; while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) { const char *str; int flag = 0; switch (c) { case '6': break; case 'A': /* alternative is optional */ flag = RTC_OBJ_OPTINAL; /* FALLTHROUGH */ case 'a': /* alternative required */ flag |= (RTC_OBJ_ALTER | RTC_OBJ_CMDLINE); if (inspect(&crle, (const char *)optarg, flag) != 0) error = 1; break; case 'c': break; case 'e': if ((flag = addenv(&crle, (const char *)optarg, RTC_ENV_REPLACE)) == 0) error = 1; else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1)) (void) printf(MSG_INTL(MSG_DIA_RPLENV), (const char *)optarg); break; case 'E': if ((flag = addenv(&crle, (const char *)optarg, RTC_ENV_PERMANT)) == 0) error = 1; else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1)) (void) printf(MSG_INTL(MSG_DIA_PRMENV), (const char *)optarg); break; case 'f': break; case 'G': /* group object */ flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER); /* FALLTHROUGH */ case 'g': flag |= (RTC_OBJ_GROUP | RTC_OBJ_CMDLINE); if (inspect(&crle, (const char *)optarg, flag) != 0) error = 1; break; case 'I': /* individual object */ flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER); /* FALLTHROUGH */ case 'i': flag |= RTC_OBJ_CMDLINE; if (inspect(&crle, (const char *)optarg, flag) != 0) error = 1; break; case 'l': /* library search path */ if (crle.c_flags & CRLE_AOUT) { str = MSG_ORIG(MSG_STR_AOUT); lib = &crle.c_adlibpath; } else { str = MSG_ORIG(MSG_STR_ELF); lib = &crle.c_edlibpath; } if (addlib(&crle, lib, (const char *)optarg) != 0) error = 1; else if (crle.c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_DLIBPTH), str, (const char *)optarg); break; case 'o': crle.c_objdir = optarg; break; case 's': /* trusted (secure) path */ if (crle.c_flags & CRLE_AOUT) { str = MSG_ORIG(MSG_STR_AOUT); lib = &crle.c_aslibpath; } else { str = MSG_ORIG(MSG_STR_ELF); lib = &crle.c_eslibpath; } if (addlib(&crle, lib, (const char *)optarg) != 0) error = 1; else if (crle.c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_TLIBPTH), str, (const char *)optarg); break; case 't': /* search path type */ if (strcmp((const char *)optarg, MSG_ORIG(MSG_STR_ELF)) == 0) crle.c_flags &= ~CRLE_AOUT; else crle.c_flags |= CRLE_AOUT; break; case 'u': break; case 'v': break; } } /* * Now that we've generated as many file/directory processing errors * as we can, return if any fatal error conditions occurred. */ if (error) { (void) unlink(crle.c_tempname); if (crle.c_flags & CRLE_CREAT) { (void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE), crle.c_name, crle.c_confil); } return (1); } /* * Create a temporary configuration file. */ if (genconfig(&crle) != 0) { (void) unlink(crle.c_tempname); return (1); } /* * If dldump(3dl) images are required spawn a process to create them. */ if (crle.c_flags & CRLE_DUMP) { if (dump(&crle) != 0) { (void) unlink(crle.c_tempname); return (1); } } /* * Copy the finished temporary configuration file to its final home. */ if (updateconfig(&crle) != 0) return (1); return (0); }