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