# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (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 # # #ident "%Z%%M% %I% %E% SMI" # # Copyright (c) 1999 by Sun Microsystems, Inc. # All rights reserved. include $(SRC)/cmd/sgs/Makefile.sub # # 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 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 2018, Joyent, Inc. # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # PROG= crle include $(SRC)/cmd/Makefile.cmd include $(SRC)/cmd/sgs/Makefile.com COMOBJ= config.o crle.o depend.o dump.o \ inspect.o hash.o print.o util.o BLTOBJ= msg.o SGSCOMMONOBJ= alist.o OBJS= $(BLTOBJ) $(COMOBJ) $(SGSCOMMONOBJ) MAPFILE= $(MAPFILE.NGB) MAPOPT= $(MAPFILE:%=-Wl,-M%) CPPFLAGS += -I$(SRC)/common/sgsrtcid -I$(SRC)/uts/$(ARCH)/sys \ -D__EXTENSIONS__ LLDFLAGS = '-R$$ORIGIN/../lib' LLDFLAGS64 = '-R$$ORIGIN/../../lib/$(MACH64)' LDFLAGS += $(VERSREF) $(MAPOPT) $(LLDFLAGS) $(ZNOLAZYLOAD) # Hammerhead: Use 64-bit lib paths, -ldl for dlopen/dlsym in libconv LDLIBS += -lelf $(CONVLIBDIR64) -lconv -ldl BLTDEFS= msg.h BLTDATA= msg.c BLTMESG= $(SGSMSGDIR)/crle BLTFILES= $(BLTDEFS) $(BLTDATA) $(BLTMESG) SGSMSGCOM= ../common/crle.msg SGSMSGTARG= $(SGSMSGCOM) SGSMSGALL= $(SGSMSGCOM) SGSMSGFLAGS += -h $(BLTDEFS) -d $(BLTDATA) -m $(BLTMESG) -n crle_msg SRCS= $(COMOBJ:%.o=../common/%.c) $(BLTDATA) \ $(COMMON:%.o=$(SGSCOMMON)/%.c) CLEANFILES += $(BLTFILES) # # 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. # # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # %.o: ../common/%.c $(COMPILE.c) $< $(POST_PROCESS_O) %.o: $(SGSCOMMON)/%.c $(COMPILE.c) $< $(POST_PROCESS_O) install: all $(ROOTPROG) all: $(PROG) $(PROG): $(OBJS) $(MAPFILE) $(LINK.c) -o $@ $(OBJS) $(LDLIBS) $(POST_PROCESS) clean: $(RM) $(OBJS) $(CLEANFILES) include $(SRC)/cmd/Makefile.targ # Derived source and header files (messaging). catalog: $(BLTMESG) chkmsg: $(SRCS) sh $(CHKMSG) $(CHKMSGFLAGS) $(SRCS) # Hammerhead: GNU Make grouped target syntax (&:) - was dmake + syntax $(BLTDEFS) $(BLTDATA) $(BLTMESG) &: $(SGSMSGALL) $(SGSMSG) $(SGSMSGFLAGS) $(SGSMSGALL) # # 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 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2019 OmniOS Community Edition (OmniOSce) Association. # include ../Makefile.com .KEEP_STATE: CPPFLAGS += -D_ELF64 LLDFLAGS = $(LLDFLAGS64) CONVLIBDIR = $(CONVLIBDIR64) ELFLIBDIR = $(ELFLIBDIR64) LDDBGLIBDIR = $(LDDBGLIBDIR64) ROOTPROG = $(ROOTPROG64) include ../Makefile.targ include $(SRC)/Makefile.master.64 /* * 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. */ #ifndef __CRLE_H #define __CRLE_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* * Hash table support routines. */ typedef struct hash_obj Hash_obj; typedef struct hash_ent Hash_ent; typedef struct hash_tbl Hash_tbl; typedef enum { HASH_STR, HASH_INT } Hash_type; /* * Each unique object (identified by dev/inode pair) is maintained as a hash * object. This descriptor identifies the object (file or directory), whether * it has an alternate, or represents a non-existent object. */ struct hash_obj { Half o_flags; /* object identification */ Hash_tbl *o_tbl; /* its dev/inode table */ char *o_alter; /* any alternate path */ Word o_calter; /* and its conf offset */ char *o_path; /* the objects real path */ Lword o_info; /* information for cache */ /* consistency checks */ }; /* * Each element of a hash table is maintained as a hash entry. Each element * points to a unique hash object. Many elements can point to the same hash * object (as is the case with linked files). Elements on the string table * hash lists identify their directory id, either the directory itself, or the * files that belong to the directory. These directory and file entries are * what will be converted into object descriptors in the final cache file. */ struct hash_ent { Hash_ent *e_next; /* next hash item */ Word e_hash; /* hash value (or inode no.) */ Addr e_key; /* name (or inode no.) */ int e_off; /* offset of file in dirname */ Half e_id; /* directory identifier */ Half e_flags; /* entry specific flags */ Word e_cnt; /* no. of files in directory */ Hash_ent *e_dir; /* files directory */ Hash_ent *e_path; /* files full path entry */ Hash_obj *e_obj; /* unique object */ Rtc_obj *e_cobj; /* final configuration object */ }; /* * Each hash table is maintained as a hash table descriptor. Each dev has a * hash table of inodes, and all directory and file entries are also maintained * on the string table hash table. */ struct hash_tbl { ulong_t t_ident; /* dev no. for inode cache */ int t_size; /* no. of buckets */ Hash_type t_type; /* HASH_INT or HASH_STR */ Hash_ent **t_entry; /* entries */ }; #define HASH_FND_ENT 0x01 /* search for existing hash entry */ #define HASH_ADD_ENT 0x02 /* add hash entry */ /* * Environment variable support. */ typedef struct { const char *e_str; /* complete environment string */ size_t e_varsz; /* variable size, ie. the LD_XXX part */ size_t e_totsz; /* total string size */ uint_t e_flags; } Env_desc; /* * Filter/filtee association support. The filtees are a list of Hash_ent's. */ typedef struct { Hash_ent *f_fent; /* filter */ const char *f_str; /* filtee string and its associated */ size_t f_strsz; /* size */ APlist *f_filtee; /* filtees */ } Flt_desc; /* * Global data for final configuration files construction. */ typedef struct crle_desc { char *c_name; /* calling program */ char *c_tempname; /* temporary file, file descriptor */ int c_tempfd; /* mmapped address and size */ Addr c_tempaddr; size_t c_tempsize; Addr c_tempheadaddr; /* Ptr to Rtc_head within c_tempaddr */ char *c_confil; /* configuration file */ char *c_objdir; /* current object directory for */ /* dldump(3C) */ char *c_audit; /* audit library name */ uint_t c_flags; /* state flags for crle processing */ int c_dlflags; /* current dldump(3C) flags */ int c_strbkts; /* internal hash table initialization */ int c_inobkts; /* parameters */ uint_t c_dirnum; /* no. of directories processed */ uint_t c_filenum; /* no. of files processed */ uint_t c_hashstrnum; /* no. of hashed strings to create */ Hash_tbl *c_strtbl; /* string table and size */ size_t c_strsize; APlist *c_inotbls; /* list of inode tables */ const char *c_app; /* specific application */ char *c_edlibpath; /* ELF default library path */ char *c_eslibpath; /* ELF secure library path */ APlist *c_env; /* environment variables */ uint_t c_envnum; /* and associated number */ APlist *c_flt; /* filter/filtee associations */ uint_t c_fltrnum; /* and associated filter number */ uint_t c_fltenum; /* and associated filtee number */ } Crle_desc; #define CRLE_CREAT 0x0001 /* config file creation required */ #define CRLE_ALTER 0x0002 /* alternative entries required */ #define CRLE_DUMP 0x0004 /* alternative create by dldump(3C) */ #define CRLE_ADDID 0x0008 /* Add Rtc_id to head of new files */ #define CRLE_VERBOSE 0x0010 /* verbose mode */ /* 0x20 was previously used for a.out support */ #define CRLE_EXISTS 0x0040 /* config file already exists */ #define CRLE_DIFFDEV 0x0080 /* config file and temporary exist on */ /* different filesystems */ #define CRLE_CONFDEF 0x0100 /* configuration file is default */ #define CRLE_UPDATE 0x0200 /* update existing configuration file */ #define CRLE_RPLENV 0x0400 /* replaceable environment variable */ #define CRLE_PRMENV 0x0800 /* permanent environment variable */ #define CRLE_EDLIB 0x1000 /* default elf search path supplied */ #define CRLE_ESLIB 0x2000 /* default elf secure path supplied */ #define AL_CNT_CRLE 10 /* * Return type code returned by inspectconfig() */ typedef enum { INSCFG_RET_OK = 0, /* Config file is OK */ INSCFG_RET_FAIL = 1, /* Config file has a fatal problem */ INSCFG_RET_NEED64 = 2, /* 64-bit config seen by 32-bit crle */ } INSCFG_RET; /* * Local functions. */ extern int addlib(Crle_desc *, char **, const char *); extern int addenv(Crle_desc *, const char *, uint_t); extern int depend(Crle_desc *, const char *, Half, GElf_Ehdr *); extern int dlflags(Crle_desc *, const char *); extern int dump(Crle_desc *); extern int genconfig(Crle_desc *); extern Hash_ent *get_hash(Hash_tbl *, Addr, Half, int); extern int inspect(Crle_desc *, const char *, Half); extern Hash_tbl *make_hash(int, Hash_type, ulong_t); extern INSCFG_RET inspectconfig(Crle_desc *, int); extern int updateconfig(Crle_desc *); #ifdef __cplusplus } #endif #endif /* __CRLE_H */ /* * 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 #include #include #include #include #include #include #include "rtc.h" #include "_crle.h" #include "msg.h" #define MAXNBKTS 10007 static const int hashsize[] = { 3, 7, 13, 31, 53, 67, 83, 97, 101, 151, 211, 251, 307, 353, 401, 457, 503, 557, 601, 653, 701, 751, 809, 859, 907, 953, 1009, 1103, 1201, 1301, 1409, 1511, 1601, 1709, 1801, 1901, 2003, 2111, 2203, 2309, 2411, 2503, 2609, 2707, 2801, 2903, 3001, 3109, 3203, 3301, 3407, 3511, 3607, 3701, 3803, 3907, 4001, 5003, 6101, 7001, 8101, 9001, MAXNBKTS }; /* * Generate a configuration file from the internal configuration information. * (very link-editor like). */ int genconfig(Crle_desc *crle) { int ndx, bkt; size_t size, hashoff = 0, stroff = 0, objoff = 0; size_t diroff = 0, fileoff = 0, envoff = 0; size_t fltroff = 0, flteoff = 0; Addr addr; Rtc_id *id; Rtc_head *head; Word *hashtbl, *hashbkt, *hashchn, hashbkts = 0; char *strtbl, *_strtbl; Rtc_obj *objtbl; Rtc_dir *dirtbl; Rtc_file *filetbl; Rtc_env *envtbl; Rtc_fltr *fltrtbl; Rtc_flte *fltetbl, * _fltetbl; Hash_tbl *stbl = crle->c_strtbl; Hash_ent *ent; /* * Establish the size of the configuration file. */ size = S_ROUND(sizeof (Rtc_head), sizeof (Word)); if (crle->c_hashstrnum) { hashoff = size; /* * Increment the hash string number to account for an initial * null entry. Indexes start at 1 to simplify hash lookup. */ crle->c_hashstrnum++; /* * Determine the hash table size. Establish the number of * buckets from the number of strings, the number of chains is * equivalent to the number of objects, and two entries for the * nbucket and nchain entries. */ for (ndx = 0; ndx < (sizeof (hashsize) / sizeof (int)); ndx++) { if (crle->c_hashstrnum > hashsize[ndx]) continue; hashbkts = hashsize[ndx]; break; } if (hashbkts == 0) hashbkts = MAXNBKTS; size += ((2 + hashbkts + crle->c_hashstrnum) * sizeof (Word)); size = S_ROUND(size, sizeof (Lword)); objoff = size; /* * Add the object table size (account for an 8-byte alignment * requirement for each object). */ size += (crle->c_hashstrnum * S_ROUND(sizeof (Rtc_obj), sizeof (Lword))); /* * Add the file descriptor arrays. */ fileoff = size; size += S_ROUND((crle->c_filenum * sizeof (Rtc_file)), sizeof (Word)); /* * Add the directory descriptor array. */ diroff = size; size += S_ROUND((crle->c_dirnum * sizeof (Rtc_dir)), sizeof (Word)); } /* * Add any environment string array (insure zero last entry). */ if (crle->c_envnum) { envoff = size; size += S_ROUND(((crle->c_envnum + 1) * sizeof (Rtc_env)), sizeof (Word)); } /* * Add any filter/filtee association arrays (insure zero last entry for * the filter array, the filtee arrays are already accounted for). */ if (crle->c_fltrnum) { fltroff = size; size += S_ROUND(((crle->c_fltrnum + 1) * sizeof (Rtc_fltr)), sizeof (Word)); flteoff = size; size += S_ROUND((crle->c_fltenum * sizeof (Rtc_flte)), sizeof (Word)); } /* * Add the string table size (this may contain library and/or secure * path strings, in addition to any directory/file strings). */ if (crle->c_strsize) { stroff = size; size += S_ROUND(crle->c_strsize, sizeof (Word)); } /* Account for addition of Rtc_id block at the start */ if (crle->c_flags & CRLE_ADDID) size += sizeof (Rtc_id); /* * Truncate our temporary file now that we know its size and map it. */ if (ftruncate(crle->c_tempfd, size) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC), crle->c_name, crle->c_tempname, strerror(err)); (void) close(crle->c_tempfd); return (1); } if ((addr = (Addr)mmap(0, size, (PROT_READ | PROT_WRITE), MAP_SHARED, crle->c_tempfd, 0)) == (Addr)-1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), crle->c_name, crle->c_tempname, strerror(err)); (void) close(crle->c_tempfd); return (1); } /* * Save the mapped files info for possible dldump(3C) updates. */ crle->c_tempaddr = addr; crle->c_tempsize = size; /* * Rtc_id goes at the top, followed by the Rtc_head. We base * all offset calculations relative to Rtc_head, not from * the top of the file. This eases backwards compatability to * older versons that lacked the Rtc_id at the top. */ if (crle->c_flags & CRLE_ADDID) { /* The contents of the Rtc_id are all known at compile time */ static const Rtc_id id_template = { RTC_ID_MAG0, RTC_ID_MAG1, RTC_ID_MAG2, RTC_ID_MAG3, M_CLASS, M_DATA, M_MACH, { 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; id = (Rtc_id *) addr; *id = id_template; /* Fill in the Rtc_id data */ addr += sizeof (Rtc_id); } else { id = NULL; } crle->c_tempheadaddr = addr; head = (Rtc_head *)addr; /* * Establish the real address of each of the structures within the file. */ head->ch_hash = hashoff; /* LINTED */ hashtbl = (Word *)(CAST_PTRINT(char *, head->ch_hash) + addr); head->ch_obj = objoff; /* LINTED */ objtbl = (Rtc_obj *)(CAST_PTRINT(char *, head->ch_obj) + addr); objtbl = (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword)); head->ch_file = fileoff; /* LINTED */ filetbl = (Rtc_file *)(CAST_PTRINT(char *, head->ch_file) + addr); head->ch_dir = diroff; /* LINTED */ dirtbl = (Rtc_dir *)(CAST_PTRINT(char *, head->ch_dir) + addr); head->ch_env = envoff; /* LINTED */ envtbl = (Rtc_env *)(CAST_PTRINT(char *, head->ch_env) + addr); head->ch_fltr = fltroff; /* LINTED */ fltrtbl = (Rtc_fltr *)(CAST_PTRINT(char *, head->ch_fltr) + addr); head->ch_flte = flteoff; /* LINTED */ fltetbl = _fltetbl = (Rtc_flte *)(CAST_PTRINT(char *, head->ch_flte) + addr); head->ch_str = stroff; strtbl = _strtbl = (char *)(CAST_PTRINT(char *, head->ch_str) + addr); /* * Fill in additional basic header information. */ head->ch_version = RTC_VER_CURRENT; if (crle->c_flags & CRLE_ALTER) head->ch_cnflags |= RTC_HDR_ALTER; if (crle->c_flags & CRLE_DUMP) { head->ch_cnflags |= RTC_HDR_IGNORE; head->ch_dlflags = crle->c_dlflags; } #ifdef _ELF64 head->ch_cnflags |= RTC_HDR_64; #endif head->ch_cnflags |= RTC_HDR_UPM; /* * If we have a hash table then there are directory and file entries * to process. */ if (crle->c_hashstrnum) { hashtbl[0] = hashbkts; hashtbl[1] = crle->c_hashstrnum; hashbkt = &hashtbl[2]; hashchn = &hashtbl[2 + hashbkts]; /* * Insure all hash chain and directory/filename table entries * are cleared. */ (void) memset(hashchn, 0, (crle->c_hashstrnum * sizeof (Word))); (void) memset(dirtbl, 0, (strtbl - (char *)dirtbl)); /* * Loop through the current string table list inspecting only * directories. */ for (ndx = 1, bkt = 0; bkt < stbl->t_size; bkt++) { for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) { Word hashval; Hash_obj *obj = ent->e_obj; char *dir = (char *)ent->e_key; Rtc_dir *_dirtbl; /* * Skip any empty and non-directory entries. */ if ((obj == NULL) || ((obj->o_flags & RTC_OBJ_DIRENT) == 0)) continue; /* * Assign basic object attributes. */ objtbl->co_hash = ent->e_hash; objtbl->co_id = ent->e_id; objtbl->co_flags = obj->o_flags | ent->e_flags; objtbl->co_info = obj->o_info; ent->e_cobj = objtbl; /* * Assign the directory name (from its key), * and copy its name to the string table. */ objtbl->co_name = (Addr)(_strtbl - strtbl); (void) strcpy(_strtbl, dir); _strtbl += strlen(dir) + 1; /* * Establish an entry in the directory table and * reserve space for its associated filename * entries (note, we add a trailing null file * entry to simplify later inspection of the * final configuration file. */ _dirtbl = &dirtbl[ent->e_id - 1]; _dirtbl->cd_file = CAST_PTRINT(Word, ((char *)filetbl- addr)); _dirtbl->cd_obj = CAST_PTRINT(Word, ((char *)objtbl - addr)); /* LINTED */ filetbl = (Rtc_file *)((char *)filetbl + ((ent->e_cnt + 1) * sizeof (Rtc_file))); /* * Add this object to the hash table. */ hashval = ent->e_hash % hashbkts; hashchn[ndx] = hashbkt[hashval]; hashbkt[hashval] = ndx++; /* * Increment Rt_obj pointer (make sure pointer * falls on an 8-byte boundary). */ objtbl = (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword)); } } /* * Now collect all pathnames. These are typically full * pathnames, but may also be relative. Simple filenames are * recorded as offsets into these pathnames, thus we need to * establish the new pathname first. */ for (bkt = 0; bkt < stbl->t_size; bkt++) { for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) { Word hashval; Hash_obj *obj = ent->e_obj; char *file = (char *)ent->e_key; char *_str; Rtc_dir *_dirtbl; Rtc_file *_filetbl; int _id; /* * Skip empty and directory entries, and any * simple filename entries. */ if ((obj == NULL) || (obj->o_flags & RTC_OBJ_DIRENT) || (ent->e_off)) continue; /* * Assign basic object attributes. */ objtbl->co_hash = ent->e_hash; objtbl->co_id = ent->e_id; objtbl->co_flags = obj->o_flags | ent->e_flags; objtbl->co_info = obj->o_info; ent->e_cobj = objtbl; /* * Assign the file name (from its key), * and copy its name to the string table. */ objtbl->co_name = (Addr)(_strtbl - strtbl); (void) strcpy(_strtbl, file); _strtbl += strlen(file) + 1; /* * Add this file to its associated directory. */ _dirtbl = &dirtbl[ent->e_id - 1]; /* LINTED */ _filetbl = (Rtc_file *)(CAST_PTRINT(char *, _dirtbl->cd_file) + addr); _id = --ent->e_dir->e_cnt; _filetbl[_id].cf_obj = CAST_PTRINT(Word, ((char *)objtbl - addr)); /* * If object has an alternative, record it in * the string table and assign the alternate * pointer. The new alternative offset is * retained for reuse in other filename entries. */ if ((objtbl->co_flags & RTC_OBJ_ALTER) && (obj->o_calter == 0)) { _str = obj->o_alter; objtbl->co_alter = obj->o_calter = (Addr)(_strtbl - strtbl); (void) strcpy(_strtbl, _str); _strtbl += strlen(_str) + 1; } else objtbl->co_alter = obj->o_calter; /* * If object identifies the specific application * for which this cache is relevant, record it * in the header. */ if ((objtbl->co_flags & (RTC_OBJ_APP | RTC_OBJ_REALPTH)) == (RTC_OBJ_APP | RTC_OBJ_REALPTH)) head->ch_app = _filetbl[_id].cf_obj; /* * Add this object to the hash table. */ hashval = ent->e_hash % hashbkts; hashchn[ndx] = hashbkt[hashval]; hashbkt[hashval] = ndx++; /* * Increment Rt_obj pointer (make sure pointer * falls on an 8-byte boundary). */ objtbl = (Rtc_obj *) S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword)); } } /* * Finally pick off any simple filenames. */ for (bkt = 0; bkt < stbl->t_size; bkt++) { for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) { Word hashval; Hash_obj * obj = ent->e_obj; Rtc_dir * _dirtbl; Rtc_file * _filetbl; int _id; /* * Skip everything except simple filenames. */ if (ent->e_off == 0) continue; /* * Assign basic object attributes. */ objtbl->co_hash = ent->e_hash; objtbl->co_id = ent->e_id; objtbl->co_flags = obj->o_flags | ent->e_flags; objtbl->co_info = obj->o_info; objtbl->co_alter = obj->o_calter; ent->e_cobj = objtbl; /* * Assign the file name from its full name. */ objtbl->co_name = (Addr)(CAST_PTRINT(char *, ent->e_path->e_cobj->co_name) + ent->e_off); /* * Add this file to its associated directory. */ _dirtbl = &dirtbl[ent->e_id - 1]; /* LINTED */ _filetbl = (Rtc_file *) (CAST_PTRINT(char *, _dirtbl->cd_file) + addr); _id = --ent->e_dir->e_cnt; _filetbl[_id].cf_obj = CAST_PTRINT(Word, ((char *)objtbl - addr)); /* * Add this object to the hash table. */ hashval = ent->e_hash % hashbkts; hashchn[ndx] = hashbkt[hashval]; hashbkt[hashval] = ndx++; /* * Increment Rt_obj pointer (make sure pointer * falls on an 8-byte boundary). */ objtbl = (Rtc_obj *) S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword)); } } } /* * Add any library, or secure path definitions. */ if (crle->c_edlibpath) { head->ch_edlibpath = head->ch_str + (_strtbl - strtbl); (void) strcpy(_strtbl, crle->c_edlibpath); _strtbl += strlen((char *)crle->c_edlibpath) + 1; } else head->ch_edlibpath = 0; /* * a.out is no longer supported, but remains in the crle file * format */ head->ch_adlibpath = 0; if (crle->c_eslibpath) { head->ch_eslibpath = head->ch_str + (_strtbl - strtbl); (void) strcpy(_strtbl, crle->c_eslibpath); _strtbl += strlen((char *)crle->c_eslibpath) + 1; } else head->ch_eslibpath = 0; /* * a.out is no longer supported, but remains in the crle file * format */ head->ch_aslibpath = 0; /* * Add any environment variable entries. */ if (crle->c_envnum) { Env_desc *env; Aliste idx; for (APLIST_TRAVERSE(crle->c_env, idx, env)) { envtbl->env_str = head->ch_str + (_strtbl - strtbl); envtbl->env_flags = env->e_flags; (void) strcpy(_strtbl, env->e_str); _strtbl += env->e_totsz; envtbl++; } envtbl->env_str = 0; envtbl->env_flags = 0; } /* * Add any filter/filtee association entries. */ if (crle->c_fltrnum) { Flt_desc *flt; Aliste idx1; for (APLIST_TRAVERSE(crle->c_flt, idx1, flt)) { Hash_ent *flte; Aliste idx2; /* * Establish the filter name, and filtee string, as * offsets into the configuration files string table. * Establish the filtee as the offset into the filtee * table. */ fltrtbl->fr_filter = flt->f_fent->e_cobj->co_name; fltrtbl->fr_string = _strtbl - strtbl; (void) strcpy(_strtbl, flt->f_str); _strtbl += flt->f_strsz; fltrtbl->fr_filtee = (Word) ((uintptr_t)_fltetbl - (uintptr_t)fltetbl); for (APLIST_TRAVERSE(flt->f_filtee, idx2, flte)) { /* * Establish the filtee name as the offset into * the configuration files string table. */ _fltetbl->fe_filtee = flte->e_cobj->co_name; _fltetbl++; } _fltetbl->fe_filtee = 0; _fltetbl++, fltrtbl++; } fltrtbl->fr_filter = 0; fltrtbl->fr_filtee = 0; } /* * Flush everything out. */ (void) close(crle->c_tempfd); if (msync((void *)crle->c_tempaddr, crle->c_tempsize, MS_ASYNC) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC), crle->c_name, crle->c_tempname, strerror(err)); return (1); } return (0); } /* * Update a configuration file. If dldump()'ed images have been created then * the memory reservation of those images is added to the configuration file. * The temporary file is then moved into its final resting place. */ int updateconfig(Crle_desc * crle) { Rtc_head *head = (Rtc_head *)crle->c_tempheadaddr; if (crle->c_flags & CRLE_DUMP) { head->ch_cnflags &= ~RTC_HDR_IGNORE; if (msync((void *)crle->c_tempaddr, crle->c_tempsize, MS_ASYNC) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC), crle->c_name, crle->c_tempname, strerror(err)); return (1); } } /* * If an original configuration file exists, remove it. */ if (crle->c_flags & CRLE_EXISTS) (void) unlink(crle->c_confil); /* * Move the config file to its final resting place. If the two files * exist on the same filesystem a rename is sufficient. */ if (crle->c_flags & CRLE_DIFFDEV) { int fd; 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 (write(fd, (void *)crle->c_tempaddr, crle->c_tempsize) != crle->c_tempsize) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_WRITE), crle->c_name, crle->c_confil, strerror(err)); return (1); } (void) close(fd); (void) unlink(crle->c_tempname); } else (void) rename(crle->c_tempname, crle->c_confil); return (0); } /* * 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 #include #include #include #include #include #include #include #include #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(3C). * * -o dir defines the output directory for any dldump(3C) 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(3C), * 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(3C), * 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(3C)'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(3C)'ed. * * -l dir library search directory * * -s dir trusted (secure) directory */ /* * 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(3C), 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(3C) 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 */ crle.c_flags |= (CRLE_EDLIB | 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 */ crle.c_flags |= (CRLE_ESLIB | CRLE_CREAT); break; /* * Search path type, undocumented but left for compatibility. * Previously used to select between AOUT and ELF, now * anything other than ELF is an error. */ case 't': if (strcmp((const char *)optarg, MSG_ORIG(MSG_STR_ELF)) != 0) { (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(3C) 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(3C) images ourself make sure we * can access any directories. */ if (crle.c_flags & CRLE_DUMP) { Objdir *objdir = NULL; 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 */ 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 */ 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': 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(3C) 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); } # # 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 2008 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # @ _START_ # Message file for cmd/sgs/crle. @ MSG_ID_CRLE # Argument usage messages. @ MSG_ARG_USAGE "usage: %s [-64] [-a name] [-A name] [-c conf] \ [-e env] [-E env] [-f flags] [-g name] [-G name ] \ [-i name] [-I name] [-l dir] [-o dir ] [-s dir] \ [-u] [-v]\n\ \t[-64]\t\tprocess 64-bit objects\n\ \t[-a name]\tdefine an alternative object name\n\ \t[-A name]\tdefine an optional alternative object \ name\n\ \t[-c conf]\tspecify configuration file\n\ \t[-e env]\tdefine a replaceable environment \ variable\n\ \t[-E env]\tdefine a permanent environment variable\n\ \t[-f flags]\tspecify flags for -G and -I\n\ \t[-g name]\tdefine a group object name\n\ \t[-G name]\tdefine a group object name and create \ alternatives\n\ \t[-i name]\tdefine an individual object name\n\ \t[-I name]\tdefine an individual object name and \ create an\n\ \t\t\talternative\n\ \t[-l dir]\tdefine a default search directory\n\ \t[-o dir]\tuse directory for alternative objects\n\ \t[-s dir]\tdefine a trusted search directory\n\ \t[-u]\t\tupdate existing configuration file\n\ \t[-v]\t\tprovide verbose processing information\n\ \n\ \tNote: without the -u option, the information \ defined by the options\n\ \tto crle() result in the replacement of any existing \ configuration\n\ \tfile, or default search paths. With the -u option, \ the information\n\ \tdefined by the options to crle() is appended to any \ existing\n\ \tconfiguration file, or default search paths.\n\ \n\ \tSet the environment variable LD_NOCONFIG=yes to \ disable the processing\n\ \tof any configuration file information. \ See ld.so.1(1).\n" @ MSG_ARG_ILLEGAL "%s: option %s has illegal argument `%s'\n" @ MSG_ARG_MULT "%s: argument %s specified multiple times\n" @ MSG_ARG_TYPE "%s: unknown object type: %s\n" @ MSG_ARG_FLAGS "%s: unknown flags: %s\n" @ MSG_ARG_MODE "%s: RTLD_REL_EXEC cannot be used with multiple \ dynamic executables: %s: %s\n" @ MSG_ARG_ALT "%s: %s: cannot override original file with \ alternative; specify -o\n" @ MSG_ARG_UPDATE "%s: %s: version [%d] of configuration file provides \ limited update (-u) and command-line regeneration \ capabilities: best effort in effect\n" @ MSG_ARG_UPDATEVER "%s: %s: file version [%d] newer than crle() version \ [%d]. Unable to update\n" @ MSG_ARG_PRINTVER "%s: %s: file version [%d] newer than crle() version \ [%d]. Displayed information may be incomplete\n" @ MSG_ARG_CLASS "%s: %s: 32-bit file is incompatible with -64 \ option\n" @ MSG_ARG_WRONGARCH "%s: %s: %s %s file is incompatible with \ %s %s version of crle()\n" @ MSG_WARN_ENV "%s: warning: %.*s: environment variable \ multiply-defined, first definition taken\n" # ISA errors @ MSG_ISA32_NO64SUP "%s: This system lacks support for 64-bit \ configuration files\n" # System error messages @ MSG_SYS_MALLOC "%s: malloc failed: %s\n" @ MSG_SYS_TEMPNAME "%s: unable to create temporary filename: %s\n" @ MSG_SYS_PIPE "%s: pipe failed: %s\n" @ MSG_SYS_FORK "%s: fork failed: %s\n" @ MSG_SYS_PUTENV "%s: putenv failed: %s\n" @ MSG_COR_TRUNC "%s: %s: corrupt file, possible truncation\n" @ MSG_SYS_ACCESS "%s: %s: access failed: %s\n" @ MSG_SYS_OPEN "%s: %s: open failed: %s\n" @ MSG_SYS_MMAP "%s: %s: mmap failed: %s\n" @ MSG_SYS_TRUNC "%s: %s: ftruncate failed: %s\n" @ MSG_SYS_STAT "%s: %s: stat failed: %s\n" @ MSG_SYS_WRITE "%s: %s: write failed: %s\n" @ MSG_SYS_EXEC "%s: %s: exec failed: due to signal %d %s\n" @ MSG_SYS_EXECLP "%s: %s: exec failed: %s\n" @ MSG_SYS_CORE "(core dumped)" # Elf error messages @ MSG_ELF_TYPE "%s: %s: invalid ELF file\n" @ MSG_ELF_BEGIN "%s: %s: elf_begin: %s\n" # Generic error messages @ MSG_GEN_INVFILE "%s: %s: invalid file type\n" @ MSG_GEN_CREATE "%s: configuration file: %s: not created\n" # System defaults @ MSG_DEF_NOCONF "\nDefault configuration file (%s) not found\n" @ MSG_DEF_NEWDLP " Default Library Path (ELF):\t/lib:/usr/lib \ (system default)\n" @ MSG_DEF_NEWTD " Trusted Directories (ELF):\t\ /lib/secure:/usr/lib/secure (system default)\n" @ MSG_DEF_NEWDLP_64 " Default Library Path (ELF):\t/lib:/usr/lib \ (system default)\n" @ MSG_DEF_NEWTD_64 " Trusted Directories (ELF):\t\ /lib/secure:/usr/lib/secure (system default)\n" @ MSG_ENV_TITLE "\nEnvironment Variables:\n" @ MSG_ENV_RPL " %s (replaceable)\n" @ MSG_ENV_PRM " %s (permanent)\n" @ MSG_FLT_TITLE "\nFilters:\n" @ MSG_FLT_FILTER " %s - %s:\n" @ MSG_FLT_FILTEE " %s (filtee)\n" # Diagnostic strings @ MSG_DIA_CONFILE "output configuration file: %s\n" @ MSG_DIA_OBJDIR "output object directory: %s\n" @ MSG_DIA_INSPECT "inspect: %s\n" @ MSG_DIA_RPLENV " adding replaceable environment variable: %s\n" @ MSG_DIA_PRMENV " adding permanent environment variable: %s\n" @ MSG_DIA_DLIBPTH " adding default library path (%s): %s\n" @ MSG_DIA_TLIBPTH " adding trusted library path (%s): %s\n" @ MSG_DIA_NOEXIST " adding directory [id=%d]: %s (non-existent path)\n" @ MSG_DIA_DIR " adding directory [id=%d]: %s\n", @ MSG_DIA_FILE " adding file [id=%d]: %s\n" @ MSG_DIA_ALTCREATE " creating alternate: %s (for %s)\n" @ MSG_DIA_ALTUPDATE " updating alternate: %s (for %s)\n" # Dump strings @ MSG_DMP_HEAD "\nConfiguration file [version %d]: %s %s\n" @ MSG_DMP_PLATFORM " Platform:\t%s %s %s\n" @ MSG_DMP_APP " Specific application: %s (original: %s)\n" @ MSG_DMP_DLIBPTH " Default Library Path (%s):\t%s\n" @ MSG_DMP_TLIBPTH " Trusted Directories (%s):\t%s\n" @ MSG_DMP_RESV "\nMemory Reservation:\t%llx - %llx (%llx bytes)\n" @ MSG_DMP_DIR_1 "\nDirectory: %s\n" @ MSG_DMP_DIR_2 "\nDirectory: %s\t(non-existent)\n" @ MSG_DMP_FILE_1 " %s\n" @ MSG_DMP_FILE_2 " %s\t(alternate: %s)\n" @ MSG_DMP_HASH "Object Entries:\n Id\tBkt\tString\n" @ MSG_DMP_HASHENT_1 " %d\t[%d]\t%s\t%s\n" @ MSG_DMP_HASHENT_2 " %d\t\t%s\t%s\n" @ MSG_DMP_STAT "\nWARNING: original file %s: stat failed: %s\n\n" @ MSG_DMP_DCMP "\nWARNING: original directory %s: modified since \ configuration file creation\n\n" @ MSG_DMP_FCMP "\nWARNING: original file %s: modified since \ configuration file creation\n\n" @ MSG_DMP_CMD_TITLE "\nCommand line:\n" @ MSG_DMP_CMD_ONE " crle %s\n\n" @ MSG_DMP_CMD_BGN " crle %s " @ MSG_DMP_CMD_MID "%s " @ MSG_DMP_CMD_END "%s\n\n" @ MSG_DMP_CMD_ONE_V " crle %s\n\n" @ MSG_DMP_CMD_BGN_V " crle %s \\\n" @ MSG_DMP_CMD_MID_V " %s \\\n" @ MSG_DMP_CMD_END_V " %s\n\n" @ _END_ # The following strings represent reserved words, files, pathnames and symbols. # Reference to this strings is via the MSG_ORIG() macro, and thus no message # translation is required. @ MSG_ARG_OPTIONS "6:a:A:c:e:E:f:G:g:I:i:l:o:s:t:uv" @ MSG_ARG_6 "-6" @ MSG_ARG_C "-c" @ MSG_ARG_F "-f" @ MSG_CMD_64 "-64" @ MSG_CMD_ALTER "-a %s" @ MSG_CMD_OPTIONAL "-A %s" @ MSG_CMD_CONF "-c %s" @ MSG_CMD_RPLENV "-e %s" @ MSG_CMD_PRMENV "-E %s" @ MSG_CMD_FLAGS "-f %s" @ MSG_CMD_GRP "-g %s" @ MSG_CMD_DUMPGRP "-G %s" @ MSG_CMD_IND "-i %s" @ MSG_CMD_DUMPIND "-I %s" @ MSG_CMD_EDLIB "-l %s" @ MSG_CMD_OUTPUT "-o %s" @ MSG_CMD_ESLIB "-s %s" @ MSG_STR_ELF "ELF" @ MSG_STR_EMPTY "" @ MSG_STR_READ "r" @ MSG_STR_NL "\n" @ MSG_STR_COLON ":" @ MSG_AUD_PRF "__CRLE_DEP__:" @ MSG_AUD_DEPEND "depend:" @ MSG_AUD_FILTER "filter:" @ MSG_AUD_RESBGN "resbgn:" @ MSG_AUD_RESEND "resend:" @ MSG_ENV_AUD_FD "CRLE_FD=%d" @ MSG_ENV_AUD_FLAGS "CRLE_FLAGS=%d" @ MSG_ENV_LD_AUDIT "LD_AUDIT=libcrle.so.1" @ MSG_ENV_LD_PRELOAD "LD_PRELOAD=%s" @ MSG_ENV_LD_CONFIG "LD_CONFIG=%s" @ MSG_ENV_LD_FLAGS "LD_FLAGS=confgen,loadfltr" @ MSG_LDFLG_LOADAVAIL ",loadavail" @ MSG_PTH_CONFIG "/var/ld/ld.config" @ MSG_PTH_CONFIG_64 "/var/ld/ld.config" @ MSG_PTH_DOT "./" @ MSG_PTH_NEWDLP "/lib:/usr/lib" @ MSG_PTH_OLDDLP "/usr/lib" @ MSG_PTH_UPDLP "/lib" @ MSG_PTH_NEWTD "/lib/secure:/usr/lib/secure" @ MSG_PTH_OLDTD "/usr/lib/secure" @ MSG_PTH_UPTD "/lib/secure" @ MSG_PTH_NEWDLP_64 "/lib:/usr/lib" @ MSG_PTH_OLDDLP_64 "/usr/lib" @ MSG_PTH_UPDLP_64 "/lib" @ MSG_PTH_NEWTD_64 "/lib/secure:/usr/lib/secure" @ MSG_PTH_OLDTD_64 "/usr/lib/secure" @ MSG_PTH_UPTD_64 "/lib/secure" @ MSG_DIR_ROOT "/" @ MSG_DIR_DOT "." @ MSG_TMP_DIR "/var/tmp" @ MSG_TMP_PFX "crle." @ MSG_FMT_PATH "%s/%s" @ MSG_FMT_COLON ":%s" @ MSG_SUNW_OST_SGS "SUNW_OST_SGS" @ MSG_MOD_SEPARATE " \t|" @ MSG_MOD_REL_RELATIVE "RTLD_REL_RELATIVE" @ MSG_MOD_REL_EXEC "RTLD_REL_EXEC" @ MSG_MOD_REL_DEPENDS "RTLD_REL_DEPENDS" @ MSG_MOD_REL_PRELOAD "RTLD_REL_PRELOAD" @ MSG_MOD_REL_SELF "RTLD_REL_SELF" @ MSG_MOD_REL_WEAK "RTLD_REL_WEAK" @ MSG_MOD_REL_ALL "RTLD_REL_ALL" @ MSG_MOD_REL_MEMORY "RTLD_MEMORY" @ MSG_MOD_REL_STRIP "RTLD_STRIP" @ MSG_MOD_REL_NOHEAP "RTLD_NOHEAP" @ MSG_MOD_REL_CONFGEN "RTLD_CONFGEN" /* * 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 #include #include #include #include #include #include #include #include "machdep.h" #include "sgs.h" #include "conv.h" #include "_crle.h" #include "msg.h" /* * Establish an association between a filter and filtee. Both the filter and * filtee already exist in the internal hash table, since auditing registers * objects (la_objopen()) before it registers filters (la_objfilter()). */ static int filter(Crle_desc *crle, const char *filter, const char *str, const char *filtee) { Hash_ent *fltrent, *flteent; Flt_desc *flt; Aliste idx; /* * Locate the filter. Mark the underlying object as the filter to * reflect that no matter how it is referenced, it's a filter. */ if ((fltrent = get_hash(crle->c_strtbl, (Addr)filter, 0, HASH_FND_ENT)) == NULL) return (1); if ((fltrent = get_hash(crle->c_strtbl, (Addr)fltrent->e_obj->o_path, 0, HASH_FND_ENT)) == NULL) return (1); fltrent->e_obj->o_flags |= RTC_OBJ_FILTER; /* * Locate the filtee. Mark the referencing object as the filtee, as * this is the object referenced by the filter. */ if ((flteent = get_hash(crle->c_strtbl, (Addr)filtee, 0, HASH_FND_ENT)) == NULL) return (1); flteent->e_flags |= RTC_OBJ_FILTEE; /* * Traverse the filter list using the filters real name. If ld.so.1 * inspects the resulting configuration file for filters, it's the * objects real name that will be used (PATHNAME()). */ for (APLIST_TRAVERSE(crle->c_flt, idx, flt)) { /* * Determine whether this filter and filtee string pair already * exist. */ if ((strcmp(flt->f_fent->e_obj->o_path, fltrent->e_obj->o_path) != 0) && (strcmp(flt->f_str, str) != 0)) continue; /* * Add this filtee additional association. */ if (aplist_append(&(flt->f_filtee), flteent, AL_CNT_CRLE) == NULL) return (1); crle->c_fltenum++; return (0); } /* * This is a new filter descriptor. Add this new filtee association. */ if (((flt = malloc(sizeof (Flt_desc))) == NULL) || ((flt->f_strsz = strlen(str) + 1) == 0) || ((flt->f_str = malloc(flt->f_strsz)) == NULL)) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), crle->c_name, strerror(err)); free(flt); return (1); } if ((aplist_append(&(crle->c_flt), flt, AL_CNT_CRLE) == NULL) || (aplist_append(&(flt->f_filtee), flteent, AL_CNT_CRLE) == NULL)) return (1); flt->f_fent = fltrent; (void) memcpy((void *)flt->f_str, (void *)str, flt->f_strsz); crle->c_strsize += flt->f_strsz; crle->c_fltrnum += 1; crle->c_fltenum += 2; /* Account for null filtee desc. */ return (0); } /* * Establish the dependencies of an ELF object and add them to the internal * configuration information. This information is gathered by using libcrle.so.1 * as an audit library - this is akin to using ldd(1) only simpler. */ int depend(Crle_desc *crle, const char *name, Half flags, GElf_Ehdr *ehdr) { const char *exename; const char *preload; int fildes[2], pid; /* * If we're dealing with a dynamic executable we'll execute it, * otherwise we'll preload the shared object with one of the lddstub's. */ if (ehdr->e_type == ET_EXEC) { exename = name; preload = NULL; } else { exename = conv_lddstub(M_CLASS); preload = name; } /* * Set up a pipe through which the audit library will write the * dependencies. */ if (pipe(fildes) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_PIPE), crle->c_name, strerror(err)); return (1); } /* * Fork ourselves to run our executable and collect its dependencies. */ if ((pid = fork()) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_FORK), crle->c_name, strerror(err)); return (1); } if (pid) { /* * Parent. Read each dependency from the audit library. The read * side of the pipe is attached to stdio to make obtaining the * individual dependencies easier. */ int error = 0, status; FILE *fd; char buffer[PATH_MAX]; (void) close(fildes[1]); if ((fd = fdopen(fildes[0], MSG_ORIG(MSG_STR_READ))) != NULL) { char *str; while (fgets(buffer, PATH_MAX, fd) != NULL) { /* * Make sure we recognize the message, remove * the newline (which allowed fgets() use) and * register the name; */ if (strncmp(MSG_ORIG(MSG_AUD_PRF), buffer, MSG_AUD_PRF_SIZE)) continue; str = strrchr(buffer, '\n'); *str = '\0'; str = buffer + MSG_AUD_PRF_SIZE; if (strncmp(MSG_ORIG(MSG_AUD_DEPEND), str, MSG_AUD_DEPEND_SIZE) == 0) { /* * Process any dependencies. */ str += MSG_AUD_DEPEND_SIZE; if ((error = inspect(crle, str, (flags & ~RTC_OBJ_GROUP))) != 0) break; } else if (strncmp(MSG_ORIG(MSG_AUD_FILTER), str, MSG_AUD_FILTER_SIZE) == 0) { char *_flt, *_str; /* * Process any filters. */ _flt = str += MSG_AUD_FILTER_SIZE; _str = strchr(str, ':'); *_str++ = '\0'; str = _str++; str = strrchr(str, ')'); *str++ = '\0'; str++; if ((error = filter(crle, _flt, _str, str)) != 0) break; } } } else error = errno; while (wait(&status) != pid) ; if (status) { if (WIFSIGNALED(status)) { (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), crle->c_name, exename, (WSIGMASK & status), ((status & WCOREFLG) ? MSG_INTL(MSG_SYS_CORE) : MSG_ORIG(MSG_STR_EMPTY))); } error = status; } (void) fclose(fd); return (error); } else { char efds[MSG_ENV_AUD_FD_SIZE + 10]; char epld[PATH_MAX]; char eldf[PATH_MAX]; (void) close(fildes[0]); /* * Child. Set up environment variables to enable and identify * auditing. Initialize CRLE_FD and LD_FLAGS strings. */ (void) snprintf(efds, (MSG_ENV_AUD_FD_SIZE + 10), MSG_ORIG(MSG_ENV_AUD_FD), fildes[1]); (void) snprintf(eldf, PATH_MAX, MSG_ORIG(MSG_ENV_LD_FLAGS)); /* * If asked to dump a group of dependencies make sure any * lazily-loaded objects get processed - (append loadavail to * LD_FLAGS=confgen). */ if (flags & RTC_OBJ_GROUP) (void) strcat(eldf, MSG_ORIG(MSG_LDFLG_LOADAVAIL)); /* * Put LD_PRELOAD= in the environment if necessary. */ if (preload) { (void) snprintf(epld, PATH_MAX, MSG_ORIG(MSG_ENV_LD_PRELOAD), preload); } /* * Put strings in the environment for exec(). * NOTE, use of automatic variables for construction of the * environment variables is legitimate here, as they are local * to the child process and are established solely for exec(). */ if ((putenv(efds) != 0) || (putenv(crle->c_audit) != 0) || (putenv(eldf) != 0) || (preload && (putenv(epld) != 0))) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_PUTENV), crle->c_name, strerror(err)); return (1); } if (execlp(exename, exename, 0) == -1) { _exit(errno); /* NOTREACHED */ } } return (0); } /* * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include "machdep.h" #include "sgs.h" #include "rtc.h" #include "conv.h" #include "_crle.h" #include "msg.h" /* * Having gathered together any dependencies, dldump(3x) any necessary images. * * All dldump(3x) processing is carried out from the audit library. The * temporary configuration file is read and all alternative marked files are * dumped. If a -E application requires RTLD_REL_EXEC then that application * acts as the new process, otherwise lddstub is used. * * Besides dldump(3x)'ing any images the audit library returns the address * range of the images which will used to update the configuration file. */ int dump(Crle_desc * crle) { const char *orgapp = (const char *)crle->c_app; int fildes[2], pid; if (orgapp == 0) orgapp = conv_lddstub(M_CLASS); /* * Set up a pipe through which the audit library will write the image * address ranges. */ if (pipe(fildes) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_PIPE), crle->c_name, strerror(err)); return (1); } /* * Fork ourselves to run the application and collect its dependencies. */ if ((pid = fork()) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_FORK), crle->c_name, strerror(err)); return (1); } if (pid) { /* * Parent. Read memory range entries from the audit library. * The read side of the pipe is attached to stdio to make * obtaining the individual dependencies easier. */ int error = 0, status; FILE *fd; char buffer[PATH_MAX]; (void) close(fildes[1]); if ((fd = fdopen(fildes[0], MSG_ORIG(MSG_STR_READ))) != NULL) { char *str; Rtc_head *rtc = (Rtc_head *)crle->c_tempheadaddr; while (fgets(buffer, PATH_MAX, fd) != NULL) { /* * Make sure we recognize the message, remove * the newline (which allowed fgets() use) and * register the memory range entry; */ if (strncmp(MSG_ORIG(MSG_AUD_PRF), buffer, MSG_AUD_PRF_SIZE)) continue; str = strrchr(buffer, '\n'); *str = '\0'; str = buffer + MSG_AUD_PRF_SIZE; if (strncmp(MSG_ORIG(MSG_AUD_RESBGN), str, MSG_AUD_RESBGN_SIZE) == 0) { rtc->ch_resbgn = strtoull(str + MSG_AUD_RESBGN_SIZE, (char **)NULL, 0); } else if (strncmp(MSG_ORIG(MSG_AUD_RESEND), str, MSG_AUD_RESEND_SIZE) == 0) { rtc->ch_resend = strtoull(str + MSG_AUD_RESEND_SIZE, (char **)NULL, 0); } else { continue; } } (void) fclose(fd); } else error = errno; while (wait(&status) != pid) ; if (status) { if (WIFSIGNALED(status)) { (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXEC), crle->c_name, orgapp, (WSIGMASK & status), ((status & WCOREFLG) ? MSG_INTL(MSG_SYS_CORE) : MSG_ORIG(MSG_STR_EMPTY))); } return (status); } return (error); } else { char efds[MSG_ENV_AUD_FD_SIZE + 10]; char eflg[MSG_ENV_AUD_FLAGS_SIZE + 10]; char ecnf[PATH_MAX]; (void) close(fildes[0]); /* * Child. Set up environment variables to enable and identify * auditing. */ (void) snprintf(efds, (MSG_ENV_AUD_FD_SIZE + 10), MSG_ORIG(MSG_ENV_AUD_FD), fildes[1]); (void) snprintf(eflg, (MSG_ENV_AUD_FLAGS_SIZE + 10), MSG_ORIG(MSG_ENV_AUD_FLAGS), crle->c_dlflags); (void) snprintf(ecnf, PATH_MAX, MSG_ORIG(MSG_ENV_LD_CONFIG), crle->c_tempname); /* * Put strings in the environment for exec(). * NOTE, use of automatic variables for construction of the * environment variables is legitimate here, as they are local * to the child process and are established solely for exec(). */ if ((putenv(efds) != 0) || (putenv(eflg) != 0) || (putenv(ecnf) != 0) || (putenv(crle->c_audit) != 0) || (putenv((char *)MSG_ORIG(MSG_ENV_LD_FLAGS)) != 0)) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_PUTENV), crle->c_name, strerror(err)); return (1); } if (execlp(orgapp, orgapp, 0) == -1) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_EXECLP), crle->c_name, orgapp, strerror(err)); _exit(err); /* NOTREACHED */ } } return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 (c) 2000 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #include #include #include "_crle.h" Hash_tbl * make_hash(int size, Hash_type type, ulong_t ident) { Hash_tbl * tbl; if ((tbl = malloc(sizeof (Hash_tbl))) == 0) return (0); tbl->t_entry = calloc(size, (unsigned)(sizeof (Hash_ent *))); if (tbl->t_entry == NULL) { free(tbl); return (0); } tbl->t_ident = ident; tbl->t_type = type; tbl->t_size = size; return (tbl); } Hash_ent * get_hash(Hash_tbl * tbl, Addr key, Half id, int mode) { int bucket; Hash_ent * ent; Word hashval; if (tbl->t_type == HASH_STR) hashval = elf_hash((const char *)key); else hashval = key; bucket = hashval % tbl->t_size; if (mode & HASH_FND_ENT) { for (ent = tbl->t_entry[bucket]; ent != NULL; ent = ent->e_next) { if (tbl->t_type == HASH_STR) { if ((strcmp((const char *)ent->e_key, (const char *)key) == 0) && ((id == 0) || (id == ent->e_id))) return (ent); } else { if (ent->e_key == key) return (ent); } } } if (!(mode & HASH_ADD_ENT)) return (0); /* * Key not found in this hash table ... insert new entry into bucket. */ if ((ent = calloc(1, sizeof (Hash_ent))) == NULL) return (0); ent->e_key = key; ent->e_hash = hashval; /* * Hook into bucket chain */ ent->e_next = tbl->t_entry[bucket]; tbl->t_entry[bucket] = ent; return (ent); } /* * 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. */ /* * Routines to add file and directory entries into the internal configuration * information. This information is maintained in a number of hash tables which * after completion of input file processing will be processed and written to * the output configuration file. * * Each hash table is defined via a Hash_tbl structure. These are organized: * * c_strtbl contains a hash entry for every file, directory, pathname and * alternative path (dldump(3C) image) processed. * c_strsize and c_objnum maintain the size and count of the * strings added to this table and are used to size the output * configuration file. * * c_inotbls contains a list of inode hash tables. Each element of the list * identifies a unique device. Thus, for each file processed its * st_dev and st_ino are used to assign its entry to the correct * hash table. * * Each directory processed is assigned a unique id (c_dirnum) * which insures each file also becomes uniquely identified. * * All file and directory additions come through the inspect() entry point. */ #include #include #include #include #include <_libelf.h> #include #include #include #include #include #include "machdep.h" #include "sgs.h" #include "rtc.h" #include "_crle.h" #include "msg.h" /* * Add an alternative pathname for an object. Although a configuration file * may contain several pathnames that resolve to the same real file, there can * only be one real file. Consequently, there can only be one alternative. * For multiple pathnames that resolve to the same real file, multiple alter- * natives may be specified. Always take the alternative for the real file * over any others. */ static int enteralt(Crle_desc *crle, const char *path, const char *file, Half flags, Hash_obj *obj) { const char *fmt; char alter[PATH_MAX]; size_t altsz; if (obj->o_alter) { /* * If an alternative has already been captured, only override * it if the specified file is the real file. */ if (strcmp(path, obj->o_path)) return (1); } /* * Create an alternative pathname from the file and object destination * directory. If we're dumping an alternative don't allow it to * override the original. */ if (flags & RTC_OBJ_DUMP) { char _alter[PATH_MAX]; (void) strlcpy(alter, crle->c_objdir, sizeof (alter)); (void) realpath(alter, _alter); (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), _alter, file); if (strcmp(alter, obj->o_path) == 0) { (void) printf(MSG_INTL(MSG_ARG_ALT), crle->c_name, obj->o_path); return (0); } obj->o_flags |= RTC_OBJ_DUMP; } else { (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), crle->c_objdir, file); } obj->o_flags |= RTC_OBJ_ALTER; /* * If we're overriding an existing alternative with the real path, free * up any previous alternative. */ if (obj->o_alter) { crle->c_strsize -= strlen(alter) + 1; fmt = MSG_INTL(MSG_DIA_ALTUPDATE); } else { fmt = MSG_INTL(MSG_DIA_ALTCREATE); } /* * Allocate the new alternative and update the string table size. */ altsz = strlen(alter) + 1; if ((obj->o_alter = malloc(altsz)) == NULL) return (0); (void) strcpy(obj->o_alter, alter); crle->c_strsize += altsz; if (crle->c_flags & CRLE_VERBOSE) (void) printf(fmt, alter, obj->o_path); return (1); } /* * Establish an inode hash entry, this is unique for each dev hash table, and * establishes the unique object descriptor. */ static Hash_ent * enterino(Crle_desc *crle, const char *name, struct stat *status, Half flags) { Hash_ent *ent; Hash_obj *obj; Hash_tbl *tbl; Aliste idx; Addr ino = (Addr)status->st_ino; ulong_t dev = status->st_dev; Lword info; int found = 0; /* * For configuration file verification we retain information about the * file or directory. */ if (flags & RTC_OBJ_DIRENT) info = (Lword)status->st_mtime; else info = (Lword)status->st_size; /* * Determine the objects device number and establish a hash table for * for this devices inodes. */ for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) { if (tbl->t_ident == dev) { found = 1; break; } } if (found == 0) { if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == NULL) return (NULL); if (aplist_append(&crle->c_inotbls, tbl, AL_CNT_CRLE) == NULL) return (NULL); } /* * Reuse or add this new object to the inode hash table. */ if ((ent = get_hash(tbl, ino, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); /* * If an object descriptor doesn't yet exist create one. */ if ((obj = ent->e_obj) == NULL) { if ((obj = calloc(1, sizeof (Hash_obj))) == NULL) return (NULL); obj->o_tbl = tbl; obj->o_flags = flags; obj->o_info = info; /* * Reallocate the objects name, as it might have been composed * and passed to us on the stack. */ if ((obj->o_path = strdup(name)) == NULL) return (NULL); /* * Assign this object to the original ino hash entry. */ ent->e_obj = obj; } return (ent); } /* * Basic directory entry, establishes entry information, updated global counts * and provides any diagnostics. */ static int _enterdir(Crle_desc *crle, const char *dir, Hash_ent *ent, Hash_obj *obj) { size_t size = strlen(dir) + 1; char *ndir; /* * Establish this hash entries key (which is the directory name itself), * assign the next available directory number, and its object. */ if ((ndir = malloc(size)) == NULL) return (0); (void) strcpy(ndir, dir); ent->e_key = (Addr)ndir; ent->e_id = crle->c_dirnum++; ent->e_obj = obj; /* * Update string table information. We add a dummy filename for each * real directory so as to have a null terminated file table array for * this directory. */ crle->c_strsize += size; crle->c_hashstrnum++; crle->c_filenum++; /* * Provide any diagnostics. */ if (crle->c_flags & CRLE_VERBOSE) { const char *fmt; if (obj->o_flags & RTC_OBJ_NOEXIST) fmt = MSG_INTL(MSG_DIA_NOEXIST); else fmt = MSG_INTL(MSG_DIA_DIR); (void) printf(fmt, ent->e_id, dir); } return (1); } /* * Establish a string hash entry for a directory. */ static Hash_ent * enterdir(Crle_desc *crle, const char *odir, Half flags, struct stat *status) { Hash_tbl *stbl = crle->c_strtbl; Hash_ent *ent; Hash_obj *obj; char rdir[PATH_MAX], *ndir; /* * Establish the directories real name, this is the name that will be * recorded in the object identifier. */ if (realpath(odir, rdir) == NULL) return (NULL); if (strcmp(odir, rdir)) ndir = rdir; else ndir = (char *)odir; /* * If we're not dealing with an all-entries directory (i.e., we're * recording this directory because of its explicitly specified * filename) leave off any filename specific attributes. */ if ((flags & RTC_OBJ_ALLENTS) == 0) flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP); flags |= RTC_OBJ_DIRENT; /* * Establish a inode table entry, and the objects unique descriptor. */ if ((ent = enterino(crle, ndir, status, flags)) == NULL) return (NULL); obj = ent->e_obj; /* * Create a string table entry for the real directory. */ if ((ent = get_hash(stbl, (Addr)ndir, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); /* * If this is a new entry reassign the directory name and assign a * unique directory id. */ if (ent->e_id == 0) { if (_enterdir(crle, ndir, ent, obj) == 0) return (NULL); } /* * If the directory name supplied is different than the real name we've * just entered, continue to create an entry for it. */ if (ndir == odir) return (ent); /* * Create a string table entry for this real directory. */ if ((ent = get_hash(stbl, (Addr)odir, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); /* * If this is a new entry reassign the directory name and assign a * unique directory id. */ if (ent->e_id == 0) { if (_enterdir(crle, odir, ent, obj) == 0) return (NULL); } return (ent); } /* * Establish a non-existent directory entry. There is no inode entry created * for this, just a directory and its associated object. */ static Hash_ent * enternoexistdir(Crle_desc *crle, const char *dir) { Hash_ent *ent; /* * Reuse or add this new non-existent directory to the string table. */ if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); /* * If this is a new entry, assign both the object and the directory * entry information. */ if (ent->e_id == 0) { Hash_obj * obj; if ((obj = calloc(1, sizeof (Hash_obj))) == NULL) return (NULL); obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT); if (_enterdir(crle, dir, ent, obj) == 0) return (NULL); } return (ent); } /* * Basic file entry, establishes entry information, updated global counts * and provides any diagnostics. */ static int _enterfile(Crle_desc *crle, const char *file, int off, Hash_ent *fent, Hash_ent *rent, Hash_ent *dent, Hash_obj *obj) { size_t size = strlen(file) + 1; char *nfile; /* * If this is a full file name reallocate it, as it might have been * composed and passed to us on the stack. Otherwise reuse the original * directory name to satisfy the filename, here we record the offset of * the file in the directory name so that we can reduce the string table * in the final configuration file. */ if (off == 0) { if ((nfile = malloc(size)) == NULL) return (0); (void) strcpy(nfile, file); } else { nfile = (char *)file; } fent->e_key = (Addr)nfile; fent->e_off = off; /* * Assign directory and directory id, and any real (full) path * association. */ fent->e_dir = dent; fent->e_id = dent->e_id; fent->e_path = rent; /* * Increment the file count for this directory. */ dent->e_cnt++; /* * Assign this object to the new string hash entry. */ fent->e_obj = obj; /* * Update string table information. */ crle->c_strsize += size; crle->c_hashstrnum++; crle->c_filenum++; /* * Provide any diagnostics. */ if (crle->c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_FILE), fent->e_id, nfile); return (1); } /* * Establish a non-existent file entry. There is no inode entry created for * this, just the files full and simple name, and its associated object. */ static Hash_ent * enternoexistfile(Crle_desc *crle, const char *path, const char *file, Hash_ent *dent) { Hash_ent *rent, *ent; Hash_obj *obj; int off; /* * Create a string table entry for the full filename. */ if ((rent = get_hash(crle->c_strtbl, (Addr)path, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); /* * If this is a new entry, assign both the object and the full filename * entry information. */ if (rent->e_id == 0) { if ((obj = calloc(1, sizeof (Hash_obj))) == NULL) return (NULL); obj->o_flags = RTC_OBJ_NOEXIST; if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0) return (NULL); } obj = rent->e_obj; if ((obj->o_path = strdup(path)) == NULL) return (NULL); /* * Express the filename in terms of the full pathname. By reusing the * name within the full filename we can reduce the overall string table * size in the output configuration file. */ off = file - path; file = (char *)rent->e_key + off; /* * Create a entry for the individual file within this directory. */ if ((ent = get_hash(crle->c_strtbl, (Addr)file, dent->e_id, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); if (ent->e_id == 0) { if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0) return (NULL); } return (ent); } /* * Establish a string hash entry for a file. */ static Hash_ent * enterfile(Crle_desc *crle, const char *opath, const char *ofile, Half flags, Hash_ent *odent, struct stat *status) { Hash_tbl *stbl = crle->c_strtbl; Hash_ent *ent, *rent, *ndent = odent; Hash_obj *obj; size_t size; char rpath[PATH_MAX], *npath, *nfile; int off; /* * Establish the files real name, this is the name that will be * recorded in the object identifier. */ if (realpath(opath, rpath) == NULL) return (NULL); if (strcmp(opath, rpath)) { npath = rpath; nfile = strrchr(npath, '/'); if (nfile != NULL) nfile++; else nfile = npath; /* * Determine if the real pathname has a different directory to * the original passed to us. */ size = nfile - npath; if (strncmp(opath, npath, size)) { char _npath[PATH_MAX]; struct stat _status; (void) strncpy(_npath, npath, size); _npath[size - 1] = '\0'; (void) stat(_npath, &_status); if ((ndent = enterdir(crle, _npath, flags, &_status)) == NULL) return (NULL); } } else { npath = (char *)opath; nfile = (char *)ofile; } /* * Establish an inode table entry, and the objects unique descriptor. */ if ((ent = enterino(crle, npath, status, flags)) == NULL) return (NULL); obj = ent->e_obj; /* * Create a string table entry for the full filename. */ if ((rent = get_hash(stbl, (Addr)npath, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); if (rent->e_id == 0) { if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0) return (NULL); } /* * Identify this entry and its directory as real paths. If dldump(3C) * processing is required this flag is checked, as we only need to dump * the real pathname. Many other objects may point to the same * alternative, but only one needs to be dumped. In addition, during * ld.so.1 validation, only this directory and file need be checked. */ rent->e_flags |= RTC_OBJ_REALPTH; ndent->e_flags |= RTC_OBJ_REALPTH; /* * Express the filename in terms of the full pathname. By reusing the * name within the full filename we can reduce the overall string table * size in the output configuration file. */ off = nfile - npath; nfile = (char *)rent->e_key + off; /* * Create a entry for the individual file within this directory. */ if ((ent = get_hash(stbl, (Addr)nfile, ndent->e_id, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); if (ent->e_id == 0) { if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0) return (NULL); } /* * If the original path name is not equivalent to the real path name, * then we had an alias (typically it's a symlink). Add the path name * to the string hash table and reference the object data structure. */ if (nfile == ofile) return (ent); /* * Establish an inode table entry, and the objects unique descriptor. */ if ((ent = enterino(crle, opath, status, 0)) == NULL) return (NULL); obj = ent->e_obj; /* * Create a string table entry for the full filename. */ if ((rent = get_hash(stbl, (Addr)opath, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); if (rent->e_id == 0) { if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0) return (NULL); } /* * Express the filename in terms of the full pathname. By reusing the * name within the full filename we can reduce the overall string table * size in the output configuration file. */ off = ofile - opath; ofile = (char *)rent->e_key + off; /* * Create a entry for the individual file within this directory. */ if ((ent = get_hash(stbl, (Addr)ofile, odent->e_id, (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) return (NULL); if (ent->e_id == 0) { if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0) return (NULL); } return (ent); } /* * Add a file to configuration information. */ static int inspect_file(Crle_desc *crle, const char *path, const char *file, Half flags, Hash_ent *dent, struct stat *status, int error) { Hash_ent *ent; Hash_obj *obj; int fd; Elf *elf; GElf_Ehdr ehdr; GElf_Xword dyflags = 0; Aliste idx; Hash_tbl *tbl; Addr ino = (Addr)status->st_ino; /* * Determine whether this file (inode) has already been processed. */ for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) { if (tbl->t_ident != status->st_dev) continue; if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == NULL) break; /* * This files inode object does exist, make sure it has a file * entry for this directory. */ if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) return (error); obj = ent->e_obj; /* * If an alternative has been asked for, and one has not yet * been established, create one. */ if ((flags & RTC_OBJ_ALTER) && ((obj->o_flags & RTC_OBJ_NOALTER) == 0)) { if (enteralt(crle, path, file, flags, obj) == 0) return (error); } return (0); } /* * This is a new file, determine if it's a valid ELF file. */ if ((fd = open(path, O_RDONLY, 0)) == -1) { if (error) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), crle->c_name, path, strerror(err)); } return (error); } /* * Obtain an ELF descriptor and determine if we have a shared object. */ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { if (error) (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), crle->c_name, path, elf_errmsg(-1)); (void) close(fd); return (error); } if ((elf_kind(elf) != ELF_K_ELF) || (gelf_getehdr(elf, &ehdr) == NULL) || (!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) || (!((ehdr.e_ident[EI_CLASS] == M_CLASS) || (ehdr.e_machine == M_MACH)))) { if (error) (void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE), crle->c_name, path); (void) close(fd); (void) elf_end(elf); return (error); } (void) close(fd); /* * If we're generating alternative objects find this objects DT_FLAGS * to insure it isn't marked as non-dumpable (libdl.so.1 falls into * this category). */ if (flags & RTC_OBJ_DUMP) dyflags = _gelf_getdyndtflags_1(elf); /* * Dynamic executables can be examined to determine their dependencies, * dldump(3C) their dependencies, and may even be dldump(3C)'ed * themselves. * * If we come across an executable while searching a directory * (error == 0) it is ignored. */ if (ehdr.e_type == ET_EXEC) { if (error == 0) { (void) elf_end(elf); return (0); } /* * If we're not dumping the application itself, or we've not * asked to gather its dependencies then its rather useless. */ if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) { (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name, path); (void) elf_end(elf); return (error); } /* * If we're dumping the application under RTLD_REL_EXEC then the * configuration file becomes specific to this application, so * make sure we haven't been here before. */ if (crle->c_app && (flags & RTC_OBJ_DUMP) && (crle->c_dlflags & RTLD_REL_EXEC)) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE), crle->c_name, crle->c_app, path); (void) elf_end(elf); return (error); } } /* * Enter the file in the string hash table. */ if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) { (void) elf_end(elf); return (error); } obj = ent->e_obj; if (flags & RTC_OBJ_ALTER) { /* * If this object is marked as non-dumpable make sure we don't * create a dldump(3C) alternative. A user requested * alternative is acceptable. */ if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) { obj->o_flags |= RTC_OBJ_NOALTER; obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP); } else { if (enteralt(crle, path, file, flags, obj) == 0) { (void) elf_end(elf); return (error); } } } /* * Executables are recorded in the configuration file either to allow * for the configuration files update, or may indicate that the * configuration file is specific to their use. */ if (ehdr.e_type == ET_EXEC) { obj->o_flags |= RTC_OBJ_EXEC; if ((flags & RTC_OBJ_DUMP) && (crle->c_dlflags & RTLD_REL_EXEC)) { /* * Get the reallocated pathname rather than using the * original (the original might be from an existing * configuration file being updated, in which case the * pointer will be unmapped before we get to use it). */ ent = get_hash(crle->c_strtbl, (Addr)path, 0, HASH_FND_ENT); obj->o_flags |= RTC_OBJ_APP; crle->c_app = (char *)ent->e_key; } } /* * If we've been asked to process this object as a group determine its * dependencies. */ if (flags & RTC_OBJ_GROUP) { if (depend(crle, path, flags, &ehdr)) { (void) elf_end(elf); return (error); } } (void) elf_end(elf); return (0); } /* * Add a directory to configuration information. */ static int inspect_dir(Crle_desc *crle, const char *name, Half flags, struct stat *status) { Hash_tbl *stbl = crle->c_strtbl; DIR *dir; struct dirent *dirent; Hash_ent *ent; int error = 0; struct stat _status; char path[PATH_MAX], * dst; const char *src; /* * Determine whether we've already visited this directory to process * all its entries. */ if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != NULL) { if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS) return (0); } else { /* * Create a directory hash entry. */ if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS), status)) == NULL) return (1); } ent->e_obj->o_flags |= RTC_OBJ_ALLENTS; /* * Establish the pathname buffer. */ for (dst = path, dst--, src = name; *src; src++) *++dst = *src; if (*dst++ != '/') *dst++ = '/'; /* * Access the directory in preparation for reading its entries. */ if ((dir = opendir(name)) == NULL) return (1); /* * Read each entry from the directory looking for ELF files. */ while ((dirent = readdir(dir)) != NULL) { const char *file = dirent->d_name; char *_dst; /* * Ignore "." and ".." entries. */ if ((file[0] == '.') && ((file[1] == '\0') || ((file[1] == '.') && (file[2] == '\0')))) continue; /* * Complete full pathname, and reassign file to the new path. */ for (_dst = dst, src = file, file = dst; *src; _dst++, src++) *_dst = *src; *_dst = '\0'; if (stat(path, &_status) == -1) continue; if ((_status.st_mode & S_IFMT) != S_IFREG) continue; if (inspect_file(crle, path, file, flags, ent, &_status, 0)) { error = 1; break; } } return (error); } /* * Inspect a file/dir name. A stat(name) results in the following actions: * * The name doesn't exist: * The name is assummed to be a non-existent directory and a directory * cache entry is created to indicate this. * * The name is a directory: * The directory is searched for appropriate files. * * The name is a file: * The file is processed and added to the cache if appropriate. */ int inspect(Crle_desc *crle, const char *name, Half flags) { Hash_ent *ent; const char *file, *dir; struct stat status; char _name[PATH_MAX], _dir[PATH_MAX]; Half nflags = flags & ~RTC_OBJ_CMDLINE; int noexist; /* * If this is the first time through here establish a string table * cache. */ if (crle->c_dirnum == 0) { if ((crle->c_strtbl = make_hash(crle->c_strbkts, HASH_STR, 0)) == NULL) return (1); crle->c_dirnum = 1; } if (crle->c_flags & CRLE_VERBOSE) (void) printf(MSG_INTL(MSG_DIA_INSPECT), name); /* * Determine whether the name exists. */ if ((noexist = stat(name, &status)) != 0) { if (errno != ENOENT) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), crle->c_name, name, strerror(err)); return (1); } else { /* * If we've been asked to create an alternative object * assume the object is a file and create a valid * alternative entry. This allows the creation of * alternatives for files that might not yet be * installed. * * Otherwise we have no idea whether the name specified * is a file or directory, so we assume a directory and * establish an object descriptor to mark this as * non-existent. This allows us to mark things like * platform specific directories as non-existent. */ if ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) != RTC_OBJ_ALTER) { if ((ent = enternoexistdir(crle, name)) == NULL) return (1); ent->e_flags |= flags; return (0); } } } /* * Determine whether we're dealing with a directory or a file. */ if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) { /* * Process the directory name to collect its shared objects into * the configuration file. */ if (inspect_dir(crle, name, nflags, &status)) return (1); ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT); ent->e_flags |= flags; return (0); } /* * If this isn't a regular file we might as well bail now. Note that * even if it is, we might still reject the file if it's not ELF later * in inspect_file(). */ if ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) { (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name, name); return (1); } /* * Break the pathname into directory and filename components. */ if ((file = strrchr(name, '/')) == NULL) { dir = MSG_ORIG(MSG_DIR_DOT); (void) strcpy(_name, MSG_ORIG(MSG_PTH_DOT)); (void) strcpy(&_name[MSG_PTH_DOT_SIZE], name); name = (const char *)_name; file = (const char *)&_name[MSG_PTH_DOT_SIZE]; } else { size_t off = file - name; if (file == name) { dir = MSG_ORIG(MSG_DIR_ROOT); } else { (void) strncpy(_dir, name, off); _dir[off] = '\0'; dir = (const char *)_dir; } file++; } /* * Determine whether we've already visited this directory and if not * create it. */ if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0, HASH_FND_ENT)) == NULL) { struct stat _status; if (stat(dir, &_status) != 0) { if (errno != ENOENT) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), crle->c_name, name, strerror(err)); return (1); } else { /* * Note that this directory will be tagged as * having an alternative - not that the * directory does, but supposedly it contains * a file that does. */ if ((ent = enternoexistdir(crle, dir)) == NULL) return (1); ent->e_flags |= nflags; } } else { if ((ent = enterdir(crle, dir, nflags, &_status)) == NULL) return (1); } } /* * Regardless of whether we've already processed this file (say from * an RTC_OBJ_ALLENTS which we could determine from the above), continue * to inspect the file. It may require alternatives or something that * hadn't be specified from the directory entry. */ if (noexist) { if ((ent = enternoexistfile(crle, name, file, ent)) == NULL) return (1); ent->e_flags |= nflags; if (enteralt(crle, name, file, flags, ent->e_obj) == 0) return (1); } else { if (inspect_file(crle, name, file, nflags, ent, &status, 1)) return (1); } /* * Make sure to propagate any RTC_OBJ_CMDLINE flag. */ ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT); if (ent != NULL) ent->e_flags |= (flags & RTC_OBJ_CMDLINE); return (0); } /* * 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 (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include "sgs.h" #include "rtc.h" #include "conv.h" #include "_crle.h" #include "msg.h" /* * Display the command line required to regenerate the configuration file. * * Under normal mode the command is printed on one line to make it more * available for grep(1) use. Under verbose mode the command is separated * into each argument (a little more readable perhaps when the arguments are * numerous of have long pathnames). * * Note that for version 1 configuration files we never used to generate any * command-line information, and as the attempt to do so is only a best effort * don't bother printing anything. */ static void printcmd(Crle_desc *crle, Rtc_head * head, APlist *cmdline) { Aliste idx, lidx; const char *fmto, *fmtb, *fmtm, *fmte; char *cmd; int output = 0; if (crle->c_flags & CRLE_VERBOSE) { fmto = MSG_INTL(MSG_DMP_CMD_ONE_V); fmtb = MSG_INTL(MSG_DMP_CMD_BGN_V); fmtm = MSG_INTL(MSG_DMP_CMD_MID_V); fmte = MSG_INTL(MSG_DMP_CMD_END_V); } else if (head->ch_version > RTC_VER_ONE) { fmto = MSG_INTL(MSG_DMP_CMD_ONE); fmtb = MSG_INTL(MSG_DMP_CMD_BGN); fmtm = MSG_INTL(MSG_DMP_CMD_MID); fmte = MSG_INTL(MSG_DMP_CMD_END); } else { (void) printf(MSG_ORIG(MSG_STR_NL)); return; } (void) printf(MSG_INTL(MSG_DMP_CMD_TITLE)); lidx = aplist_nitems(cmdline) - 1; for (APLIST_TRAVERSE(cmdline, idx, cmd)) { if (output++ == 0) { if (idx < lidx) (void) printf(fmtb, cmd); else (void) printf(fmto, cmd); } else { if (idx < lidx) (void) printf(fmtm, cmd); else (void) printf(fmte, cmd); } } } /* * Establish the argument required to generate the associated object. */ static const char * getformat(Half flags) { if (flags & RTC_OBJ_ALTER) { if (flags & RTC_OBJ_DUMP) { if (flags & RTC_OBJ_GROUP) return (MSG_ORIG(MSG_CMD_DUMPGRP)); else return (MSG_ORIG(MSG_CMD_DUMPIND)); } else { if (flags & RTC_OBJ_OPTINAL) return (MSG_ORIG(MSG_CMD_OPTIONAL)); else return (MSG_ORIG(MSG_CMD_ALTER)); } } else { if (flags & RTC_OBJ_GROUP) return (MSG_ORIG(MSG_CMD_GRP)); else return (MSG_ORIG(MSG_CMD_IND)); } } /* * Fabricate a system default search path. If an update is requested, and * new search paths are specified while no configuration file exists, or if a * configuration file does exist but doesn't specify this particular search * path, create any system defaults. The intent is to allow * "crle -u -l/usr/local/lib" and have this append the search path to the * system default, rather than have the user have to determine and specify * this default themselves. */ static int fablib(Crle_desc * crle, int flag) { const char *path; char **list; switch (flag) { case CRLE_EDLIB: #if M_CLASS == ELFCLASS64 path = MSG_ORIG(MSG_PTH_NEWDLP_64); #else path = MSG_ORIG(MSG_PTH_NEWDLP); #endif list = &crle->c_edlibpath; break; case CRLE_ESLIB: #if M_CLASS == ELFCLASS64 path = MSG_ORIG(MSG_PTH_NEWTD_64); #else path = MSG_ORIG(MSG_PTH_NEWTD); #endif list = &crle->c_eslibpath; break; default: return (1); } return (addlib(crle, list, path)); } /* * Establish the flags required to generate the associated object. Actually * the flags are already part of the object being inspected from the present * configuration file, but instead of using them all, which can cause some * unsuspected propagation down the inspect() family, only use those flags that * would have been contributed from crle()'s calls to inspect. */ static Half getflags(Half flags) { flags &= (RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP | RTC_OBJ_OPTINAL); return (flags | RTC_OBJ_CMDLINE); } /* * Dump a configuration files information. This routine is very close to the * scanconfig() in libcrle. */ /*ARGSUSED2*/ static INSCFG_RET scanconfig(Crle_desc * crle, Addr addr, int c_class) { Conv_inv_buf_t inv_buf1, inv_buf2, inv_buf3, inv_buf4; Conv_dl_flag_buf_t dl_flag_buf; Rtc_id *id; Rtc_head *head; Rtc_dir *dirtbl; Rtc_file *filetbl; Rtc_obj *objtbl, *obj; Word *hash, *chain; const char *strtbl; int ndx, bkts; APlist *cmdline = NULL; boolean_t cmdset = B_FALSE; char _cmd[PATH_MAX], *cmd; char _objdir[PATH_MAX], *objdir = NULL; /* * If there is an Rtc_id present, the Rtc_head follows it. * Otherwise, it is at the top. */ if (RTC_ID_TEST(addr)) { id = (Rtc_id *) addr; addr += sizeof (*id); /* Rtc_head follows */ } else { id = NULL; /* * When updating an existing config file that is lacking * the Rtc_id block, don't put one into the resulting file. */ crle->c_flags &= ~CRLE_ADDID; } head = (Rtc_head *) addr; /* * The rest of the configuration file can only be examined by * a program of the same ELFCLASS, byte order, and hardware * architecture as the one that created it. */ #ifdef _ELF64 /* 64-bit program with an existing 32-bit file? Abort. */ if (!(head->ch_cnflags & RTC_HDR_64)) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS), crle->c_name, crle->c_confil); return (INSCFG_RET_FAIL); } #else /* 32-bit program with an existing 64-bit file? Restart. */ if (head->ch_cnflags & RTC_HDR_64) return (INSCFG_RET_NEED64); /* * 32-bit program with an existing 32-bit file, but the * user specified the -64 option? Abort */ if (c_class != ELFCLASS32) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS), crle->c_name, crle->c_confil); return (INSCFG_RET_FAIL); } #endif /* * Now that the ELFCLASS has been settled, ensure that the * byte order and hardware match. Unlike ELFCLASS, where restarting * the other version is an option, we cannot work around a mismatch * of these attributes. */ if (id) { /* Rtc_id is present */ /* * Was the file produced by compatible hardware? * ELFCLASS doesn't matter here, because we can * adjust for that, but byte order and machine type do. */ if ((id->id_data != M_DATA) || (id->id_machine != M_MACH)) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_WRONGARCH), crle->c_name, crle->c_confil, conv_ehdr_data(id->id_data, CONV_FMT_ALT_FILE, &inv_buf1), conv_ehdr_mach(id->id_machine, CONV_FMT_ALT_FILE, &inv_buf2), conv_ehdr_data(M_DATA, CONV_FMT_ALT_FILE, &inv_buf3), conv_ehdr_mach(M_MACH, CONV_FMT_ALT_FILE, &inv_buf4)); return (INSCFG_RET_FAIL); } } /* LINTED */ objtbl = (Rtc_obj *)(CAST_PTRINT(char *, head->ch_obj) + addr); strtbl = (const char *)(CAST_PTRINT(char *, head->ch_str) + addr); /* * If the configuration file has a version higher than we * recognise, we face two issues: * (1) Updates are not possible because we have no * way to recognise or propagate the new features. * This has to be a fatal error. * (2) Printing has the risk that we may have been * handed something other than a real config file, as * well as the fact that we can't display the information * for the new features. So, we print a warning, but * continue on to do the best we can with it. */ if (head->ch_version > RTC_VER_CURRENT) { if (crle->c_flags & CRLE_UPDATE) { (void) fprintf(stderr, MSG_INTL(MSG_ARG_UPDATEVER), crle->c_name, crle->c_confil, (int)head->ch_version, RTC_VER_CURRENT); return (INSCFG_RET_FAIL); } else { (void) fprintf(stderr, MSG_INTL(MSG_ARG_PRINTVER), crle->c_name, crle->c_confil, (int)head->ch_version, RTC_VER_CURRENT); } } /* * If this is a version 1 configuration file we can't generate accurate * update information, or the command-line used to create the file. */ if (head->ch_version == RTC_VER_ONE) { (void) printf(MSG_INTL(MSG_ARG_UPDATE), crle->c_name, crle->c_confil, (int)head->ch_version); } if (!(crle->c_flags & CRLE_UPDATE) && (head->ch_cnflags & RTC_HDR_64)) { /* * Construct the original command line argument. */ cmd = strdupa(MSG_ORIG(MSG_CMD_64)); if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } /* * Start analyzing the configuration files header information. */ if ((crle->c_flags & CRLE_UPDATE) == 0) { const char *fmt; if (head->ch_dlflags) fmt = conv_dl_flag(head->ch_dlflags, 0, &dl_flag_buf); else fmt = MSG_ORIG(MSG_STR_EMPTY); (void) printf(MSG_INTL(MSG_DMP_HEAD), (int)head->ch_version, crle->c_confil, fmt); /* * If the file has an id block, show the information */ if (id) (void) printf(MSG_INTL(MSG_DMP_PLATFORM), conv_ehdr_class(id->id_class, CONV_FMT_ALT_FILE, &inv_buf1), conv_ehdr_data(id->id_data, CONV_FMT_ALT_FILE, &inv_buf2), conv_ehdr_mach(id->id_machine, CONV_FMT_ALT_FILE, &inv_buf3)); /* * Construct the original command line argument. */ (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_CONF), crle->c_confil); cmd = strdupa(_cmd); if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); /* * Construct any -f usage. */ if (head->ch_dlflags && (head->ch_dlflags != RTLD_REL_RELATIVE)) { (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_FLAGS), conv_dl_flag(head->ch_dlflags, CONV_FMT_ALT_CRLE, &dl_flag_buf)); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } else { /* * Establish any -f usage. */ if (head->ch_dlflags && (head->ch_dlflags != RTLD_REL_RELATIVE)) crle->c_dlflags = head->ch_dlflags; } /* * Determine if this configuration file is only applicable to a specific * application. */ if (head->ch_app) { char *alter; obj = (Rtc_obj *)(head->ch_app + addr); /* * Determine the output directory for the files * alternative name. */ alter = (char *)(strtbl + obj->co_alter); (void) strcpy(_objdir, alter); alter = strrchr(_objdir, '/'); *alter = '\0'; crle->c_objdir = objdir = _objdir; if (crle->c_flags & CRLE_UPDATE) { if (inspect(crle, (strtbl + obj->co_name), (RTC_OBJ_DUMP | RTC_OBJ_ALTER | RTC_OBJ_GROUP | RTC_OBJ_CMDLINE)) != 0) return (INSCFG_RET_FAIL); } else { (void) printf(MSG_INTL(MSG_DMP_APP), (strtbl + obj->co_alter), (strtbl + obj->co_name)); /* * Construct the original command line arguments. */ cmdset = B_TRUE; (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_OUTPUT), crle->c_objdir); cmd = strdupa(_cmd); if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_DUMPGRP), (strtbl + obj->co_name)); cmd = strdupa(_cmd); if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } /* * Analyze any alternative library path and trusted directory entries. */ if (head->ch_edlibpath) { const char *str; str = (const char *)(head->ch_edlibpath + addr); if (crle->c_flags & CRLE_UPDATE) { if ((head->ch_cnflags & RTC_HDR_UPM) == 0) { if (head->ch_cnflags & RTC_HDR_64) str = conv_config_upm(str, MSG_ORIG(MSG_PTH_OLDDLP_64), MSG_ORIG(MSG_PTH_UPDLP_64), MSG_PTH_UPDLP_64_SIZE); else str = conv_config_upm(str, MSG_ORIG(MSG_PTH_OLDDLP), MSG_ORIG(MSG_PTH_UPDLP), MSG_PTH_UPDLP_SIZE); } if (addlib(crle, &crle->c_edlibpath, str) != 0) return (INSCFG_RET_FAIL); } else { (void) printf(MSG_INTL(MSG_DMP_DLIBPTH), MSG_ORIG(MSG_STR_ELF), str); (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_EDLIB), str); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } else { if (crle->c_flags & CRLE_UPDATE) { if (crle->c_flags & CRLE_EDLIB) { /* * If we've been asked to update a configuration * file, and no existing default ELF search * path exists, but the user is going to add new * entries, fabricate the system defaults so * that the users get added to them. */ if (fablib(crle, CRLE_EDLIB) != 0) return (INSCFG_RET_FAIL); } } else { /* * Indicate any system default. */ #if M_CLASS == ELFCLASS64 (void) printf(MSG_INTL(MSG_DEF_NEWDLP_64)); #else (void) printf(MSG_INTL(MSG_DEF_NEWDLP)); #endif } } if (head->ch_eslibpath) { const char *str; str = (const char *)(head->ch_eslibpath + addr); if (crle->c_flags & CRLE_UPDATE) { if ((head->ch_cnflags & RTC_HDR_UPM) == 0) { if (head->ch_cnflags & RTC_HDR_64) str = conv_config_upm(str, MSG_ORIG(MSG_PTH_OLDTD_64), MSG_ORIG(MSG_PTH_UPTD_64), MSG_PTH_UPTD_64_SIZE); else str = conv_config_upm(str, MSG_ORIG(MSG_PTH_OLDTD), MSG_ORIG(MSG_PTH_UPTD), MSG_PTH_UPTD_SIZE); } if (addlib(crle, &crle->c_eslibpath, str) != 0) return (INSCFG_RET_FAIL); } else { (void) printf(MSG_INTL(MSG_DMP_TLIBPTH), MSG_ORIG(MSG_STR_ELF), str); (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_ESLIB), str); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } else { if (crle->c_flags & CRLE_UPDATE) { if (crle->c_flags & CRLE_ESLIB) { /* * If we've been asked to update a configuration * file, and no existing default ELF secure * path exists, but the user is going to add new * entries, fabricate the system defaults so * that the users get added to them. */ if (fablib(crle, CRLE_ESLIB) != 0) return (INSCFG_RET_FAIL); } } else { /* * Indicate any system default. */ #if M_CLASS == ELFCLASS64 (void) printf(MSG_INTL(MSG_DEF_NEWTD_64)); #else (void) printf(MSG_INTL(MSG_DEF_NEWTD)); #endif } } /* * Display any environment variables. */ if ((head->ch_version >= RTC_VER_THREE) && head->ch_env) { Rtc_env *envtbl; if ((crle->c_flags & CRLE_UPDATE) == 0) (void) printf(MSG_INTL(MSG_ENV_TITLE)); for (envtbl = (Rtc_env *)(head->ch_env + addr); envtbl->env_str; envtbl++) { const char *str; str = (const char *)(envtbl->env_str + addr); if (crle->c_flags & CRLE_UPDATE) { if (addenv(crle, str, (envtbl->env_flags | RTC_ENV_CONFIG)) == 0) return (INSCFG_RET_FAIL); } else { const char *pfmt, *sfmt; if (envtbl->env_flags & RTC_ENV_PERMANT) { pfmt = MSG_INTL(MSG_ENV_PRM); sfmt = MSG_ORIG(MSG_CMD_PRMENV); } else { pfmt = MSG_INTL(MSG_ENV_RPL); sfmt = MSG_ORIG(MSG_CMD_RPLENV); } (void) printf(pfmt, str); (void) snprintf(_cmd, PATH_MAX, sfmt, str); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } } /* * Display any filter/filtee associations. */ if ((head->ch_version >= RTC_VER_FOUR) && head->ch_fltr) { if ((crle->c_flags & CRLE_UPDATE) == 0) { Rtc_fltr *fltrtbl; Rtc_flte *fltetbl; /* LINTED */ fltrtbl = (Rtc_fltr *) (CAST_PTRINT(char *, head->ch_fltr) + addr); /* LINTED */ fltetbl = (Rtc_flte *) (CAST_PTRINT(char *, head->ch_flte) + addr); (void) printf(MSG_INTL(MSG_FLT_TITLE)); while (fltrtbl->fr_filter) { Rtc_flte *_fltetbl; /* * Print the filter and filtee string pair. */ (void) printf(MSG_INTL(MSG_FLT_FILTER), (strtbl + fltrtbl->fr_filter), (strtbl + fltrtbl->fr_string)); /* * Print each filtee. */ /* LINTED */ for (_fltetbl = (Rtc_flte *)((char *)fltetbl + fltrtbl->fr_filtee); _fltetbl->fe_filtee; _fltetbl++) { (void) printf(MSG_INTL(MSG_FLT_FILTEE), (strtbl + _fltetbl->fe_filtee)); } fltrtbl++; } } } /* * Display any memory reservations required for any alternative * objects. */ if (head->ch_resbgn && ((crle->c_flags & CRLE_UPDATE) == 0)) (void) printf(MSG_INTL(MSG_DMP_RESV), (u_longlong_t)head->ch_resbgn, (u_longlong_t)head->ch_resend, (u_longlong_t)(head->ch_resend - head->ch_resbgn)); /* * If there's no hash table there's nothing else to process. */ if (head->ch_hash == 0) { if (((crle->c_flags & CRLE_UPDATE) == 0) && cmdset) printcmd(crle, head, cmdline); return (INSCFG_RET_OK); } /* * Traverse the directory and filename arrays. */ for (dirtbl = (Rtc_dir *)(head->ch_dir + addr); dirtbl->cd_obj; dirtbl++) { struct stat status; Rtc_obj *dobj; const char *str; dobj = (Rtc_obj *)(dirtbl->cd_obj + addr); filetbl = (Rtc_file *)(dirtbl->cd_file + addr); str = strtbl + dobj->co_name; /* * Simplify recreation by using any command-line directories. * If we're dealing with a version 1 configuration file use * every directory. */ if ((dobj->co_flags & RTC_OBJ_CMDLINE) || (head->ch_version == RTC_VER_ONE)) { if (crle->c_flags & CRLE_UPDATE) { if (inspect(crle, str, getflags(dobj->co_flags)) != 0) return (INSCFG_RET_FAIL); if ((dobj->co_flags & (RTC_OBJ_NOEXIST | RTC_OBJ_ALTER)) == RTC_OBJ_NOEXIST) continue; } else { /* LINTED */ (void) snprintf(_cmd, PATH_MAX, getformat(dobj->co_flags), str); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } } /* * If this isn't an update print the directory name. If the * directory has no entries (possible if the directory is a * symlink to another directory, in which case we record the * real path also), don't bother printing it unless we're in * verbose mode. */ if ((crle->c_flags & CRLE_UPDATE) == 0) { if ((dobj->co_flags & (RTC_OBJ_NOEXIST | RTC_OBJ_ALTER)) == RTC_OBJ_NOEXIST) { (void) printf(MSG_INTL(MSG_DMP_DIR_2), str); continue; } else if (filetbl->cf_obj || (crle->c_flags & CRLE_VERBOSE)) (void) printf(MSG_INTL(MSG_DMP_DIR_1), str); } /* * Under verbose mode validate any real directory entry - the * same test will be carried out by ld.so.1. */ if (((crle->c_flags & CRLE_UPDATE) == 0) && (crle->c_flags & CRLE_VERBOSE) && (dobj->co_flags & RTC_OBJ_REALPTH)) { if (stat(str, &status) != 0) { int err = errno; (void) printf(MSG_INTL(MSG_DMP_STAT), str, strerror(err)); } else if (status.st_mtime != dobj->co_info) { (void) printf(MSG_INTL(MSG_DMP_DCMP), str); } } for (; filetbl->cf_obj; filetbl++) { Rtc_obj *fobj; Half flags; fobj = (Rtc_obj *)(filetbl->cf_obj + addr); str = strtbl + fobj->co_name; flags = fobj->co_flags; /* * Only update individual files that were originally * specified on the command-line. Or, if this is a * version 1 configuration file use every file that * isn't part of an all-entries directory. */ if (((flags & RTC_OBJ_CMDLINE) && ((fobj->co_flags & RTC_OBJ_APP) == 0)) || ((head->ch_version == RTC_VER_ONE) && ((dobj->co_flags & RTC_OBJ_ALLENTS) == 0))) { char *alter = NULL, altdir[PATH_MAX]; /* * Determine whether this file requires an * alternative, and if so, and we haven't * already an alternative in affect, create one. */ if (fobj->co_flags & RTC_OBJ_ALTER) { alter = (char *)(strtbl + fobj->co_alter); (void) strcpy(altdir, alter); alter = strrchr(altdir, '/'); *alter = '\0'; if ((objdir == NULL) || (strcmp(objdir, altdir) != 0)) { (void) strcpy(_objdir, altdir); crle->c_objdir = alter = objdir = _objdir; } else alter = NULL; } if (crle->c_flags & CRLE_UPDATE) { if (inspect(crle, str, getflags(flags)) != 0) return (INSCFG_RET_FAIL); continue; } if (alter) { (void) snprintf(_cmd, PATH_MAX, MSG_ORIG(MSG_CMD_OUTPUT), crle->c_objdir); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } /* LINTED */ (void) snprintf(_cmd, PATH_MAX, getformat(flags), str); cmd = strdupa(_cmd); cmdset = B_TRUE; if (aplist_append(&cmdline, cmd, AL_CNT_CRLE) == NULL) return (INSCFG_RET_FAIL); } if (crle->c_flags & CRLE_UPDATE) continue; /* * Although we record both full pathnames and their * simple filenames (basename), only print the simple * names unless we're under verbose mode. */ if ((strchr(str, '/') == 0) || (crle->c_flags & CRLE_VERBOSE)) { if (fobj->co_flags & RTC_OBJ_ALTER) (void) printf(MSG_INTL(MSG_DMP_FILE_2), str, (strtbl + fobj->co_alter)); else (void) printf(MSG_INTL(MSG_DMP_FILE_1), str); } /* * Under verbose mode validate any real file entry - the * same test will be carried out by ld.so.1. */ if ((crle->c_flags & CRLE_VERBOSE) && (fobj->co_flags & RTC_OBJ_REALPTH)) { if (stat(str, &status) != 0) { int err = errno; (void) printf(MSG_INTL(MSG_DMP_STAT), str, strerror(err)); } else if (status.st_size != fobj->co_info) { (void) printf(MSG_INTL(MSG_DMP_FCMP), str); } } } } if (((crle->c_flags & CRLE_UPDATE) == 0) && cmdset) printcmd(crle, head, cmdline); if ((crle->c_flags & CRLE_VERBOSE) == 0) return (INSCFG_RET_OK); /* * If we've in verbose mode scan the hash list. */ /* LINTED */ hash = (Word *)(CAST_PTRINT(char *, head->ch_hash) + addr); bkts = hash[0]; chain = &hash[2 + bkts]; hash += 2; (void) printf(MSG_INTL(MSG_DMP_HASH)); /* * Scan the hash buckets looking for valid entries. */ for (ndx = 0; ndx < bkts; ndx++, hash++) { Conv_config_obj_buf_t config_obj_buf; Rtc_obj *obj; const char *str; Word _ndx; if (*hash == 0) continue; obj = objtbl + *hash; str = strtbl + obj->co_name; (void) printf(MSG_INTL(MSG_DMP_HASHENT_1), obj->co_id, ndx, str, conv_config_obj(obj->co_flags, &config_obj_buf)); /* * Determine whether there are other objects chained to this * bucket. */ for (_ndx = chain[*hash]; _ndx; _ndx = chain[_ndx]) { obj = objtbl + _ndx; str = strtbl + obj->co_name; (void) printf(MSG_INTL(MSG_DMP_HASHENT_2), obj->co_id, str, conv_config_obj(obj->co_flags, &config_obj_buf)); } } (void) printf(MSG_ORIG(MSG_STR_NL)); return (INSCFG_RET_OK); } INSCFG_RET inspectconfig(Crle_desc * crle, int c_class) { INSCFG_RET error; int fd; Addr addr; struct stat status; const char *caller = crle->c_name, *file = crle->c_confil; Conv_inv_buf_t inv_buf1, inv_buf2, inv_buf3; /* * Open the configuration file, determine its size and map it in. */ if ((fd = open(file, O_RDONLY, 0)) == -1) { int err = errno; if (err == ENOENT) { #ifndef _ELF64 /* Must restart if user requested a 64-bit file */ if (c_class == ELFCLASS64) return (INSCFG_RET_NEED64); #endif /* * To allow an update (-u) from scratch, fabricate any * default search and secure paths that the user * intends to add to. */ if (crle->c_flags & CRLE_UPDATE) { if (crle->c_flags & CRLE_EDLIB) { if (fablib(crle, CRLE_EDLIB)) return (INSCFG_RET_FAIL); } if (crle->c_flags & CRLE_ESLIB) { if (fablib(crle, CRLE_ESLIB)) return (INSCFG_RET_FAIL); } return (INSCFG_RET_OK); } else if (crle->c_flags & CRLE_CONFDEF) { const char *fmt1, *fmt2; /* * Otherwise if the user is inspecting a default * configuration file that doesn't exist inform * them and display the ELF defaults. */ (void) printf(MSG_INTL(MSG_DEF_NOCONF), file); (void) printf(MSG_INTL(MSG_DMP_PLATFORM), conv_ehdr_class(M_CLASS, CONV_FMT_ALT_FILE, &inv_buf1), conv_ehdr_data(M_DATA, CONV_FMT_ALT_FILE, &inv_buf2), conv_ehdr_mach(M_MACH, CONV_FMT_ALT_FILE, &inv_buf3)); #if M_CLASS == ELFCLASS64 fmt1 = MSG_INTL(MSG_DEF_NEWDLP_64); fmt2 = MSG_INTL(MSG_DEF_NEWTD_64); #else fmt1 = MSG_INTL(MSG_DEF_NEWDLP); fmt2 = MSG_INTL(MSG_DEF_NEWTD); #endif (void) printf(fmt1); (void) printf(fmt2); return (INSCFG_RET_OK); } } /* * Otherwise there's an error condition in accessing the file. */ (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), caller, file, strerror(err)); return (INSCFG_RET_FAIL); } (void) fstat(fd, &status); if (status.st_size < sizeof (Rtc_head)) { (void) close(fd); (void) fprintf(stderr, MSG_INTL(MSG_COR_TRUNC), caller, file); return (INSCFG_RET_FAIL); } if ((addr = (Addr)mmap(0, status.st_size, PROT_READ, MAP_SHARED, fd, 0)) == (Addr)MAP_FAILED) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP), caller, file, strerror(err)); (void) close(fd); return (INSCFG_RET_FAIL); } (void) close(fd); /* * Print the contents of the configuration file. */ error = scanconfig(crle, addr, c_class); (void) munmap((void *)addr, status.st_size); return (error); } /* * 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. */ /* * Utility functions */ #include #include #include #include #include #include #include "sgs.h" #include "rtc.h" #include "_crle.h" #include "msg.h" /* * Add an environment string. A list of environment variable descriptors is * maintained so that duplicate definitions can be caught, the first one wins. */ int addenv(Crle_desc *crle, const char *arg, unsigned int flags) { Env_desc *env; char *str; size_t varsz, totsz = strlen(arg) + 1; /* * Determine "=" location so as to separated the variable name from * its value. */ if ((str = strchr(arg, '=')) != NULL) { Aliste idx; varsz = (size_t)(str - arg); /* * Traverse any existing environment variables to see if we've * caught a duplicate. */ for (APLIST_TRAVERSE(crle->c_env, idx, env)) { if ((env->e_varsz == varsz) && (strncmp(env->e_str, arg, varsz) == 0)) { /* * If the user has already specified this string * given them a warning, and ignore the new one. */ if ((env->e_flags & RTC_ENV_CONFIG) == 0) { (void) fprintf(stderr, MSG_INTL(MSG_WARN_ENV), crle->c_name, (int)varsz, env->e_str); return (2); } /* * Otherwise the original string must have been * retrieved from a config file. In this case * allow the user to override it. */ free((void *)env->e_str); crle->c_strsize -= env->e_totsz; crle->c_strsize += totsz; if ((env->e_str = strdup(arg)) == 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), crle->c_name, strerror(err)); return (0); } env->e_varsz = varsz; env->e_totsz = totsz; env->e_flags &= ~RTC_ENV_CONFIG; env->e_flags |= flags; return (1); } } } else { Aliste idx; /* * Although this is just a plain environment definition (no "=") * and probably has no effect on ld.so.1 anyway, we might as * well make sure we're not duplicating the same string. */ for (APLIST_TRAVERSE(crle->c_env, idx, env)) { if (env->e_varsz) continue; if (strcmp(env->e_str, arg) == 0) { if ((env->e_flags & RTC_ENV_CONFIG) == 0) { (void) fprintf(stderr, MSG_INTL(MSG_WARN_ENV), crle->c_name, (int)totsz, env->e_str); return (2); } env->e_flags &= ~RTC_ENV_CONFIG; env->e_flags |= flags; return (1); } } varsz = 0; } /* * Allocate a new environment descriptor. */ if (((env = malloc(sizeof (Env_desc))) == NULL) || ((env->e_str = strdup(arg)) == NULL)) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), crle->c_name, strerror(err)); free(env); return (0); } env->e_varsz = varsz; env->e_totsz = totsz; env->e_flags = flags; if (aplist_append(&(crle->c_env), env, AL_CNT_CRLE) == NULL) return (0); /* * Update the number of environment variables found, and the string * table requirement. */ crle->c_envnum++; crle->c_strsize += totsz; return (1); } /* * Add a library path. Multiple library paths are concatenated together into a * colon separated string suitable for runtime processing. These colon * separated strings can also be passed in as arguments to addlib(), e.g., * -l /usr/lib:/usr/local/lib. This is enabled to make update easier. */ int addlib(Crle_desc *crle, char **lib, const char *args) { char *str, *arg; char *lasts; size_t tlen = strlen(args) + 1; const char *colon = MSG_ORIG(MSG_STR_COLON); /* * Parse the argument for any ":" separated elements. */ str = alloca(tlen); (void) strcpy(str, args); arg = str; if ((arg = strtok_r(arg, colon, &lasts)) != NULL) { do { size_t llen, alen = strlen(arg); if (*lib) { /* * Determine whether this argument exists in the * existing string buffer. */ if (((str = strstr(*lib, arg)) != NULL) && (((str == *lib) || (*(str - 1) == *colon)) && (str += alen) && ((*str == '\0') || (*str == *colon)))) continue; llen = strlen(*lib); tlen = llen + 1; } else { /* * This is the first argument to be added. */ llen = 0; tlen = 0; } /* * This is a new string, so add it to the buffer. If * this is the first occurrence of a string the size is * simply the size of the string + a trailing null. * Otherwise the size is the old string + ":" + the * size of the new string + a trailing null. */ alen += 1; tlen += alen; if ((str = realloc((void *)*lib, tlen)) == 0) { int err = errno; (void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC), crle->c_name, strerror(err)); return (1); } if (llen == 0) (void) strcpy(str, arg); else { /* LINTED */ (void) sprintf(&str[llen], MSG_ORIG(MSG_FMT_COLON), arg); } *lib = str; crle->c_strsize += alen; } while ((arg = strtok_r(NULL, colon, &lasts)) != NULL); } return (0); } /* * -f option expansion. Interpret its argument as a numeric or symbolic * representation of the dldump(3C) flags. */ int dlflags(Crle_desc *crle, const char *arg) { int _flags; char *tok, *_arg; char *lasts; const char *separate = MSG_ORIG(MSG_MOD_SEPARATE); /* * Scan the argument looking for allowable tokens. First determine if * the string is numeric, otherwise try and parse any known flags. */ if ((_flags = (int)strtol(arg, (char **)NULL, 0)) != 0) return (_flags); if ((_arg = malloc(strlen(arg) + 1)) == 0) return (0); (void) strcpy(_arg, arg); if ((tok = strtok_r(_arg, separate, &lasts)) != NULL) { /* BEGIN CSTYLED */ do { if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_RELATIVE)) == 0) _flags |= RTLD_REL_RELATIVE; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_EXEC)) == 0) _flags |= RTLD_REL_EXEC; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_DEPENDS)) == 0) _flags |= RTLD_REL_DEPENDS; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_PRELOAD)) == 0) _flags |= RTLD_REL_PRELOAD; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_SELF)) == 0) _flags |= RTLD_REL_SELF; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_WEAK)) == 0) _flags |= RTLD_REL_WEAK; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_ALL)) == 0) _flags |= RTLD_REL_ALL; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_MEMORY)) == 0) _flags |= RTLD_MEMORY; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_STRIP)) == 0) _flags |= RTLD_STRIP; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_NOHEAP)) == 0) _flags |= RTLD_NOHEAP; else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_CONFGEN)) == 0) _flags |= RTLD_CONFGEN; else { (void) fprintf(stderr, MSG_INTL(MSG_ARG_FLAGS), crle->c_name, tok); free(_arg); return (0); } } while ((tok = strtok_r(NULL, separate, &lasts)) != NULL); /* END CSTYLED */ } if (_flags == 0) (void) fprintf(stderr, MSG_INTL(MSG_ARG_FLAGS), crle->c_name, arg); free(_arg); return (_flags); } /* * Internationalization interface for sgsmsg(1l) use. */ const char * _crle_msg(Msg mid) { return (gettext(MSG_ORIG(mid))); }