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
main(int argc,char ** argv,char ** envp)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;
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