OpenSolaris_b135/cmd/sgs/crle/common/config.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include	<sys/mman.h>
#include	<sys/types.h>
#include	<fcntl.h>
#include	<unistd.h>
#include	<errno.h>
#include	<stdio.h>
#include	<string.h>
#include	"rtc.h"
#include	"_crle.h"
#include	"msg.h"


#define	MAXNBKTS 10007

static const int hashsize[] = {
	3,	7,	13,	31,	53,	67,	83,	97,
	101,	151,	211,	251,	307,	353,	401,	457,	503,
	557,	601,	653,	701,	751,	809,	859,	907,	953,
	1009,	1103,	1201,	1301,	1409,	1511,	1601,	1709,	1801,
	1901,	2003,	2111,	2203,	2309,	2411,	2503,	2609,	2707,
	2801,	2903,	3001,	3109,	3203,	3301,	3407,	3511,	3607,
	3701,	3803,	3907,	4001,	5003,   6101,   7001,   8101,   9001,
	MAXNBKTS
};

/*
 * Generate a configuration file from the internal configuration information.
 * (very link-editor like).
 */
int
genconfig(Crle_desc *crle)
{
	int		ndx, bkt;
	size_t		size, hashoff = 0, stroff = 0, objoff = 0;
	size_t		diroff = 0, fileoff = 0, envoff = 0;
	size_t		fltroff = 0, flteoff = 0;
	Addr		addr;
	Rtc_id		*id;
	Rtc_head	*head;
	Word		*hashtbl, *hashbkt, *hashchn, hashbkts = 0;
	char		*strtbl, *_strtbl;
	Rtc_obj		*objtbl;
	Rtc_dir		*dirtbl;
	Rtc_file	*filetbl;
	Rtc_env		*envtbl;
	Rtc_fltr	*fltrtbl;
	Rtc_flte	*fltetbl, * _fltetbl;
	Hash_tbl	*stbl = crle->c_strtbl;
	Hash_ent	*ent;

	/*
	 * Establish the size of the configuration file.
	 */
	size = S_ROUND(sizeof (Rtc_head), sizeof (Word));

	if (crle->c_hashstrnum) {
		hashoff = size;

		/*
		 * Increment the hash string number to account for an initial
		 * null entry.  Indexes start at 1 to simplify hash lookup.
		 */
		crle->c_hashstrnum++;

		/*
		 * Determine the hash table size.  Establish the number of
		 * buckets from the number of strings, the number of chains is
		 * equivalent to the number of objects, and two entries for the
		 * nbucket and nchain entries.
		 */
		for (ndx = 0; ndx < (sizeof (hashsize) / sizeof (int)); ndx++) {
			if (crle->c_hashstrnum > hashsize[ndx])
				continue;
			hashbkts = hashsize[ndx];
			break;
		}
		if (hashbkts == 0)
			hashbkts = MAXNBKTS;
		size += ((2 + hashbkts + crle->c_hashstrnum) * sizeof (Word));
		size = S_ROUND(size, sizeof (Lword));
		objoff = size;

		/*
		 * Add the object table size (account for an 8-byte alignment
		 * requirement for each object).
		 */
		size += (crle->c_hashstrnum *
		    S_ROUND(sizeof (Rtc_obj), sizeof (Lword)));

		/*
		 * Add the file descriptor arrays.
		 */
		fileoff = size;
		size += S_ROUND((crle->c_filenum * sizeof (Rtc_file)),
		    sizeof (Word));

		/*
		 * Add the directory descriptor array.
		 */
		diroff = size;
		size += S_ROUND((crle->c_dirnum * sizeof (Rtc_dir)),
		    sizeof (Word));
	}

	/*
	 * Add any environment string array (insure zero last entry).
	 */
	if (crle->c_envnum) {
		envoff = size;
		size += S_ROUND(((crle->c_envnum + 1) * sizeof (Rtc_env)),
		    sizeof (Word));
	}

	/*
	 * Add any filter/filtee association arrays (insure zero last entry for
	 * the filter array, the filtee arrays are already accounted for).
	 */
	if (crle->c_fltrnum) {
		fltroff = size;
		size += S_ROUND(((crle->c_fltrnum + 1) * sizeof (Rtc_fltr)),
		    sizeof (Word));
		flteoff = size;
		size += S_ROUND((crle->c_fltenum * sizeof (Rtc_flte)),
		    sizeof (Word));
	}

	/*
	 * Add the string table size (this may contain library and/or secure
	 * path strings, in addition to any directory/file strings).
	 */
	if (crle->c_strsize) {
		stroff = size;
		size += S_ROUND(crle->c_strsize, sizeof (Word));
	}

	/* Account for addition of Rtc_id block at the start */
	if (crle->c_flags & CRLE_ADDID)
		size += sizeof (Rtc_id);

	/*
	 * Truncate our temporary file now that we know its size and map it.
	 */
	if (ftruncate(crle->c_tempfd, size) == -1) {
		int err = errno;
		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
		    crle->c_name, crle->c_tempname, strerror(err));
		(void) close(crle->c_tempfd);
		return (1);
	}
	if ((addr = (Addr)mmap(0, size, (PROT_READ | PROT_WRITE), MAP_SHARED,
	    crle->c_tempfd, 0)) == (Addr)-1) {
		int err = errno;
		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MMAP),
		    crle->c_name, crle->c_tempname, strerror(err));
		(void) close(crle->c_tempfd);
		return (1);
	}

	/*
	 * Save the mapped files info for possible dldump(3dl) updates.
	 */
	crle->c_tempaddr = addr;
	crle->c_tempsize = size;

	/*
	 * Rtc_id goes at the top, followed by the Rtc_head. We base
	 * all offset calculations relative to Rtc_head, not from
	 * the top of the file. This eases backwards compatability to
	 * older versons that lacked the Rtc_id at the top.
	 */
	if (crle->c_flags & CRLE_ADDID) {
		/* The contents of the Rtc_id are all known at compile time */
		static const Rtc_id id_template = {
			RTC_ID_MAG0, RTC_ID_MAG1, RTC_ID_MAG2, RTC_ID_MAG3,
			M_CLASS, M_DATA, M_MACH,
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0 } };

		id = (Rtc_id *) addr;
		*id = id_template;	/* Fill in the Rtc_id data */
		addr += sizeof (Rtc_id);
	} else {
		id = NULL;
	}
	crle->c_tempheadaddr = addr;
	head = (Rtc_head *)addr;

	/*
	 * Establish the real address of each of the structures within the file.
	 */
	head->ch_hash = hashoff;
	/* LINTED */
	hashtbl = (Word *)(CAST_PTRINT(char *, head->ch_hash) + addr);

	head->ch_obj = objoff;
	/* LINTED */
	objtbl = (Rtc_obj *)(CAST_PTRINT(char *, head->ch_obj) + addr);
	objtbl = (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1), sizeof (Lword));

	head->ch_file = fileoff;
	/* LINTED */
	filetbl = (Rtc_file *)(CAST_PTRINT(char *, head->ch_file) + addr);

	head->ch_dir = diroff;
	/* LINTED */
	dirtbl = (Rtc_dir *)(CAST_PTRINT(char *, head->ch_dir) + addr);

	head->ch_env = envoff;
	/* LINTED */
	envtbl = (Rtc_env *)(CAST_PTRINT(char *, head->ch_env) + addr);

	head->ch_fltr = fltroff;
	/* LINTED */
	fltrtbl = (Rtc_fltr *)(CAST_PTRINT(char *, head->ch_fltr) + addr);
	head->ch_flte = flteoff;
	/* LINTED */
	fltetbl = _fltetbl = (Rtc_flte *)(CAST_PTRINT(char *, head->ch_flte) +
	    addr);

	head->ch_str = stroff;
	strtbl = _strtbl = (char *)(CAST_PTRINT(char *, head->ch_str) + addr);

	/*
	 * Fill in additional basic header information.
	 */
	head->ch_version = RTC_VER_CURRENT;

	if (crle->c_flags & CRLE_ALTER)
		head->ch_cnflags |= RTC_HDR_ALTER;
	if (crle->c_flags & CRLE_DUMP) {
		head->ch_cnflags |= RTC_HDR_IGNORE;
		head->ch_dlflags = crle->c_dlflags;
	}
#ifdef _ELF64
	head->ch_cnflags |= RTC_HDR_64;
#endif

#ifndef	SGS_PRE_UNIFIED_PROCESS
	head->ch_cnflags |= RTC_HDR_UPM;
#endif
	/*
	 * If we have a hash table then there are directory and file entries
	 * to process.
	 */
	if (crle->c_hashstrnum) {
		hashtbl[0] = hashbkts;
		hashtbl[1] = crle->c_hashstrnum;
		hashbkt = &hashtbl[2];
		hashchn = &hashtbl[2 + hashbkts];

		/*
		 * Insure all hash chain and directory/filename table entries
		 * are cleared.
		 */
		(void) memset(hashchn, 0, (crle->c_hashstrnum * sizeof (Word)));
		(void) memset(dirtbl, 0, (strtbl - (char *)dirtbl));

		/*
		 * Loop through the current string table list inspecting only
		 * directories.
		 */
		for (ndx = 1, bkt = 0; bkt < stbl->t_size; bkt++) {
			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
				Word		hashval;
				Hash_obj	*obj = ent->e_obj;
				char		*dir = (char *)ent->e_key;
				Rtc_dir		*_dirtbl;

				/*
				 * Skip any empty and non-directory entries.
				 */
				if ((obj == NULL) ||
				    ((obj->o_flags & RTC_OBJ_DIRENT) == 0))
					continue;

				/*
				 * Assign basic object attributes.
				 */
				objtbl->co_hash = ent->e_hash;
				objtbl->co_id = ent->e_id;
				objtbl->co_flags = obj->o_flags | ent->e_flags;
				objtbl->co_info = obj->o_info;

				ent->e_cobj = objtbl;

				/*
				 * Assign the directory name (from its key),
				 * and copy its name to the string table.
				 */
				objtbl->co_name = (Addr)(_strtbl - strtbl);
				(void) strcpy(_strtbl, dir);
				_strtbl += strlen(dir) + 1;

				/*
				 * Establish an entry in the directory table and
				 * reserve space for its associated filename
				 * entries (note, we add a trailing null file
				 * entry to simplify later inspection of the
				 * final configuration file.
				 */
				_dirtbl = &dirtbl[ent->e_id - 1];
				_dirtbl->cd_file =
				    CAST_PTRINT(Word, ((char *)filetbl- addr));
				_dirtbl->cd_obj =
				    CAST_PTRINT(Word, ((char *)objtbl - addr));

				/* LINTED */
				filetbl = (Rtc_file *)((char *)filetbl +
				    ((ent->e_cnt + 1) * sizeof (Rtc_file)));

				/*
				 * Add this object to the hash table.
				 */
				hashval = ent->e_hash % hashbkts;
				hashchn[ndx] = hashbkt[hashval];
				hashbkt[hashval] = ndx++;

				/*
				 * Increment Rt_obj pointer (make sure pointer
				 * falls on an 8-byte boundary).
				 */
				objtbl =
				    (Rtc_obj *)S_ROUND((uintptr_t)(objtbl + 1),
				    sizeof (Lword));
			}
		}

		/*
		 * Now collect all pathnames.  These are typically full
		 * pathnames, but may also be relative.  Simple filenames are
		 * recorded as offsets into these pathnames, thus we need to
		 * establish the new pathname first.
		 */
		for (bkt = 0; bkt < stbl->t_size; bkt++) {
			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
				Word		hashval;
				Hash_obj	*obj = ent->e_obj;
				char		*file = (char *)ent->e_key;
				char		*_str;
				Rtc_dir		*_dirtbl;
				Rtc_file	*_filetbl;
				int		_id;

				/*
				 * Skip empty and directory entries, and any
				 * simple filename entries.
				 */
				if ((obj == NULL) ||
				    (obj->o_flags & RTC_OBJ_DIRENT) ||
				    (ent->e_off))
					continue;

				/*
				 * Assign basic object attributes.
				 */
				objtbl->co_hash = ent->e_hash;
				objtbl->co_id = ent->e_id;
				objtbl->co_flags = obj->o_flags | ent->e_flags;
				objtbl->co_info = obj->o_info;

				ent->e_cobj = objtbl;

				/*
				 * Assign the file name (from its key),
				 * and copy its name to the string table.
				 */
				objtbl->co_name = (Addr)(_strtbl - strtbl);
				(void) strcpy(_strtbl, file);
				_strtbl += strlen(file) + 1;

				/*
				 * Add this file to its associated directory.
				 */
				_dirtbl = &dirtbl[ent->e_id - 1];
				/* LINTED */
				_filetbl = (Rtc_file *)(CAST_PTRINT(char *,
				    _dirtbl->cd_file) + addr);

				_id = --ent->e_dir->e_cnt;
				_filetbl[_id].cf_obj =
				    CAST_PTRINT(Word, ((char *)objtbl - addr));

				/*
				 * If object has an alternative, record it in
				 * the string table and assign the alternate
				 * pointer.  The new alternative offset is
				 * retained for reuse in other filename entries.
				 */
				if ((objtbl->co_flags & RTC_OBJ_ALTER) &&
				    (obj->o_calter == 0)) {
					_str = obj->o_alter;
					objtbl->co_alter = obj->o_calter =
					    (Addr)(_strtbl - strtbl);
					(void) strcpy(_strtbl, _str);
					_strtbl += strlen(_str) + 1;
				} else
					objtbl->co_alter = obj->o_calter;

				/*
				 * If object identifies the specific application
				 * for which this cache is relevant, record it
				 * in the header.
				 */
				if ((objtbl->co_flags &
				    (RTC_OBJ_APP | RTC_OBJ_REALPTH)) ==
				    (RTC_OBJ_APP | RTC_OBJ_REALPTH))
					head->ch_app = _filetbl[_id].cf_obj;

				/*
				 * Add this object to the hash table.
				 */
				hashval = ent->e_hash % hashbkts;
				hashchn[ndx] = hashbkt[hashval];
				hashbkt[hashval] = ndx++;

				/*
				 * Increment Rt_obj pointer (make sure pointer
				 * falls on an 8-byte boundary).
				 */
				objtbl = (Rtc_obj *)
				    S_ROUND((uintptr_t)(objtbl + 1),
				    sizeof (Lword));
			}
		}

		/*
		 * Finally pick off any simple filenames.
		 */
		for (bkt = 0; bkt < stbl->t_size; bkt++) {
			for (ent = stbl->t_entry[bkt]; ent; ent = ent->e_next) {
				Word		hashval;
				Hash_obj *	obj = ent->e_obj;
				Rtc_dir *	_dirtbl;
				Rtc_file *	_filetbl;
				int		_id;

				/*
				 * Skip everything except simple filenames.
				 */
				if (ent->e_off == 0)
					continue;

				/*
				 * Assign basic object attributes.
				 */
				objtbl->co_hash = ent->e_hash;
				objtbl->co_id = ent->e_id;
				objtbl->co_flags = obj->o_flags | ent->e_flags;
				objtbl->co_info = obj->o_info;
				objtbl->co_alter = obj->o_calter;

				ent->e_cobj = objtbl;

				/*
				 * Assign the file name from its full name.
				 */
				objtbl->co_name = (Addr)(CAST_PTRINT(char *,
				    ent->e_path->e_cobj->co_name) + ent->e_off);

				/*
				 * Add this file to its associated directory.
				 */
				_dirtbl = &dirtbl[ent->e_id - 1];
				/* LINTED */
				_filetbl = (Rtc_file *)
				    (CAST_PTRINT(char *, _dirtbl->cd_file) +
				    addr);

				_id = --ent->e_dir->e_cnt;
				_filetbl[_id].cf_obj =
				    CAST_PTRINT(Word, ((char *)objtbl - addr));

				/*
				 * Add this object to the hash table.
				 */
				hashval = ent->e_hash % hashbkts;
				hashchn[ndx] = hashbkt[hashval];
				hashbkt[hashval] = ndx++;

				/*
				 * Increment Rt_obj pointer (make sure pointer
				 * falls on an 8-byte boundary).
				 */
				objtbl = (Rtc_obj *)
				    S_ROUND((uintptr_t)(objtbl + 1),
				    sizeof (Lword));
			}
		}
	}

	/*
	 * Add any library, or secure path definitions.
	 */
	if (crle->c_edlibpath) {
		head->ch_edlibpath = head->ch_str + (_strtbl - strtbl);

		(void) strcpy(_strtbl, crle->c_edlibpath);
		_strtbl += strlen((char *)crle->c_edlibpath) + 1;
	} else
		head->ch_edlibpath = 0;

	if (crle->c_adlibpath) {
		head->ch_adlibpath = head->ch_str + (_strtbl - strtbl);

		(void) strcpy(_strtbl, crle->c_adlibpath);
		_strtbl += strlen((char *)crle->c_adlibpath) + 1;
	} else
		head->ch_adlibpath = 0;

	if (crle->c_eslibpath) {
		head->ch_eslibpath = head->ch_str + (_strtbl - strtbl);

		(void) strcpy(_strtbl, crle->c_eslibpath);
		_strtbl += strlen((char *)crle->c_eslibpath) + 1;
	} else
		head->ch_eslibpath = 0;

	if (crle->c_aslibpath) {
		head->ch_aslibpath = head->ch_str + (_strtbl - strtbl);

		(void) strcpy(_strtbl, crle->c_aslibpath);
		_strtbl += strlen((char *)crle->c_aslibpath) + 1;
	} else
		head->ch_aslibpath = 0;

	/*
	 * Add any environment variable entries.
	 */
	if (crle->c_envnum) {
		Env_desc	*env;
		Aliste		idx;

		for (APLIST_TRAVERSE(crle->c_env, idx, env)) {
			envtbl->env_str = head->ch_str + (_strtbl - strtbl);
			envtbl->env_flags = env->e_flags;

			(void) strcpy(_strtbl, env->e_str);
			_strtbl += env->e_totsz;

			envtbl++;
		}
		envtbl->env_str = 0;
		envtbl->env_flags = 0;
	}

	/*
	 * Add any filter/filtee association entries.
	 */
	if (crle->c_fltrnum) {
		Flt_desc	*flt;
		Aliste		idx1;

		for (APLIST_TRAVERSE(crle->c_flt, idx1, flt)) {
			Hash_ent	*flte;
			Aliste		idx2;

			/*
			 * Establish the filter name, and filtee string, as
			 * offsets into the configuration files string table.
			 * Establish the filtee as the offset into the filtee
			 * table.
			 */
			fltrtbl->fr_filter = flt->f_fent->e_cobj->co_name;
			fltrtbl->fr_string = _strtbl - strtbl;
			(void) strcpy(_strtbl, flt->f_str);
			_strtbl += flt->f_strsz;
			fltrtbl->fr_filtee = (Word)
			    ((uintptr_t)_fltetbl - (uintptr_t)fltetbl);

			for (APLIST_TRAVERSE(flt->f_filtee, idx2, flte)) {
				/*
				 * Establish the filtee name as the offset into
				 * the configuration files string table.
				 */
				_fltetbl->fe_filtee = flte->e_cobj->co_name;
				_fltetbl++;
			}
			_fltetbl->fe_filtee = 0;
			_fltetbl++, fltrtbl++;
		}
		fltrtbl->fr_filter = 0;
		fltrtbl->fr_filtee = 0;
	}

	/*
	 * Flush everything out.
	 */
	(void) close(crle->c_tempfd);
	if (msync((void *)crle->c_tempaddr, crle->c_tempsize, MS_ASYNC) == -1) {
		int err = errno;
		(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
		    crle->c_name, crle->c_tempname, strerror(err));
		return (1);
	}

	return (0);
}

/*
 * Update a configuration file.  If dldump()'ed images have been created then
 * the memory reservation of those images is added to the configuration file.
 * The temporary file is then moved into its final resting place.
 */
int
updateconfig(Crle_desc * crle)
{
	Rtc_head *head = (Rtc_head *)crle->c_tempheadaddr;

	if (crle->c_flags & CRLE_DUMP) {
		head->ch_cnflags &= ~RTC_HDR_IGNORE;

		if (msync((void *)crle->c_tempaddr, crle->c_tempsize,
		    MS_ASYNC) == -1) {
			int err = errno;
			(void) fprintf(stderr, MSG_INTL(MSG_SYS_TRUNC),
			    crle->c_name, crle->c_tempname, strerror(err));
			return (1);
		}
	}

	/*
	 * If an original configuration file exists, remove it.
	 */
	if (crle->c_flags & CRLE_EXISTS)
		(void) unlink(crle->c_confil);

	/*
	 * Move the config file to its final resting place.  If the two files
	 * exist on the same filesystem a rename is sufficient.
	 */
	if (crle->c_flags & CRLE_DIFFDEV) {
		int	fd;

		if ((fd = open(crle->c_confil, (O_RDWR | O_CREAT | O_TRUNC),
		    0666)) == -1) {
			int err = errno;
			(void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
			    crle->c_name, crle->c_confil, strerror(err));
			return (1);
		}
		if (write(fd, (void *)crle->c_tempaddr, crle->c_tempsize) !=
		    crle->c_tempsize) {
			int err = errno;
			(void) fprintf(stderr, MSG_INTL(MSG_SYS_WRITE),
			    crle->c_name, crle->c_confil, strerror(err));
			return (1);
		}
		(void) close(fd);
		(void) unlink(crle->c_tempname);
	} else
		(void) rename(crle->c_tempname, crle->c_confil);

	return (0);
}