crle.c revision 238:265780cf7e76
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, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include	<sys/types.h>
29#include	<sys/stat.h>
30#include	<fcntl.h>
31#include	<stdio.h>
32#include	<string.h>
33#include	<unistd.h>
34#include	<locale.h>
35#include	<dlfcn.h>
36#include	<errno.h>
37#include	"_crle.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)
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
122	if (list_append(&objdirs, lobjdir) == 0)
123		return (1);
124
125	/*
126	 * Establish locale.
127	 */
128	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
129	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
130
131	/*
132	 * Initialization configuration information.
133	 */
134	crle.c_name = argv[0];
135	crle.c_strbkts = 503;
136	crle.c_inobkts = 251;
137	crle.c_class = ELFCLASS32;
138	crle.c_machine = M_MACH;
139
140	/*
141	 * First argument pass.
142	 */
143	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
144		switch (c) {
145
146		case '6':			/* operate on 64-bit objects */
147			if (optarg[0] != '4') {
148				(void) fprintf(stderr,
149				    MSG_INTL(MSG_ARG_ILLEGAL), crle.c_name,
150				    MSG_ORIG(MSG_ARG_6), optarg);
151				error = 1;
152			}
153			crle.c_class = ELFCLASS64;
154#if	defined(sparc)
155			crle.c_machine = EM_SPARCV9;
156#elif	defined(i386)
157			crle.c_machine = EM_IA_64;
158#endif
159			break;
160
161		case 'A':			/* create optional */
162			/* FALLTHROUGH */	/*	alternative */
163		case 'a':			/* create alternative */
164			crle.c_flags |= (CRLE_CREAT | CRLE_ALTER);
165			lobjdir->o_flags |= (CRLE_CREAT | CRLE_ALTER);
166			break;
167
168		case 'c':			/* define the config file */
169			if (crle.c_confil) {
170				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
171				    crle.c_name, MSG_ORIG(MSG_ARG_C));
172				error = 1;
173			}
174			crle.c_confil = optarg;
175			break;
176
177		case 'e':			/* replaceable env variable */
178			crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT);
179			break;
180
181		case 'E':			/* permanent env variable */
182			crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT);
183			break;
184
185		case 'f':			/* dldump(3dl) flags */
186			if (crle.c_dlflags) {
187				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
188				    crle.c_name, MSG_ORIG(MSG_ARG_F));
189				error = 1;
190			}
191			if ((crle.c_dlflags = dlflags(&crle,
192			    (const char *)optarg)) == 0)
193				error = 1;
194			break;
195
196		case 'G':			/* group object */
197			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
198			lobjdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
199			/* FALLTHROUGH */
200		case 'g':
201			crle.c_flags |= CRLE_CREAT;
202			lobjdir->o_flags |= CRLE_CREAT;
203			break;
204
205		case 'I':			/* individual object */
206			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
207			lobjdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
208			/* FALLTHROUGH */
209		case 'i':
210			crle.c_flags |= CRLE_CREAT;
211			lobjdir->o_flags |= CRLE_CREAT;
212			break;
213
214		case 'l':			/* library search path */
215			if (crle.c_flags & CRLE_AOUT)
216				crle.c_flags |= CRLE_ADLIB;
217			else
218				crle.c_flags |= CRLE_EDLIB;
219			crle.c_flags |= CRLE_CREAT;
220			break;
221
222		case 'o':			/* define an object directory */
223			if (lobjdir->o_objdir) {
224				if ((lobjdir = calloc(sizeof (Objdir), 1)) == 0)
225					return (1);
226				if (list_append(&objdirs, lobjdir) == 0)
227					return (1);
228			}
229			lobjdir->o_objdir = optarg;
230			break;
231
232		case 's':			/* trusted (secure) path */
233			if (crle.c_flags & CRLE_AOUT)
234				crle.c_flags |= CRLE_ASLIB;
235			else
236				crle.c_flags |= CRLE_ESLIB;
237			crle.c_flags |= CRLE_CREAT;
238			break;
239
240		case 't':			/* search path type */
241			if (strcmp((const char *)optarg,
242			    MSG_ORIG(MSG_STR_ELF)) == 0)
243				crle.c_flags &= ~CRLE_AOUT;
244			else if (strcmp((const char *)optarg,
245			    MSG_ORIG(MSG_STR_AOUT)) == 0)
246				crle.c_flags |= CRLE_AOUT;
247			else {
248				(void) fprintf(stderr, MSG_INTL(MSG_ARG_TYPE),
249				    crle.c_name, optarg);
250				error = 1;
251			}
252			break;
253
254		case 'u':			/* update mode */
255			crle.c_flags |= (CRLE_CREAT | CRLE_UPDATE);
256			break;
257
258		case 'v':			/* verbose mode */
259			crle.c_flags |= CRLE_VERBOSE;
260			break;
261
262		default:
263			error = 2;
264		}
265	}
266
267	if (optind != argc)
268		error = 2;
269
270	/*
271	 * Determine the configuration file, which in the case of an existing
272	 * error condition is required in the final error message.
273	 */
274	if (crle.c_confil == 0) {
275		crle.c_flags |= CRLE_CONFDEF;
276
277		if (crle.c_class == ELFCLASS32)
278			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG);
279		else
280			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG_64);
281	}
282
283	/*
284	 * Now that we've generated as many file/directory processing errors
285	 * as we can, return if any fatal error conditions occurred.
286	 */
287	if (error) {
288		if (error == 2) {
289			(void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE),
290			    crle.c_name);
291		} else if (crle.c_flags & CRLE_CREAT) {
292			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
293			    crle.c_name, crle.c_confil);
294		}
295		return (1);
296	}
297
298	/*
299	 * Apply any additional defaults.
300	 */
301	if (crle.c_dlflags == 0)
302		crle.c_dlflags = RTLD_REL_RELATIVE;
303
304	crle.c_audit = (char *)MSG_ORIG(MSG_ENV_LD_AUDIT);
305
306	(void) elf_version(EV_CURRENT);
307
308	/*
309	 * If we're updating an existing file or not creating a configuration
310	 * file at all, investigate the original.
311	 */
312	if ((crle.c_flags & CRLE_UPDATE) ||
313	    ((crle.c_flags & CRLE_CREAT) == 0)) {
314		if (inspectconfig(&crle))
315			return (1);
316		if ((crle.c_flags & CRLE_UPDATE) == 0)
317			return (0);
318	}
319
320	if (crle.c_flags & CRLE_VERBOSE)
321		(void) printf(MSG_INTL(MSG_DIA_CONFILE), crle.c_confil);
322
323	/*
324	 * Make sure the configuration file is accessible.  Stat the file to
325	 * determine its dev number - this is used to determine whether the
326	 * temporary configuration file we're about to build can be renamed or
327	 * must be copied to its final destination.
328	 */
329	(void) umask(022);
330	if (access(crle.c_confil, (R_OK | W_OK)) == 0) {
331		crle.c_flags |= CRLE_EXISTS;
332
333		if (stat(crle.c_confil, &ostatus) != 0) {
334			int err = errno;
335			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
336			    crle.c_name, crle.c_confil, strerror(err));
337			return (1);
338		}
339	} else if (errno != ENOENT) {
340		int err = errno;
341		(void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name,
342		    crle.c_confil, strerror(err));
343		return (1);
344	} else {
345		int	fd;
346
347		/*
348		 * Try opening the file now, if it works delete it, there may
349		 * be a lot of processing ahead of us, so we'll come back and
350		 * create the real thing later.
351		 */
352		if ((fd = open(crle.c_confil, (O_RDWR | O_CREAT | O_TRUNC),
353		    0666)) == -1) {
354			int err = errno;
355			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
356			    crle.c_name, crle.c_confil, strerror(err));
357			return (1);
358		}
359		if (fstat(fd, &ostatus) != 0) {
360			int err = errno;
361			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
362			    crle.c_name, crle.c_confil, strerror(err));
363			return (1);
364		}
365		(void) close(fd);
366		(void) unlink(crle.c_confil);
367	}
368
369	/*
370	 * If an object directory is required to hold dldump(3dl) output assign
371	 * a default if necessary and insure we're able to write there.
372	 */
373	if (crle.c_flags & CRLE_ALTER) {
374		if (lobjdir->o_objdir == 0) {
375			char	*str;
376
377			/*
378			 * Use the configuration files directory.
379			 */
380			if ((str = strrchr(crle.c_confil, '/')) == NULL)
381				lobjdir->o_objdir =
382				    (char *)MSG_ORIG(MSG_DIR_DOT);
383			else {
384				int	len = str - crle.c_confil;
385
386				if ((lobjdir->o_objdir =
387				    malloc(len + 1)) == 0) {
388					int err = errno;
389					(void) fprintf(stderr,
390					    MSG_INTL(MSG_SYS_MALLOC),
391					    crle.c_name, strerror(err));
392					return (1);
393				}
394				(void) strncpy(lobjdir->o_objdir,
395				    crle.c_confil, len);
396				lobjdir->o_objdir[len] = '\0';
397			}
398		}
399
400		/*
401		 * If we're going to dldump(3dl) images ourself make sure we
402		 * can access any directories.
403		 */
404		if (crle.c_flags & CRLE_DUMP) {
405			Objdir *	objdir;
406			Listnode *	lnp;
407			int		err = 0;
408
409			for (LIST_TRAVERSE(&objdirs, lnp, objdir)) {
410				if (crle.c_flags & CRLE_VERBOSE)
411					(void) printf(MSG_INTL(MSG_DIA_OBJDIR),
412					    objdir->o_objdir);
413
414				if ((objdir->o_flags & CRLE_DUMP) == 0)
415					continue;
416
417				if (access(objdir->o_objdir,
418				    (R_OK | W_OK)) != 0) {
419					err = errno;
420					(void) fprintf(stderr,
421					    MSG_INTL(MSG_SYS_ACCESS),
422					    crle.c_name, objdir->o_objdir,
423					    strerror(err));
424				}
425			}
426			if (err)
427				return (1);
428		}
429	}
430
431	/*
432	 * Establish any initial object directory.
433	 */
434	crle.c_objdir = _lobjdir.o_objdir;
435
436	/*
437	 * Create a temporary file name in which to build the configuration
438	 * information.
439	 */
440	if ((crle.c_tempname = tempnam(MSG_ORIG(MSG_TMP_DIR),
441	    MSG_ORIG(MSG_TMP_PFX))) == NULL) {
442		int err = errno;
443		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TEMPNAME),
444		    crle.c_name, strerror(err));
445		return (1);
446	}
447	if ((crle.c_tempfd = open(crle.c_tempname, (O_RDWR | O_CREAT),
448	    0666)) == -1) {
449		int err = errno;
450		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
451		    crle.c_name, crle.c_tempname, strerror(err));
452		return (1);
453	}
454	if (stat(crle.c_tempname, &nstatus) != 0) {
455		int err = errno;
456		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
457		    crle.c_name, crle.c_tempname, strerror(err));
458		return (1);
459	}
460	if (ostatus.st_dev != nstatus.st_dev)
461		crle.c_flags |= CRLE_DIFFDEV;
462
463	/*
464	 * Second pass.
465	 */
466	error = 0;
467	optind = 1;
468	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
469		const char	*str;
470		int		flag = 0;
471
472		switch (c) {
473
474		case '6':
475			break;
476
477		case 'A':			/* alternative is optional */
478			flag = RTC_OBJ_OPTINAL;
479			/* FALLTHROUGH */
480		case 'a':			/* alternative required */
481			flag |= (RTC_OBJ_ALTER | RTC_OBJ_CMDLINE);
482			if (inspect(&crle, (const char *)optarg, flag) != 0)
483				error = 1;
484			break;
485
486		case 'c':
487			break;
488
489		case 'e':
490			if ((flag = addenv(&crle, (const char *)optarg,
491			    RTC_ENV_REPLACE)) == 0)
492				error = 1;
493			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
494				(void) printf(MSG_INTL(MSG_DIA_RPLENV),
495				    (const char *)optarg);
496			break;
497
498		case 'E':
499			if ((flag = addenv(&crle, (const char *)optarg,
500			    RTC_ENV_PERMANT)) == 0)
501				error = 1;
502			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
503				(void) printf(MSG_INTL(MSG_DIA_PRMENV),
504				    (const char *)optarg);
505			break;
506
507		case 'f':
508			break;
509
510		case 'G':			/* group object */
511			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
512			/* FALLTHROUGH */
513		case 'g':
514			flag |= (RTC_OBJ_GROUP | RTC_OBJ_CMDLINE);
515			if (inspect(&crle, (const char *)optarg, flag) != 0)
516				error = 1;
517			break;
518
519		case 'I':			/* individual object */
520			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
521			/* FALLTHROUGH */
522		case 'i':
523			flag |= RTC_OBJ_CMDLINE;
524			if (inspect(&crle, (const char *)optarg, flag) != 0)
525				error = 1;
526			break;
527
528		case 'l':			/* library search path */
529			if (crle.c_flags & CRLE_AOUT) {
530				str = MSG_ORIG(MSG_STR_AOUT);
531				lib = &crle.c_adlibpath;
532			} else {
533				str = MSG_ORIG(MSG_STR_ELF);
534				lib = &crle.c_edlibpath;
535			}
536			if (addlib(&crle, lib, (const char *)optarg) != 0)
537				error = 1;
538			else if (crle.c_flags & CRLE_VERBOSE)
539				(void) printf(MSG_INTL(MSG_DIA_DLIBPTH),
540				    str, (const char *)optarg);
541			break;
542
543		case 'o':
544			crle.c_objdir = optarg;
545			break;
546
547		case 's':			/* trusted (secure) path */
548			if (crle.c_flags & CRLE_AOUT) {
549				str = MSG_ORIG(MSG_STR_AOUT);
550				lib = &crle.c_aslibpath;
551			} else {
552				str = MSG_ORIG(MSG_STR_ELF);
553				lib = &crle.c_eslibpath;
554			}
555			if (addlib(&crle, lib, (const char *)optarg) != 0)
556				error = 1;
557			else if (crle.c_flags & CRLE_VERBOSE)
558				(void) printf(MSG_INTL(MSG_DIA_TLIBPTH),
559				    str, (const char *)optarg);
560			break;
561
562		case 't':			/* search path type */
563			if (strcmp((const char *)optarg,
564			    MSG_ORIG(MSG_STR_ELF)) == 0)
565				crle.c_flags &= ~CRLE_AOUT;
566			else
567				crle.c_flags |= CRLE_AOUT;
568			break;
569
570		case 'u':
571			break;
572
573		case 'v':
574			break;
575		}
576	}
577
578	/*
579	 * Now that we've generated as many file/directory processing errors
580	 * as we can, return if any fatal error conditions occurred.
581	 */
582	if (error) {
583		(void) unlink(crle.c_tempname);
584		if (crle.c_flags & CRLE_CREAT) {
585			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
586			    crle.c_name, crle.c_confil);
587		}
588		return (1);
589	}
590
591	/*
592	 * Create a temporary configuration file.
593	 */
594	if (genconfig(&crle) != 0) {
595		(void) unlink(crle.c_tempname);
596		return (1);
597	}
598
599	/*
600	 * If dldump(3dl) images are required spawn a process to create them.
601	 */
602	if (crle.c_flags & CRLE_DUMP) {
603		if (dump(&crle) != 0) {
604			(void) unlink(crle.c_tempname);
605			return (1);
606		}
607	}
608
609	/*
610	 * Copy the finished temporary configuration file to its final home.
611	 */
612	if (updateconfig(&crle) != 0)
613		return (1);
614
615	return (0);
616}
617