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