xref: /illumos-gate/usr/src/cmd/sgs/crle/common/crle.c (revision fec04708)
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 
98 /*
99  * Establish a structure for maintaining current object directory attributes.
100  * We wish to validate the access of any object directory that will be written
101  * to (dldump(3dl), and thus by maintaining a current object directory and its
102  * intended use we can perform this validation later.
103  */
104 typedef struct {
105 	char	*o_objdir;
106 	uint_t	o_flags;
107 } Objdir;
108 
109 /*ARGSUSED2*/
110 int
main(int argc,char ** argv,char ** envp)111 main(int argc, char **argv, char **envp)
112 {
113 	Crle_desc	crle = { 0 };
114 	int		c, error = 0;
115 	char		**lib;
116 	Alist		*objdirs = NULL;
117 	Objdir		*objdir, *iobjdir;
118 	struct stat	ostatus, nstatus;
119 	int		c_class;
120 
121 	if ((objdir = iobjdir = alist_append(&objdirs, NULL, sizeof (Objdir),
122 	    AL_CNT_CRLE)) == NULL)
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_flags |= CRLE_ADDID;
136 	crle.c_strbkts = 503;
137 	crle.c_inobkts = 251;
138 	c_class = M_CLASS;
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 
154 			c_class = ELFCLASS64;
155 			break;
156 
157 		case 'A':			/* create optional */
158 			/* FALLTHROUGH */	/*	alternative */
159 		case 'a':			/* create alternative */
160 			crle.c_flags |= (CRLE_CREAT | CRLE_ALTER);
161 			objdir->o_flags |= (CRLE_CREAT | CRLE_ALTER);
162 			break;
163 
164 		case 'c':			/* define the config file */
165 			if (crle.c_confil) {
166 				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
167 				    crle.c_name, MSG_ORIG(MSG_ARG_C));
168 				error = 1;
169 			}
170 			crle.c_confil = optarg;
171 			break;
172 
173 		case 'e':			/* replaceable env variable */
174 			crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT);
175 			break;
176 
177 		case 'E':			/* permanent env variable */
178 			crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT);
179 			break;
180 
181 		case 'f':			/* dldump(3dl) flags */
182 			if (crle.c_dlflags) {
183 				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
184 				    crle.c_name, MSG_ORIG(MSG_ARG_F));
185 				error = 1;
186 			}
187 			if ((crle.c_dlflags = dlflags(&crle,
188 			    (const char *)optarg)) == 0)
189 				error = 1;
190 			break;
191 
192 		case 'G':			/* group object */
193 			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
194 			objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
195 			/* FALLTHROUGH */
196 		case 'g':
197 			crle.c_flags |= CRLE_CREAT;
198 			objdir->o_flags |= CRLE_CREAT;
199 			break;
200 
201 		case 'I':			/* individual object */
202 			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
203 			objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
204 			/* FALLTHROUGH */
205 		case 'i':
206 			crle.c_flags |= CRLE_CREAT;
207 			objdir->o_flags |= CRLE_CREAT;
208 			break;
209 
210 		case 'l':			/* library search path */
211 			crle.c_flags |= (CRLE_EDLIB | CRLE_CREAT);
212 			break;
213 
214 		case 'o':			/* define an object directory */
215 			if (objdir->o_objdir) {
216 				if ((objdir = alist_append(&objdirs, NULL,
217 				    sizeof (Objdir), AL_CNT_CRLE)) == NULL)
218 					return (1);
219 			}
220 			objdir->o_objdir = optarg;
221 			break;
222 
223 		case 's':			/* trusted (secure) path */
224 			crle.c_flags |= (CRLE_ESLIB | CRLE_CREAT);
225 			break;
226 
227 		/*
228 		 * Search path type, undocumented but left for compatibility.
229 		 * Previously used to select between AOUT and ELF, now
230 		 * anything other than ELF is an error.
231 		 */
232 		case 't':
233 			if (strcmp((const char *)optarg,
234 			    MSG_ORIG(MSG_STR_ELF)) != 0) {
235 				(void) fprintf(stderr, MSG_INTL(MSG_ARG_TYPE),
236 				    crle.c_name, optarg);
237 				error = 1;
238 			}
239 			break;
240 
241 		case 'u':			/* update mode */
242 			crle.c_flags |= (CRLE_CREAT | CRLE_UPDATE);
243 			break;
244 
245 		case 'v':			/* verbose mode */
246 			crle.c_flags |= CRLE_VERBOSE;
247 			break;
248 
249 		default:
250 			error = 2;
251 		}
252 	}
253 
254 	if (optind != argc)
255 		error = 2;
256 
257 	/*
258 	 * Determine the configuration file, which in the case of an existing
259 	 * error condition is required in the final error message.
260 	 */
261 	if (crle.c_confil == NULL) {
262 		crle.c_flags |= CRLE_CONFDEF;
263 		if (c_class == ELFCLASS32) {
264 			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG);
265 		} else {
266 			crle.c_confil = (char *)MSG_ORIG(MSG_PTH_CONFIG_64);
267 		}
268 	}
269 
270 	/*
271 	 * Now that we've generated as many file/directory processing errors
272 	 * as we can, return if any fatal error conditions occurred.
273 	 */
274 	if (error) {
275 		if (error == 2) {
276 			(void) fprintf(stderr, MSG_INTL(MSG_ARG_USAGE),
277 			    crle.c_name);
278 		} else if (crle.c_flags & CRLE_CREAT) {
279 			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
280 			    crle.c_name, crle.c_confil);
281 		}
282 		return (1);
283 	}
284 
285 	/*
286 	 * Apply any additional defaults.
287 	 */
288 	if (crle.c_dlflags == 0)
289 		crle.c_dlflags = RTLD_REL_RELATIVE;
290 
291 	crle.c_audit = (char *)MSG_ORIG(MSG_ENV_LD_AUDIT);
292 
293 	(void) elf_version(EV_CURRENT);
294 
295 	/*
296 	 * If we're updating an existing file or not creating a configuration
297 	 * file at all, investigate the original.
298 	 */
299 	if ((crle.c_flags & CRLE_UPDATE) ||
300 	    ((crle.c_flags & CRLE_CREAT) == 0)) {
301 		switch (inspectconfig(&crle, c_class)) {
302 		case INSCFG_RET_OK:
303 			if ((crle.c_flags & CRLE_UPDATE) == 0)
304 				return (0);
305 			break;
306 		case INSCFG_RET_FAIL:
307 			return (1);
308 		case INSCFG_RET_NEED64:
309 			c_class = ELFCLASS64;
310 			break;
311 		}
312 	}
313 
314 	/*
315 	 * Ensure that the right version (32 or 64-bit) of this program
316 	 * is running. The 32 and 64-bit compilers may align fields within
317 	 * structures differently. Using the right version of crle for
318 	 * the config file ensures that all linker components will see
319 	 * the same layout, without the need for special code.
320 	 */
321 #ifdef _ELF64
322 	if (c_class == ELFCLASS32) {
323 		(void) fprintf(stderr, MSG_INTL(MSG_ARG_CLASS),
324 		    crle.c_name, crle.c_confil);
325 		return (1);
326 	}
327 #else
328 	if (c_class == ELFCLASS64) {
329 		(void) conv_check_native(argv, envp);
330 
331 		/*
332 		 * conv_check_native() should not return, as we expect
333 		 * the 64-bit version to have executed on top of us.
334 		 * If it does, it means there is no 64-bit support
335 		 * available on this system.
336 		 */
337 		(void) fprintf(stderr, MSG_INTL(MSG_ISA32_NO64SUP),
338 		    crle.c_name);
339 		return (1);
340 	}
341 #endif
342 
343 	if (crle.c_flags & CRLE_VERBOSE)
344 		(void) printf(MSG_INTL(MSG_DIA_CONFILE), crle.c_confil);
345 
346 	/*
347 	 * Make sure the configuration file is accessible.  Stat the file to
348 	 * determine its dev number - this is used to determine whether the
349 	 * temporary configuration file we're about to build can be renamed or
350 	 * must be copied to its final destination.
351 	 */
352 	(void) umask(022);
353 	if (access(crle.c_confil, (R_OK | W_OK)) == 0) {
354 		crle.c_flags |= CRLE_EXISTS;
355 
356 		if (stat(crle.c_confil, &ostatus) != 0) {
357 			int err = errno;
358 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
359 			    crle.c_name, crle.c_confil, strerror(err));
360 			return (1);
361 		}
362 	} else if (errno != ENOENT) {
363 		int err = errno;
364 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_ACCESS), crle.c_name,
365 		    crle.c_confil, strerror(err));
366 		return (1);
367 	} else {
368 		int	fd;
369 
370 		/*
371 		 * Try opening the file now, if it works delete it, there may
372 		 * be a lot of processing ahead of us, so we'll come back and
373 		 * create the real thing later.
374 		 */
375 		if ((fd = open(crle.c_confil, (O_RDWR | O_CREAT | O_TRUNC),
376 		    0666)) == -1) {
377 			int err = errno;
378 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
379 			    crle.c_name, crle.c_confil, strerror(err));
380 			return (1);
381 		}
382 		if (fstat(fd, &ostatus) != 0) {
383 			int err = errno;
384 			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
385 			    crle.c_name, crle.c_confil, strerror(err));
386 			return (1);
387 		}
388 		(void) close(fd);
389 		(void) unlink(crle.c_confil);
390 	}
391 
392 	/*
393 	 * If an object directory is required to hold dldump(3dl) output assign
394 	 * a default if necessary and insure we're able to write there.
395 	 */
396 	if (crle.c_flags & CRLE_ALTER) {
397 		if (objdir->o_objdir == NULL) {
398 			char	*str;
399 
400 			/*
401 			 * Use the configuration files directory.
402 			 */
403 			if ((str = strrchr(crle.c_confil, '/')) == NULL)
404 				objdir->o_objdir =
405 				    (char *)MSG_ORIG(MSG_DIR_DOT);
406 			else {
407 				int	len = str - crle.c_confil;
408 
409 				if ((objdir->o_objdir =
410 				    malloc(len + 1)) == NULL) {
411 					int err = errno;
412 					(void) fprintf(stderr,
413 					    MSG_INTL(MSG_SYS_MALLOC),
414 					    crle.c_name, strerror(err));
415 					return (1);
416 				}
417 				(void) strncpy(objdir->o_objdir,
418 				    crle.c_confil, len);
419 				objdir->o_objdir[len] = '\0';
420 			}
421 		}
422 
423 		/*
424 		 * If we're going to dldump(3dl) images ourself make sure we
425 		 * can access any directories.
426 		 */
427 		if (crle.c_flags & CRLE_DUMP) {
428 			Objdir	*objdir = NULL;
429 			Aliste	idx;
430 			int	err = 0;
431 
432 			for (ALIST_TRAVERSE(objdirs, idx, objdir)) {
433 				if (crle.c_flags & CRLE_VERBOSE)
434 					(void) printf(MSG_INTL(MSG_DIA_OBJDIR),
435 					    objdir->o_objdir);
436 
437 				if ((objdir->o_flags & CRLE_DUMP) == 0)
438 					continue;
439 
440 				if (access(objdir->o_objdir,
441 				    (R_OK | W_OK)) != 0) {
442 					err = errno;
443 					(void) fprintf(stderr,
444 					    MSG_INTL(MSG_SYS_ACCESS),
445 					    crle.c_name, objdir->o_objdir,
446 					    strerror(err));
447 				}
448 			}
449 			if (err)
450 				return (1);
451 		}
452 	}
453 
454 	/*
455 	 * Establish any initial object directory.
456 	 */
457 	crle.c_objdir = iobjdir->o_objdir;
458 
459 	/*
460 	 * Create a temporary file name in which to build the configuration
461 	 * information.
462 	 */
463 	if ((crle.c_tempname = tempnam(MSG_ORIG(MSG_TMP_DIR),
464 	    MSG_ORIG(MSG_TMP_PFX))) == NULL) {
465 		int err = errno;
466 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TEMPNAME),
467 		    crle.c_name, strerror(err));
468 		return (1);
469 	}
470 	if ((crle.c_tempfd = open(crle.c_tempname, (O_RDWR | O_CREAT),
471 	    0666)) == -1) {
472 		int err = errno;
473 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
474 		    crle.c_name, crle.c_tempname, strerror(err));
475 		return (1);
476 	}
477 	if (stat(crle.c_tempname, &nstatus) != 0) {
478 		int err = errno;
479 		(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
480 		    crle.c_name, crle.c_tempname, strerror(err));
481 		return (1);
482 	}
483 	if (ostatus.st_dev != nstatus.st_dev)
484 		crle.c_flags |= CRLE_DIFFDEV;
485 
486 	/*
487 	 * Second pass.
488 	 */
489 	error = 0;
490 	optind = 1;
491 	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
492 		const char	*str;
493 		int		flag = 0;
494 
495 		switch (c) {
496 
497 		case '6':
498 			break;
499 
500 		case 'A':			/* alternative is optional */
501 			flag = RTC_OBJ_OPTINAL;
502 			/* FALLTHROUGH */
503 		case 'a':			/* alternative required */
504 			flag |= (RTC_OBJ_ALTER | RTC_OBJ_CMDLINE);
505 			if (inspect(&crle, (const char *)optarg, flag) != 0)
506 				error = 1;
507 			break;
508 
509 		case 'c':
510 			break;
511 
512 		case 'e':
513 			if ((flag = addenv(&crle, (const char *)optarg,
514 			    RTC_ENV_REPLACE)) == 0)
515 				error = 1;
516 			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
517 				(void) printf(MSG_INTL(MSG_DIA_RPLENV),
518 				    (const char *)optarg);
519 			break;
520 
521 		case 'E':
522 			if ((flag = addenv(&crle, (const char *)optarg,
523 			    RTC_ENV_PERMANT)) == 0)
524 				error = 1;
525 			else if ((crle.c_flags & CRLE_VERBOSE) && (flag == 1))
526 				(void) printf(MSG_INTL(MSG_DIA_PRMENV),
527 				    (const char *)optarg);
528 			break;
529 
530 		case 'f':
531 			break;
532 
533 		case 'G':			/* group object */
534 			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
535 			/* FALLTHROUGH */
536 		case 'g':
537 			flag |= (RTC_OBJ_GROUP | RTC_OBJ_CMDLINE);
538 			if (inspect(&crle, (const char *)optarg, flag) != 0)
539 				error = 1;
540 			break;
541 
542 		case 'I':			/* individual object */
543 			flag = (RTC_OBJ_DUMP | RTC_OBJ_ALTER);
544 			/* FALLTHROUGH */
545 		case 'i':
546 			flag |= RTC_OBJ_CMDLINE;
547 			if (inspect(&crle, (const char *)optarg, flag) != 0)
548 				error = 1;
549 			break;
550 
551 		case 'l':			/* library search path */
552 			str = MSG_ORIG(MSG_STR_ELF);
553 			lib = &crle.c_edlibpath;
554 			if (addlib(&crle, lib, (const char *)optarg) != 0)
555 				error = 1;
556 			else if (crle.c_flags & CRLE_VERBOSE)
557 				(void) printf(MSG_INTL(MSG_DIA_DLIBPTH),
558 				    str, (const char *)optarg);
559 			break;
560 
561 		case 'o':
562 			crle.c_objdir = optarg;
563 			break;
564 
565 		case 's':			/* trusted (secure) path */
566 			str = MSG_ORIG(MSG_STR_ELF);
567 			lib = &crle.c_eslibpath;
568 			if (addlib(&crle, lib, (const char *)optarg) != 0)
569 				error = 1;
570 			else if (crle.c_flags & CRLE_VERBOSE)
571 				(void) printf(MSG_INTL(MSG_DIA_TLIBPTH),
572 				    str, (const char *)optarg);
573 			break;
574 
575 		case 't':
576 			break;
577 
578 		case 'u':
579 			break;
580 
581 		case 'v':
582 			break;
583 		}
584 	}
585 
586 	/*
587 	 * Now that we've generated as many file/directory processing errors
588 	 * as we can, return if any fatal error conditions occurred.
589 	 */
590 	if (error) {
591 		(void) unlink(crle.c_tempname);
592 		if (crle.c_flags & CRLE_CREAT) {
593 			(void) fprintf(stderr, MSG_INTL(MSG_GEN_CREATE),
594 			    crle.c_name, crle.c_confil);
595 		}
596 		return (1);
597 	}
598 
599 	/*
600 	 * Create a temporary configuration file.
601 	 */
602 	if (genconfig(&crle) != 0) {
603 		(void) unlink(crle.c_tempname);
604 		return (1);
605 	}
606 
607 	/*
608 	 * If dldump(3dl) images are required spawn a process to create them.
609 	 */
610 	if (crle.c_flags & CRLE_DUMP) {
611 		if (dump(&crle) != 0) {
612 			(void) unlink(crle.c_tempname);
613 			return (1);
614 		}
615 	}
616 
617 	/*
618 	 * Copy the finished temporary configuration file to its final home.
619 	 */
620 	if (updateconfig(&crle) != 0)
621 		return (1);
622 
623 	return (0);
624 }
625