xref: /illumos-gate/usr/src/cmd/svc/startd/env.c (revision ef2504f26d1ea5859db9838255bb63f488f1b050)
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 <assert.h>
27 #include <libuutil.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <zone.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 
35 #include "startd.h"
36 
37 /*
38  * This file contains functions for setting the environment for
39  * processes started by svc.startd.
40  */
41 
42 #define	MAXCMDL		512
43 #define	DEF_PATH	"PATH=/usr/sbin:/usr/bin"
44 
45 static char *ENVFILE	= "/etc/default/init"; /* Default env. */
46 
47 static char **glob_envp;	/* Array of environment strings */
48 static int glob_env_n;		/* Number of environment slots allocated. */
49 
50 static char zonename[ZONENAME_MAX];
51 
52 /*
53  * init_env()
54  *   A clone of the work init.c does to provide as much compatibility
55  *   for startup scripts as possible.
56  */
57 void
58 init_env()
59 {
60 	int	i;
61 	char	line[MAXCMDL];
62 	FILE	*fp;
63 	int	inquotes, length, wslength;
64 	char	*tokp, *cp1, *cp2;
65 	char	**newp;
66 
67 	glob_env_n = 16;
68 	glob_envp = startd_alloc(sizeof (*glob_envp) * glob_env_n);
69 
70 	glob_envp[0] = startd_alloc((unsigned)(strlen(DEF_PATH)+2));
71 	(void) strcpy(glob_envp[0], DEF_PATH);
72 
73 	if ((fp = fopen(ENVFILE, "r")) == NULL) {
74 		uu_warn("Cannot open %s. Environment not initialized.\n",
75 		    ENVFILE);
76 
77 		glob_envp[1] = NULL;
78 		return;
79 	}
80 
81 	i = 1;
82 
83 	while (fgets(line, MAXCMDL - 1, fp) != NULL) {
84 		/*
85 		 * Toss newline
86 		 */
87 		length = strlen(line);
88 		if (line[length - 1] == '\n')
89 			line[length - 1] = '\0';
90 
91 		/*
92 		 * Ignore blank or comment lines.
93 		 */
94 		if (line[0] == '#' || line[0] == '\0' ||
95 		    (wslength = strspn(line, " \t\n")) == strlen(line) ||
96 		    strchr(line, '#') == line + wslength)
97 			continue;
98 
99 		/*
100 		 * First make a pass through the line and change
101 		 * any non-quoted semi-colons to blanks so they
102 		 * will be treated as token separators below.
103 		 */
104 		inquotes = 0;
105 		for (cp1 = line; *cp1 != '\0'; cp1++) {
106 			if (*cp1 == '"') {
107 				if (inquotes == 0)
108 					inquotes = 1;
109 				else
110 					inquotes = 0;
111 			} else if (*cp1 == ';') {
112 				if (inquotes == 0)
113 					*cp1 = ' ';
114 			}
115 		}
116 
117 		/*
118 		 * Tokens within the line are separated by blanks
119 		 *  and tabs.  For each token in the line which
120 		 * contains a '=' we strip out any quotes and then
121 		 * stick the token in the environment array.
122 		 */
123 		if ((tokp = strtok(line, " \t")) == NULL)
124 			continue;
125 
126 		do {
127 			cp1 = strchr(tokp, '=');
128 			if (cp1 == NULL || cp1 == tokp)
129 				continue;
130 			length = strlen(tokp);
131 			while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) {
132 				for (cp2 = cp1; cp2 < &tokp[length]; cp2++)
133 					*cp2 = *(cp2 + 1);
134 				length--;
135 			}
136 
137 			/*
138 			 * init already started us with this umask, and we
139 			 * handled it in startd.c, so just skip it.
140 			 */
141 			if (strncmp(tokp, "CMASK=", 6) == 0 ||
142 			    strncmp(tokp, "SMF_", 4) == 0)
143 				continue;
144 
145 			glob_envp[i] = startd_alloc((unsigned)(length + 1));
146 			(void) strcpy(glob_envp[i], tokp);
147 
148 			/*
149 			 * Double the environment size whenever it is
150 			 * full.
151 			 */
152 			if (++i == glob_env_n) {
153 				glob_env_n *= 2;
154 				newp = startd_alloc(sizeof (*glob_envp) *
155 				    glob_env_n);
156 				(void) memcpy(newp, glob_envp,
157 				    sizeof (*glob_envp) * glob_env_n / 2);
158 				startd_free(glob_envp,
159 				    sizeof (*glob_envp) * glob_env_n / 2);
160 				glob_envp = newp;
161 			}
162 		} while ((tokp = strtok(NULL, " \t")) != NULL);
163 	}
164 
165 	startd_fclose(fp);
166 
167 	/* Append a null pointer to the environment array to mark its end. */
168 	glob_envp[i] = NULL;
169 
170 	/*
171 	 * Get the zonename once; it is used to set SMF_ZONENAME for methods.
172 	 */
173 	(void) getzonenamebyid(getzoneid(), zonename, sizeof (zonename));
174 
175 }
176 
177 static int
178 valid_env_var(const char *var, const restarter_inst_t *inst, const char *path)
179 {
180 	char *cp = strchr(var, '=');
181 
182 	if (cp == NULL || cp == var) {
183 		if (inst != NULL)
184 			log_instance(inst, B_FALSE, "Invalid environment "
185 			    "variable \"%s\".", var);
186 		return (0);
187 	} else if (strncmp(var, "SMF_", 4) == 0) {
188 		if (inst != NULL)
189 			log_instance(inst, B_FALSE, "Invalid environment "
190 			    "variable \"%s\"; \"SMF_\" prefix is reserved.",
191 			    var);
192 		return (0);
193 	} else if (path != NULL && strncmp(var, "PATH=", 5) == 0) {
194 		return (0);
195 	}
196 
197 	return (1);
198 }
199 
200 static char **
201 find_dup(const char *var, char **env, const restarter_inst_t *inst)
202 {
203 	char **p;
204 	char *tmp;
205 
206 	for (p = env; *p != NULL; p++) {
207 		assert((tmp = strchr(*p, '=')) != NULL);
208 		tmp++;
209 		if (strncmp(*p, var, tmp - *p) == 0)
210 			break;
211 	}
212 
213 	if (*p == NULL)
214 		return (NULL);
215 
216 	/*
217 	 * The first entry in the array can be ignored when it is the
218 	 * default path.
219 	 */
220 	if (inst != NULL && p != env &&
221 	    strncmp(*p, DEF_PATH, strlen(DEF_PATH)) != 0) {
222 		log_instance(inst, B_FALSE, "Ignoring duplicate "
223 		    "environment variable \"%s\".", *p);
224 	}
225 
226 	return (p);
227 }
228 
229 /*
230  * Create an environment which is appropriate for spawning an SMF
231  * aware process. The new environment will consist of the values from
232  * the global environment as modified by the supplied (local) environment.
233  *
234  * In order to preserve the correctness of the new environment,
235  * various checks are performed on the local environment (init_env()
236  * is relied upon to ensure the global environment is correct):
237  *
238  * - All SMF_ entries are ignored. All SMF_ entries should be provided
239  *   by this function.
240  * - Duplicates in the entry are eliminated.
241  * - Malformed entries are eliminated.
242  *
243  * Detected errors are logged as warnings to the appropriate instance
244  * logfile, since a single bad entry should not be enough to prevent
245  * an SMF_ functional environment from being created. The faulty entry
246  * is then ignored when building the environment.
247  *
248  * If env is NULL, then the return is an environment which contains
249  * all default values.
250  *
251  * If "path" is non-NULL, it will silently over-ride any previous
252  * PATH environment variable.
253  *
254  * NB: The returned env and strings are allocated using startd_alloc().
255  */
256 char **
257 set_smf_env(char **env, size_t env_sz, const char *path,
258     const restarter_inst_t *inst, const char *method)
259 {
260 	char **nenv;
261 	char **p, **np;
262 	size_t nenv_size;
263 	size_t sz;
264 
265 	/*
266 	 * Max. of glob_env, env, four SMF_ variables,
267 	 * path, and terminating NULL.
268 	 */
269 	nenv_size = glob_env_n + env_sz + 4 + 1 + 1;
270 
271 	nenv = startd_zalloc(sizeof (char *) * nenv_size);
272 
273 	np = nenv;
274 
275 	if (path != NULL) {
276 		sz = strlen(path) + 1;
277 		*np = startd_alloc(sz);
278 		(void) strlcpy(*np, path, sz);
279 		np++;
280 	}
281 
282 	if (inst) {
283 		sz = sizeof ("SMF_FMRI=") + strlen(inst->ri_i.i_fmri);
284 		*np = startd_alloc(sz);
285 		(void) strlcpy(*np, "SMF_FMRI=", sz);
286 		(void) strlcat(*np, inst->ri_i.i_fmri, sz);
287 		np++;
288 	}
289 
290 	if (method) {
291 		sz = sizeof ("SMF_METHOD=") + strlen(method);
292 		*np = startd_alloc(sz);
293 		(void) strlcpy(*np, "SMF_METHOD=", sz);
294 		(void) strlcat(*np, method, sz);
295 		np++;
296 	}
297 
298 	sz = sizeof ("SMF_RESTARTER=") + strlen(SCF_SERVICE_STARTD);
299 	*np = startd_alloc(sz);
300 	(void) strlcpy(*np, "SMF_RESTARTER=", sz);
301 	(void) strlcat(*np, SCF_SERVICE_STARTD, sz);
302 	np++;
303 
304 	sz = sizeof ("SMF_ZONENAME=") + strlen(zonename);
305 	*np = startd_alloc(sz);
306 	(void) strlcpy(*np, "SMF_ZONENAME=", sz);
307 	(void) strlcat(*np, zonename, sz);
308 	np++;
309 
310 	for (p = glob_envp; *p != NULL; p++) {
311 		if (valid_env_var(*p, inst, path)) {
312 			sz = strlen(*p) + 1;
313 			*np = startd_alloc(sz);
314 			(void) strlcpy(*np, *p, sz);
315 			np++;
316 		}
317 	}
318 
319 	if (env) {
320 		for (p = env; *p != NULL; p++) {
321 			char **dup_pos;
322 
323 			if (!valid_env_var(*p, inst, path))
324 				continue;
325 
326 			if ((dup_pos = find_dup(*p, nenv, inst)) != NULL) {
327 				startd_free(*dup_pos, strlen(*dup_pos) + 1);
328 				sz = strlen(*p) + 1;
329 				*dup_pos = startd_alloc(sz);
330 				(void) strlcpy(*dup_pos, *p, sz);
331 			} else {
332 				sz = strlen(*p) + 1;
333 				*np = startd_alloc(sz);
334 				(void) strlcpy(*np, *p, sz);
335 				np++;
336 			}
337 		}
338 	}
339 	*np = NULL;
340 
341 	return (nenv);
342 }
343