crle.c revision 2647:e440e3da2a6f
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
112/*ARGSUSED2*/
113int
114main(int argc, char **argv, char **envp)
115{
116	Crle_desc	crle = { 0 };
117	int		c, error = 0;
118	char **		lib;
119	List		objdirs = { 0, 0 };
120	Objdir		_lobjdir = { 0, 0 }, * lobjdir = &_lobjdir;
121	struct stat	ostatus, nstatus;
122	int 		c_class;
123
124	if (list_append(&objdirs, lobjdir) == 0)
125		return (1);
126
127	/*
128	 * Establish locale.
129	 */
130	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
131	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
132
133	/*
134	 * Initialization configuration information.
135	 */
136	crle.c_name = argv[0];
137	crle.c_flags |= CRLE_ADDID;
138	crle.c_strbkts = 503;
139	crle.c_inobkts = 251;
140	c_class = M_CLASS;
141
142	/*
143	 * First argument pass.
144	 */
145	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
146		switch (c) {
147
148		case '6':			/* operate on 64-bit objects */
149			if (optarg[0] != '4') {
150				(void) fprintf(stderr,
151				    MSG_INTL(MSG_ARG_ILLEGAL), crle.c_name,
152				    MSG_ORIG(MSG_ARG_6), optarg);
153				error = 1;
154			}
155
156			c_class = ELFCLASS64;
157			break;
158
159		case 'A':			/* create optional */
160			/* FALLTHROUGH */	/*	alternative */
161		case 'a':			/* create alternative */
162			crle.c_flags |= (CRLE_CREAT | CRLE_ALTER);
163			lobjdir->o_flags |= (CRLE_CREAT | CRLE_ALTER);
164			break;
165
166		case 'c':			/* define the config file */
167			if (crle.c_confil) {
168				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
169				    crle.c_name, MSG_ORIG(MSG_ARG_C));
170				error = 1;
171			}
172			crle.c_confil = optarg;
173			break;
174
175		case 'e':			/* replaceable env variable */
176			crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT);
177			break;
178
179		case 'E':			/* permanent env variable */
180			crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT);
181			break;
182
183		case 'f':			/* dldump(3dl) flags */
184			if (crle.c_dlflags) {
185				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
186				    crle.c_name, MSG_ORIG(MSG_ARG_F));
187				error = 1;
188			}
189			if ((crle.c_dlflags = dlflags(&crle,
190			    (const char *)optarg)) == 0)
191				error = 1;
192			break;
193
194		case 'G':			/* group object */
195			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
196			lobjdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
197			/* FALLTHROUGH */
198		case 'g':
199			crle.c_flags |= CRLE_CREAT;
200			lobjdir->o_flags |= CRLE_CREAT;
201			break;
202
203		case 'I':			/* individual object */
204			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
205			lobjdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
206			/* FALLTHROUGH */
207		case 'i':
208			crle.c_flags |= CRLE_CREAT;
209			lobjdir->o_flags |= CRLE_CREAT;
210			break;
211
212		case 'l':			/* library search path */
213			if (crle.c_flags & CRLE_AOUT)
214				crle.c_flags |= CRLE_ADLIB;
215			else
216				crle.c_flags |= CRLE_EDLIB;
217			crle.c_flags |= CRLE_CREAT;
218			break;
219
220		case 'o':			/* define an object directory */
221			if (lobjdir->o_objdir) {
222				if ((lobjdir = calloc(sizeof (Objdir), 1)) == 0)
223					return (1);
224				if (list_append(&objdirs, lobjdir) == 0)
225					return (1);
226			}
227			lobjdir->o_objdir = optarg;
228			break;
229
230		case 's':			/* trusted (secure) path */
231			if (crle.c_flags & CRLE_AOUT)
232				crle.c_flags |= CRLE_ASLIB;
233			else
234				crle.c_flags |= CRLE_ESLIB;
235			crle.c_flags |= CRLE_CREAT;
236			break;
237
238		case 't':			/* search path type */
239			if (strcmp((const char *)optarg,
240			    MSG_ORIG(MSG_STR_ELF)) == 0)
241				crle.c_flags &= ~CRLE_AOUT;
242			else if (strcmp((const char *)optarg,
243			    MSG_ORIG(MSG_STR_AOUT)) == 0)
244				crle.c_flags |= CRLE_AOUT;
245			else {
246				(void) fprintf(stderr, MSG_INTL(MSG_ARG_TYPE),
247				    crle.c_name, optarg);
248				error = 1;
249			}
250			break;
251
252		case 'u':			/* update mode */
253			crle.c_flags |= (CRLE_CREAT | CRLE_UPDATE);
254			break;
255
256		case 'v':			/* verbose mode */
257			crle.c_flags |= CRLE_VERBOSE;
258			break;
259
260		default:
261			error = 2;
262		}
263	}
264
265	if (optind != argc)
266		error = 2;
267
268	/*
269	 * Determine the configuration file, which in the case of an existing
270	 * error condition is required in the final error message.
271	 */
272	if (crle.c_confil == 0) {
273		crle.c_flags |= CRLE_CONFDEF;
274		if (c_class == ELFCLASS32) {
275			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG);
276		} else {
277			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG_64);
278		}
279	}
280
281	/*
282	 * Now that we've generated as many file/directory processing errors
283	 * as we can, return if any fatal error conditions occurred.
284	 */
285	if (error) {
286		if (error == 2) {
287			(void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE),
288			    crle.c_name);
289		} else if (crle.c_flags & CRLE_CREAT) {
290			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
291			    crle.c_name, crle.c_confil);
292		}
293		return (1);
294	}
295
296	/*
297	 * Apply any additional defaults.
298	 */
299	if (crle.c_dlflags == 0)
300		crle.c_dlflags = RTLD_REL_RELATIVE;
301
302	crle.c_audit = (char *)MSG_ORIG(MSG_ENV_LD_AUDIT);
303
304	(void) elf_version(EV_CURRENT);
305
306	/*
307	 * If we're updating an existing file or not creating a configuration
308	 * file at all, investigate the original.
309	 */
310	if ((crle.c_flags & CRLE_UPDATE) ||
311	    ((crle.c_flags & CRLE_CREAT) == 0)) {
312		switch (inspectconfig(&crle, c_class)) {
313		case INSCFG_RET_OK:
314			if ((crle.c_flags & CRLE_UPDATE) == 0)
315				return (0);
316			break;
317		case INSCFG_RET_FAIL:
318			return (1);
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		(void) 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