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