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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include	<sys/types.h>
27#include	<sys/stat.h>
28#include	<fcntl.h>
29#include	<stdio.h>
30#include	<string.h>
31#include	<unistd.h>
32#include	<locale.h>
33#include	<dlfcn.h>
34#include	<errno.h>
35#include	"_crle.h"
36#include	"conv.h"
37#include	"msg.h"
38
39
40/*
41 * crle(1) entry point and argument processing.
42 *
43 * Two passes of the arguments are carried out; the first collects any single
44 * instance options and establishes defaults that might be appropriate for
45 * other arguments:
46 *
47 *  -64		operate on, or apply, 64-bit objects (default is 32-bit).
48 *
49 *  -c file	defines the output configuration file.
50 *
51 *  -f flag	flags for dldump(3dl).
52 *
53 *  -o dir	defines the output directory for any dldump(3dl) objects
54 *		that follow.  For backward compatibility (RTC_VER_ONE only
55 * 		allowed one output directory) allow the first occurrence of this
56 *		specification to catch any previous files.  If not specified,
57 *		the configuration files parent directory is used).
58 *
59 *  -u		update any existing configuration file.  Any additional
60 *		arguments supplied will be added to the new configuration
61 *		information.
62 *
63 *  -v		verbose mode.
64 *
65 * The second pass collects all other options and constructs an internal
66 * string table which will be used to create the eventual configuration file.
67 *
68 *  -a name	add the individual name, with an alternative to the
69 *		configuration cache.  No alternative is created via dldump(3dl),
70 *		it is the users responsibility to furnish the alternative.
71 *
72 *  -A name	add the individual name, with an optional alternative to the
73 *		configuration cache.  No alternative is created via dldump(3dl),
74 *		it is the users responsibility to furnish the alternative.
75 *
76 *  -e envar	replaceable environment variable
77 *
78 *  -E envar	permanent environment variable
79 *
80 *  -i name	add the individual name to the configuration cache.  If name
81 *		is a directory each shared object within the directory is added
82 *		to the cache.
83 *
84 *  -I name	same as -i, but in addition any ELF objects are dldump(3dl)'ed.
85 *
86 *  -g name	add the group name to the configuration cache.  Each object is
87 * 		expanded to determine its dependencies and these are added to
88 *		the cache.  If name is a directory each shared object within the
89 *		directory and its dependencies are added to the cache.
90 *
91 *  -G app	same as -g, but in addition any ELF objects are dldump(3dl)'ed.
92 *
93 *  -l dir	library search directory
94 *
95 *  -s dir	trusted (secure) directory
96 *
97 *  -t type	search directory type (ELF or AOUT).
98 */
99
100/*
101 * Establish a structure for maintaining current object directory attributes.
102 * We wish to validate the access of any object directory that will be written
103 * to (dldump(3dl), and thus by maintaining a current object directory and its
104 * intended use we can perform this validation later.
105 */
106typedef struct {
107	char	*o_objdir;
108	uint_t	o_flags;
109} Objdir;
110
111/*ARGSUSED2*/
112int
113main(int argc, char **argv, char **envp)
114{
115	Crle_desc	crle = { 0 };
116	int		c, error = 0;
117	char		**lib;
118	Alist		*objdirs = NULL;
119	Objdir		*objdir, *iobjdir;
120	struct stat	ostatus, nstatus;
121	int 		c_class;
122
123	if ((objdir = iobjdir = alist_append(&objdirs, NULL, sizeof (Objdir),
124	    AL_CNT_CRLE)) == NULL)
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			objdir->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			objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
197			/* FALLTHROUGH */
198		case 'g':
199			crle.c_flags |= CRLE_CREAT;
200			objdir->o_flags |= CRLE_CREAT;
201			break;
202
203		case 'I':			/* individual object */
204			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
205			objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
206			/* FALLTHROUGH */
207		case 'i':
208			crle.c_flags |= CRLE_CREAT;
209			objdir->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 (objdir->o_objdir) {
222				if ((objdir = alist_append(&objdirs, NULL,
223				    sizeof (Objdir), AL_CNT_CRLE)) == NULL)
224					return (1);
225			}
226			objdir->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 == NULL) {
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, c_class)) {
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		case INSCFG_RET_NEED64:
319			c_class = ELFCLASS64;
320			break;
321		}
322	}
323
324	/*
325	 * Ensure that the right version (32 or 64-bit) of this program
326	 * is running. The 32 and 64-bit compilers may align fields within
327	 * structures differently. Using the right version of crle for
328	 * the config file ensures that all linker components will see
329	 * the same layout, without the need for special code.
330	 */
331#ifdef _ELF64
332	if (c_class == ELFCLASS32) {
333		(void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS),
334		    crle.c_name, crle.c_confil);
335		return (1);
336	}
337#else
338	if (c_class == ELFCLASS64) {
339		(void) conv_check_native(argv, envp);
340
341		/*
342		 * conv_check_native() should not return, as we expect
343		 * the 64-bit version to have executed on top of us.
344		 * If it does, it means there is no 64-bit support
345		 * available on this system.
346		 */
347		(void) fprintf(stderr, MSG_INTL(MSG_ISA32_NO64SUP),
348		    crle.c_name);
349		return (1);
350	}
351#endif
352
353	if (crle.c_flags & CRLE_VERBOSE)
354		(void) printf(MSG_INTL(MSG_DIA_CONFILE), crle.c_confil);
355
356	/*
357	 * Make sure the configuration file is accessible.  Stat the file to
358	 * determine its dev number - this is used to determine whether the
359	 * temporary configuration file we're about to build can be renamed or
360	 * must be copied to its final destination.
361	 */
362	(void) umask(022);
363	if (access(crle.c_confil, (R_OK | W_OK)) == 0) {
364		crle.c_flags |= CRLE_EXISTS;
365
366		if (stat(crle.c_confil, &ostatus) != 0) {
367			int err = errno;
368			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
369			    crle.c_name, crle.c_confil, strerror(err));
370			return (1);
371		}
372	} else if (errno != ENOENT) {
373		int err = errno;
374		(void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name,
375		    crle.c_confil, strerror(err));
376		return (1);
377	} else {
378		int	fd;
379
380		/*
381		 * Try opening the file now, if it works delete it, there may
382		 * be a lot of processing ahead of us, so we'll come back and
383		 * create the real thing later.
384		 */
385		if ((fd = open(crle.c_confil, (O_RDWR | O_CREAT | O_TRUNC),
386		    0666)) == -1) {
387			int err = errno;
388			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
389			    crle.c_name, crle.c_confil, strerror(err));
390			return (1);
391		}
392		if (fstat(fd, &ostatus) != 0) {
393			int err = errno;
394			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
395			    crle.c_name, crle.c_confil, strerror(err));
396			return (1);
397		}
398		(void) close(fd);
399		(void) unlink(crle.c_confil);
400	}
401
402	/*
403	 * If an object directory is required to hold dldump(3dl) output assign
404	 * a default if necessary and insure we're able to write there.
405	 */
406	if (crle.c_flags & CRLE_ALTER) {
407		if (objdir->o_objdir == NULL) {
408			char	*str;
409
410			/*
411			 * Use the configuration files directory.
412			 */
413			if ((str = strrchr(crle.c_confil, '/')) == NULL)
414				objdir->o_objdir =
415				    (char *)MSG_ORIG(MSG_DIR_DOT);
416			else {
417				int	len = str - crle.c_confil;
418
419				if ((objdir->o_objdir =
420				    malloc(len + 1)) == NULL) {
421					int err = errno;
422					(void) fprintf(stderr,
423					    MSG_INTL(MSG_SYS_MALLOC),
424					    crle.c_name, strerror(err));
425					return (1);
426				}
427				(void) strncpy(objdir->o_objdir,
428				    crle.c_confil, len);
429				objdir->o_objdir[len] = '\0';
430			}
431		}
432
433		/*
434		 * If we're going to dldump(3dl) images ourself make sure we
435		 * can access any directories.
436		 */
437		if (crle.c_flags & CRLE_DUMP) {
438			Objdir	*objdir;
439			Aliste	idx;
440			int	err = 0;
441
442			for (ALIST_TRAVERSE(objdirs, idx, objdir)) {
443				if (crle.c_flags & CRLE_VERBOSE)
444					(void) printf(MSG_INTL(MSG_DIA_OBJDIR),
445					    objdir->o_objdir);
446
447				if ((objdir->o_flags & CRLE_DUMP) == 0)
448					continue;
449
450				if (access(objdir->o_objdir,
451				    (R_OK | W_OK)) != 0) {
452					err = errno;
453					(void) fprintf(stderr,
454					    MSG_INTL(MSG_SYS_ACCESS),
455					    crle.c_name, objdir->o_objdir,
456					    strerror(err));
457				}
458			}
459			if (err)
460				return (1);
461		}
462	}
463
464	/*
465	 * Establish any initial object directory.
466	 */
467	crle.c_objdir = iobjdir->o_objdir;
468
469	/*
470	 * Create a temporary file name in which to build the configuration
471	 * information.
472	 */
473	if ((crle.c_tempname = tempnam(MSG_ORIG(MSG_TMP_DIR),
474	    MSG_ORIG(MSG_TMP_PFX))) == NULL) {
475		int err = errno;
476		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TEMPNAME),
477		    crle.c_name, strerror(err));
478		return (1);
479	}
480	if ((crle.c_tempfd = open(crle.c_tempname, (O_RDWR | O_CREAT),
481	    0666)) == -1) {
482		int err = errno;
483		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
484		    crle.c_name, crle.c_tempname, strerror(err));
485		return (1);
486	}
487	if (stat(crle.c_tempname, &nstatus) != 0) {
488		int err = errno;
489		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
490		    crle.c_name, crle.c_tempname, strerror(err));
491		return (1);
492	}
493	if (ostatus.st_dev != nstatus.st_dev)
494		crle.c_flags |= CRLE_DIFFDEV;
495
496	/*
497	 * Second pass.
498	 */
499	error = 0;
500	optind = 1;
501	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
502		const char	*str;
503		int		flag = 0;
504
505		switch (c) {
506
507		case '6':
508			break;
509
510		case 'A':			/* alternative is optional */
511			flag = RTC_OBJ_OPTINAL;
512			/* FALLTHROUGH */
513		case 'a':			/* alternative required */
514			flag |= (RTC_OBJ_ALTER | RTC_OBJ_CMDLINE);
515			if (inspect(&crle, (const char *)optarg, flag) != 0)
516				error = 1;
517			break;
518
519		case 'c':
520			break;
521
522		case 'e':
523			if ((flag = addenv(&crle, (const char *)optarg,
524			    RTC_ENV_REPLACE)) == 0)
525				error = 1;
526			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
527				(void) printf(MSG_INTL(MSG_DIA_RPLENV),
528				    (const char *)optarg);
529			break;
530
531		case 'E':
532			if ((flag = addenv(&crle, (const char *)optarg,
533			    RTC_ENV_PERMANT)) == 0)
534				error = 1;
535			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
536				(void) printf(MSG_INTL(MSG_DIA_PRMENV),
537				    (const char *)optarg);
538			break;
539
540		case 'f':
541			break;
542
543		case 'G':			/* group object */
544			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
545			/* FALLTHROUGH */
546		case 'g':
547			flag |= (RTC_OBJ_GROUP | RTC_OBJ_CMDLINE);
548			if (inspect(&crle, (const char *)optarg, flag) != 0)
549				error = 1;
550			break;
551
552		case 'I':			/* individual object */
553			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
554			/* FALLTHROUGH */
555		case 'i':
556			flag |= RTC_OBJ_CMDLINE;
557			if (inspect(&crle, (const char *)optarg, flag) != 0)
558				error = 1;
559			break;
560
561		case 'l':			/* library search path */
562			if (crle.c_flags & CRLE_AOUT) {
563				str = MSG_ORIG(MSG_STR_AOUT);
564				lib = &crle.c_adlibpath;
565			} else {
566				str = MSG_ORIG(MSG_STR_ELF);
567				lib = &crle.c_edlibpath;
568			}
569			if (addlib(&crle, lib, (const char *)optarg) != 0)
570				error = 1;
571			else if (crle.c_flags & CRLE_VERBOSE)
572				(void) printf(MSG_INTL(MSG_DIA_DLIBPTH),
573				    str, (const char *)optarg);
574			break;
575
576		case 'o':
577			crle.c_objdir = optarg;
578			break;
579
580		case 's':			/* trusted (secure) path */
581			if (crle.c_flags & CRLE_AOUT) {
582				str = MSG_ORIG(MSG_STR_AOUT);
583				lib = &crle.c_aslibpath;
584			} else {
585				str = MSG_ORIG(MSG_STR_ELF);
586				lib = &crle.c_eslibpath;
587			}
588			if (addlib(&crle, lib, (const char *)optarg) != 0)
589				error = 1;
590			else if (crle.c_flags & CRLE_VERBOSE)
591				(void) printf(MSG_INTL(MSG_DIA_TLIBPTH),
592				    str, (const char *)optarg);
593			break;
594
595		case 't':			/* search path type */
596			if (strcmp((const char *)optarg,
597			    MSG_ORIG(MSG_STR_ELF)) == 0)
598				crle.c_flags &= ~CRLE_AOUT;
599			else
600				crle.c_flags |= CRLE_AOUT;
601			break;
602
603		case 'u':
604			break;
605
606		case 'v':
607			break;
608		}
609	}
610
611	/*
612	 * Now that we've generated as many file/directory processing errors
613	 * as we can, return if any fatal error conditions occurred.
614	 */
615	if (error) {
616		(void) unlink(crle.c_tempname);
617		if (crle.c_flags & CRLE_CREAT) {
618			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
619			    crle.c_name, crle.c_confil);
620		}
621		return (1);
622	}
623
624	/*
625	 * Create a temporary configuration file.
626	 */
627	if (genconfig(&crle) != 0) {
628		(void) unlink(crle.c_tempname);
629		return (1);
630	}
631
632	/*
633	 * If dldump(3dl) images are required spawn a process to create them.
634	 */
635	if (crle.c_flags & CRLE_DUMP) {
636		if (dump(&crle) != 0) {
637			(void) unlink(crle.c_tempname);
638			return (1);
639		}
640	}
641
642	/*
643	 * Copy the finished temporary configuration file to its final home.
644	 */
645	if (updateconfig(&crle) != 0)
646		return (1);
647
648	return (0);
649}
650