crle.c revision 9131:d7741cc87056
1877Sache/*
276588Sjoerg * CDDL HEADER START
31019Sache *
4877Sache * The contents of this file are subject to the terms of the
51019Sache * Common Development and Distribution License (the "License").
61019Sache * You may not use this file except in compliance with the License.
71019Sache *
81019Sache * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91019Sache * or http://www.opensolaris.org/os/licensing.
101019Sache * See the License for the specific language governing permissions
111019Sache * and limitations under the License.
121019Sache *
13877Sache * When distributing Covered Code, include this CDDL HEADER in each
142838Sdg * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152838Sdg * If applicable, add the following below this CDDL HEADER, with the
162838Sdg * fields enclosed by brackets "[]" replaced with your own identifying
172838Sdg * information: Portions Copyright [yyyy] [name of copyright owner]
182838Sdg *
192838Sdg * CDDL HEADER END
202838Sdg */
212838Sdg/*
222838Sdg * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
232838Sdg * Use is subject to license terms.
242838Sdg */
252838Sdg
261817Sdg#include	<sys/types.h>
2750477Speter#include	<sys/stat.h>
28877Sache#include	<fcntl.h>
29877Sache#include	<stdio.h>
3018444Sbde#include	<string.h>
3118444Sbde#include	<unistd.h>
32877Sache#include	<locale.h>
3355205Speter#include	<dlfcn.h>
34877Sache#include	<errno.h>
3518444Sbde#include	"_crle.h"
3618444Sbde#include	"conv.h"
37877Sache#include	"msg.h"
38877Sache
39877Sache
40877Sache/*
41877Sache * crle(1) entry point and argument processing.
42877Sache *
43877Sache * Two passes of the arguments are carried out; the first collects any single
44877Sache * instance options and establishes defaults that might be appropriate for
4587992Sjoerg * other arguments:
46877Sache *
47134081Sphk *  -64		operate on, or apply, 64-bit objects (default is 32-bit).
48134081Sphk *
49134081Sphk *  -c file	defines the output configuration file.
50134081Sphk *
51134081Sphk *  -f flag	flags for dldump(3dl).
52134081Sphk *
53134081Sphk *  -o dir	defines the output directory for any dldump(3dl) objects
54134081Sphk *		that follow.  For backward compatibility (RTC_VER_ONE only
55134081Sphk * 		allowed one output directory) allow the first occurrence of this
56134081Sphk *		specification to catch any previous files.  If not specified,
57134081Sphk *		the configuration files parent directory is used).
58134081Sphk *
59877Sache *  -u		update any existing configuration file.  Any additional
60134081Sphk *		arguments supplied will be added to the new configuration
61134081Sphk *		information.
62134081Sphk *
63877Sache *  -v		verbose mode.
64134081Sphk *
65134081Sphk * The second pass collects all other options and constructs an internal
66134081Sphk * string table which will be used to create the eventual configuration file.
67134081Sphk *
68134081Sphk *  -a name	add the individual name, with an alternative to the
69877Sache *		configuration cache.  No alternative is created via dldump(3dl),
70877Sache *		it is the users responsibility to furnish the alternative.
71877Sache *
72877Sache *  -A name	add the individual name, with an optional alternative to the
73134081Sphk *		configuration cache.  No alternative is created via dldump(3dl),
74134081Sphk *		it is the users responsibility to furnish the alternative.
75134081Sphk *
76134081Sphk *  -e envar	replaceable environment variable
77877Sache *
78134081Sphk *  -E envar	permanent environment variable
79134081Sphk *
80134081Sphk *  -i name	add the individual name to the configuration cache.  If name
81134081Sphk *		is a directory each shared object within the directory is added
82877Sache *		to the cache.
83877Sache *
84877Sache *  -I name	same as -i, but in addition any ELF objects are dldump(3dl)'ed.
85877Sache *
86877Sache *  -g name	add the group name to the configuration cache.  Each object is
87877Sache * 		expanded to determine its dependencies and these are added to
8887992Sjoerg *		the cache.  If name is a directory each shared object within the
89877Sache *		directory and its dependencies are added to the cache.
90877Sache *
91877Sache *  -G app	same as -g, but in addition any ELF objects are dldump(3dl)'ed.
92891Sache *
93891Sache *  -l dir	library search directory
9487992Sjoerg *
9587992Sjoerg *  -s dir	trusted (secure) directory
9687992Sjoerg *
9787992Sjoerg *  -t type	search directory type (ELF or AOUT).
9887992Sjoerg */
99134081Sphk
100877Sache/*
101877Sache * Establish a structure for maintaining current object directory attributes.
10276588Sjoerg * We wish to validate the access of any object directory that will be written
10376588Sjoerg * to (dldump(3dl), and thus by maintaining a current object directory and its
10476588Sjoerg * intended use we can perform this validation later.
10576588Sjoerg */
10678856Sjoergtypedef struct {
10778856Sjoerg	char	*o_objdir;
10878856Sjoerg	uint_t	o_flags;
10978856Sjoerg} Objdir;
11078856Sjoerg
11178856Sjoerg/*ARGSUSED2*/
11278856Sjoergint
11378856Sjoergmain(int argc, char **argv, char **envp)
11478856Sjoerg{
11578856Sjoerg	Crle_desc	crle = { 0 };
11678856Sjoerg	int		c, error = 0;
11787992Sjoerg	char		**lib;
11887992Sjoerg	Alist		*objdirs = NULL;
11987992Sjoerg	Objdir		*objdir, *iobjdir;
12087992Sjoerg	struct stat	ostatus, nstatus;
12187992Sjoerg	int 		c_class;
12287992Sjoerg
12387992Sjoerg	if ((objdir = iobjdir = alist_append(&objdirs, NULL, sizeof (Objdir),
12487992Sjoerg	    AL_CNT_CRLE)) == NULL)
12587992Sjoerg		return (1);
12687992Sjoerg
127877Sache	/*
128877Sache	 * Establish locale.
1292838Sdg	 */
130877Sache	(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
1312838Sdg	(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
1322838Sdg
133134207Snyan	/*
134134207Snyan	 * Initialization configuration information.
135134207Snyan	 */
136134207Snyan	crle.c_name = argv[0];
1372838Sdg	crle.c_flags |= CRLE_ADDID;
13876588Sjoerg	crle.c_strbkts = 503;
1392838Sdg	crle.c_inobkts = 251;
14078856Sjoerg	c_class = M_CLASS;
14178856Sjoerg
1423095Sjoerg	/*
14376588Sjoerg	 * First argument pass.
14476588Sjoerg	 */
14576588Sjoerg	while ((c = getopt(argc, argv, MSG_ORIG(MSG_ARG_OPTIONS))) != -1) {
14687992Sjoerg		switch (c) {
14776588Sjoerg
14887992Sjoerg		case '6':			/* operate on 64-bit objects */
14987992Sjoerg			if (optarg[0] != '4') {
15076588Sjoerg				(void) fprintf(stderr,
15176588Sjoerg				    MSG_INTL(MSG_ARG_ILLEGAL), crle.c_name,
15276588Sjoerg				    MSG_ORIG(MSG_ARG_6), optarg);
15376588Sjoerg				error = 1;
15476588Sjoerg			}
155134081Sphk
15687992Sjoerg			c_class = ELFCLASS64;
15787992Sjoerg			break;
158134081Sphk
15976588Sjoerg		case 'A':			/* create optional */
16076588Sjoerg			/* FALLTHROUGH */	/*	alternative */
16187992Sjoerg		case 'a':			/* create alternative */
16287992Sjoerg			crle.c_flags |= (CRLE_CREAT | CRLE_ALTER);
16387992Sjoerg			objdir->o_flags |= (CRLE_CREAT | CRLE_ALTER);
16487992Sjoerg			break;
16587992Sjoerg
1663095Sjoerg		case 'c':			/* define the config file */
1673095Sjoerg			if (crle.c_confil) {
1683095Sjoerg				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
1693095Sjoerg				    crle.c_name, MSG_ORIG(MSG_ARG_C));
17087992Sjoerg				error = 1;
1713095Sjoerg			}
172134081Sphk			crle.c_confil = optarg;
173134081Sphk			break;
174134081Sphk
175134081Sphk		case 'e':			/* replaceable env variable */
176134081Sphk			crle.c_flags |= (CRLE_RPLENV | CRLE_CREAT);
177134081Sphk			break;
178134081Sphk
179134081Sphk		case 'E':			/* permanent env variable */
180134081Sphk			crle.c_flags |= (CRLE_PRMENV | CRLE_CREAT);
181134081Sphk			break;
182134081Sphk
183134081Sphk		case 'f':			/* dldump(3dl) flags */
184134081Sphk			if (crle.c_dlflags) {
185134081Sphk				(void) fprintf(stderr, MSG_INTL(MSG_ARG_MULT),
186134081Sphk				    crle.c_name, MSG_ORIG(MSG_ARG_F));
187134081Sphk				error = 1;
188134081Sphk			}
189134081Sphk			if ((crle.c_dlflags = dlflags(&crle,
190134081Sphk			    (const char *)optarg)) == 0)
191134081Sphk				error = 1;
192134081Sphk			break;
193134081Sphk
194134081Sphk		case 'G':			/* group object */
195134081Sphk			crle.c_flags |= (CRLE_DUMP | CRLE_ALTER);
196134081Sphk			objdir->o_flags |= (CRLE_DUMP | CRLE_ALTER);
197134081Sphk			/* FALLTHROUGH */
198134081Sphk		case 'g':
199134081Sphk			crle.c_flags |= CRLE_CREAT;
200134081Sphk			objdir->o_flags |= CRLE_CREAT;
201134081Sphk			break;
20218444Sbde
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