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