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/*
27 * Utility functions
28 */
29#include <libintl.h>
30#include <stdio.h>
31#include <dlfcn.h>
32#include <string.h>
33#include <errno.h>
34#include <alloca.h>
35#include "sgs.h"
36#include "rtc.h"
37#include "_crle.h"
38#include "msg.h"
39
40/*
41 * Add an environment string.  A list of environment variable descriptors is
42 * maintained so that duplicate definitions can be caught, the first one wins.
43 */
44int
45addenv(Crle_desc *crle, const char *arg, unsigned int flags)
46{
47	Env_desc	*env;
48	char		*str;
49	size_t		varsz, totsz = strlen(arg) + 1;
50
51	/*
52	 * Determine "=" location so as to separated the variable name from
53	 * its value.
54	 */
55	if ((str = strchr(arg, '=')) != NULL) {
56		Aliste	idx;
57
58		varsz = (size_t)(str - arg);
59
60		/*
61		 * Traverse any existing environment variables to see if we've
62		 * caught a duplicate.
63		 */
64		for (APLIST_TRAVERSE(crle->c_env, idx, env)) {
65			if ((env->e_varsz == varsz) &&
66			    (strncmp(env->e_str, arg, varsz) == 0)) {
67				/*
68				 * If the user has already specified this string
69				 * given them a warning, and ignore the new one.
70				 */
71				if ((env->e_flags & RTC_ENV_CONFIG) == 0) {
72					(void) fprintf(stderr,
73					    MSG_INTL(MSG_WARN_ENV),
74					    crle->c_name, (int)varsz,
75					    env->e_str);
76					return (2);
77				}
78
79				/*
80				 * Otherwise the original string must have been
81				 * retrieved from a config file.  In this case
82				 * allow the user to override it.
83				 */
84				free((void *)env->e_str);
85				crle->c_strsize -= env->e_totsz;
86				crle->c_strsize += totsz;
87
88				if ((env->e_str = strdup(arg)) == 0) {
89					int err = errno;
90					(void) fprintf(stderr,
91					    MSG_INTL(MSG_SYS_MALLOC),
92					    crle->c_name, strerror(err));
93					return (0);
94				}
95				env->e_varsz = varsz;
96				env->e_totsz = totsz;
97				env->e_flags &= ~RTC_ENV_CONFIG;
98				env->e_flags |= flags;
99
100				return (1);
101			}
102		}
103	} else {
104		Aliste	idx;
105
106		/*
107		 * Although this is just a plain environment definition (no "=")
108		 * and probably has no effect on ld.so.1 anyway, we might as
109		 * well make sure we're not duplicating the same string.
110		 */
111		for (APLIST_TRAVERSE(crle->c_env, idx, env)) {
112			if (env->e_varsz)
113				continue;
114			if (strcmp(env->e_str, arg) == 0) {
115				if ((env->e_flags & RTC_ENV_CONFIG) == 0) {
116					(void) fprintf(stderr,
117					    MSG_INTL(MSG_WARN_ENV),
118					    crle->c_name, (int)totsz,
119					    env->e_str);
120					return (2);
121				}
122				env->e_flags &= ~RTC_ENV_CONFIG;
123				env->e_flags |= flags;
124
125				return (1);
126			}
127		}
128		varsz = 0;
129	}
130
131	/*
132	 * Allocate a new environment descriptor.
133	 */
134	if (((env = malloc(sizeof (Env_desc))) == 0) ||
135	    ((env->e_str = strdup(arg)) == 0)) {
136		int err = errno;
137		(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
138		    crle->c_name, strerror(err));
139		return (0);
140	}
141	env->e_varsz = varsz;
142	env->e_totsz = totsz;
143	env->e_flags = flags;
144
145	if (aplist_append(&(crle->c_env), env, AL_CNT_CRLE) == NULL)
146		return (0);
147
148	/*
149	 * Update the number of environment variables found, and the string
150	 * table requirement.
151	 */
152	crle->c_envnum++;
153	crle->c_strsize += totsz;
154
155	return (1);
156}
157
158/*
159 * Add a library path.  Multiple library paths are concatenated together into a
160 * colon separated string suitable for runtime processing.  These colon
161 * separated strings can also be passed in as arguments to addlib(), e.g.,
162 * -l /usr/lib:/usr/local/lib.  This is enabled to make update easier.
163 */
164int
165addlib(Crle_desc *crle, char **lib, const char *args)
166{
167	char		*str, *arg;
168	char		*lasts;
169	size_t		tlen = strlen(args) + 1;
170	const char	*colon = MSG_ORIG(MSG_STR_COLON);
171
172	/*
173	 * Parse the argument for any ":" separated elements.
174	 */
175	str = alloca(tlen);
176	(void) strcpy(str, args);
177	arg = str;
178
179	if ((arg = strtok_r(arg, colon, &lasts)) != NULL) {
180		do {
181			size_t	llen, alen = strlen(arg);
182
183			if (*lib) {
184				/*
185				 * Determine whether this argument exists in the
186				 * existing string buffer.
187				 */
188				if (((str = strstr(*lib, arg)) != NULL) &&
189				    (((str == *lib) ||
190				    (*(str - 1) == *colon)) &&
191				    (str += alen) &&
192				    ((*str == '\0') || (*str == *colon))))
193					continue;
194
195				llen = strlen(*lib);
196				tlen = llen + 1;
197			} else {
198				/*
199				 * This is the first argument to be added.
200				 */
201				llen = 0;
202				tlen = 0;
203			}
204
205			/*
206			 * This is a new string, so add it to the buffer.  If
207			 * this is the first occurrence of a string the size is
208			 * simply the size of the string + a trailing null.
209			 * Otherwise the size is the old string + ":" + the
210			 * size of the new string + a trailing null.
211			 */
212			alen += 1;
213			tlen += alen;
214			if ((str = realloc((void *)*lib, tlen)) == 0) {
215				int err = errno;
216				(void) fprintf(stderr, MSG_INTL(MSG_SYS_MALLOC),
217				    crle->c_name, strerror(err));
218				return (1);
219			}
220			if (llen == 0)
221				(void) strcpy(str, arg);
222			else {
223				/* LINTED */
224				(void) sprintf(&str[llen],
225				    MSG_ORIG(MSG_FMT_COLON), arg);
226			}
227			*lib = str;
228			crle->c_strsize += alen;
229
230		} while ((arg = strtok_r(NULL, colon, &lasts)) != NULL);
231	}
232
233	return (0);
234}
235
236
237/*
238 * -f option expansion.  Interpret its argument as a numeric or symbolic
239 * representation of the dldump(3dl) flags.
240 */
241int
242dlflags(Crle_desc *crle, const char *arg)
243{
244	int		_flags;
245	char		*tok, *_arg;
246	char		*lasts;
247	const char	*separate = MSG_ORIG(MSG_MOD_SEPARATE);
248
249	/*
250	 * Scan the argument looking for allowable tokens.  First determine if
251	 * the string is numeric, otherwise try and parse any known flags.
252	 */
253	if ((_flags = (int)strtol(arg, (char **)NULL, 0)) != 0)
254		return (_flags);
255
256	if ((_arg = malloc(strlen(arg) + 1)) == 0)
257		return (0);
258	(void) strcpy(_arg, arg);
259
260	if ((tok = strtok_r(_arg, separate, &lasts)) != NULL) {
261		/* BEGIN CSTYLED */
262		do {
263		    if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_RELATIVE)) == 0)
264			_flags |= RTLD_REL_RELATIVE;
265		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_EXEC)) == 0)
266			_flags |= RTLD_REL_EXEC;
267		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_DEPENDS)) == 0)
268			_flags |= RTLD_REL_DEPENDS;
269		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_PRELOAD)) == 0)
270			_flags |= RTLD_REL_PRELOAD;
271		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_SELF)) == 0)
272			_flags |= RTLD_REL_SELF;
273		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_WEAK)) == 0)
274			_flags |= RTLD_REL_WEAK;
275		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_ALL)) == 0)
276			_flags |= RTLD_REL_ALL;
277		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_MEMORY)) == 0)
278			_flags |= RTLD_MEMORY;
279		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_STRIP)) == 0)
280			_flags |= RTLD_STRIP;
281		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_NOHEAP)) == 0)
282			_flags |= RTLD_NOHEAP;
283		    else if (strcmp(tok, MSG_ORIG(MSG_MOD_REL_CONFGEN)) == 0)
284			_flags |= RTLD_CONFGEN;
285		    else {
286			(void) fprintf(stderr, MSG_INTL(MSG_ARG_FLAGS),
287			    crle->c_name, tok);
288			free(_arg);
289			return (0);
290		    }
291		} while ((tok = strtok_r(NULL, separate, &lasts)) != NULL);
292		/* END CSTYLED */
293	}
294	if (_flags == 0)
295		(void) fprintf(stderr, MSG_INTL(MSG_ARG_FLAGS),
296		    crle->c_name, arg);
297
298	free(_arg);
299	return (_flags);
300}
301
302/*
303 * Internationalization interface for sgsmsg(1l) use.
304 */
305const char *
306_crle_msg(Msg mid)
307{
308	return (gettext(MSG_ORIG(mid)));
309}
310