xref: /illumos-gate/usr/src/cmd/sgs/crle/common/crle.c (revision a93a1f58a8763fa69172980b98e3d24720c1136e)
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  */
107 typedef struct {
108 	char		*o_objdir;
109 	unsigned int	o_flags;
110 } Objdir;
111 
112 /*ARGSUSED2*/
113 int
114 main(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