xref: /illumos-gate/usr/src/cmd/svc/startd/env.c (revision 4de2612967d06c4fdbf524a62556a1e8118a006f)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <libuutil.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.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 /*
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 static int
172 valid_env_var(const char *var, const restarter_inst_t *inst, const char *path)
173 {
174 	char *cp = strchr(var, '=');
175 
176 	if (cp == NULL || cp == var) {
177 		if (inst != NULL)
178 			log_instance(inst, B_FALSE, "Invalid environment "
179 			    "variable \"%s\".", var);
180 		return (0);
181 	} else if (strncmp(var, "SMF_", 4) == 0) {
182 		if (inst != NULL)
183 			log_instance(inst, B_FALSE, "Invalid environment "
184 			    "variable \"%s\"; \"SMF_\" prefix is reserved.",
185 			    var);
186 		return (0);
187 	} else if (path != NULL && strncmp(var, "PATH=", 5) == 0) {
188 		return (0);
189 	}
190 
191 	return (1);
192 }
193 
194 static char **
195 find_dup(const char *var, char **env, const restarter_inst_t *inst)
196 {
197 	char **p;
198 	char *tmp;
199 
200 	for (p = env; *p != NULL; p++) {
201 		assert((tmp = strchr(*p, '=')) != NULL);
202 		tmp++;
203 		if (strncmp(*p, var, tmp - *p) == 0)
204 			break;
205 	}
206 
207 	if (*p == NULL)
208 		return (NULL);
209 
210 	if (inst != NULL)
211 		log_instance(inst, B_FALSE, "Ignoring duplicate "
212 		    "environment variable \"%s\".", *p);
213 	return (p);
214 }
215 
216 /*
217  * Create an environment which is appropriate for spawning an SMF
218  * aware process. The new environment will consist of the values from
219  * the global environment as modified by the supplied (local) environment.
220  *
221  * In order to preserve the correctness of the new environment,
222  * various checks are performed on the local environment (init_env()
223  * is relied upon to ensure the global environment is correct):
224  *
225  * - All SMF_ entries are ignored. All SMF_ entries should be provided
226  *   by this function.
227  * - Duplicates in the entry are eliminated.
228  * - Malformed entries are eliminated.
229  *
230  * Detected errors are logged as warnings to the appropriate instance
231  * logfile, since a single bad entry should not be enough to prevent
232  * an SMF_ functional environment from being created. The faulty entry
233  * is then ignored when building the environment.
234  *
235  * If env is NULL, then the return is an environment which contains
236  * all default values.
237  *
238  * If "path" is non-NULL, it will silently over-ride any previous
239  * PATH environment variable.
240  *
241  * NB: The returned env and strings are allocated using startd_alloc().
242  */
243 char **
244 set_smf_env(char **env, size_t env_sz, const char *path,
245     const restarter_inst_t *inst, const char *method)
246 {
247 	char **nenv;
248 	char **p, **np;
249 	size_t nenv_size;
250 	size_t sz;
251 
252 	/*
253 	 * Max. of glob_env, env, three SMF_ variables,
254 	 * path, and terminating NULL.
255 	 */
256 	nenv_size = glob_env_n + env_sz + 3 + 1 + 1;
257 
258 	nenv = startd_zalloc(sizeof (char *) * nenv_size);
259 
260 	np = nenv;
261 
262 	if (path != NULL) {
263 		sz = strlen(path) + 1;
264 		*np = startd_alloc(sz);
265 		(void) strlcpy(*np, path, sz);
266 		np++;
267 	}
268 
269 
270 	if (inst) {
271 		sz = sizeof ("SMF_FMRI=") + strlen(inst->ri_i.i_fmri);
272 		*np = startd_alloc(sz);
273 		(void) strlcpy(*np, "SMF_FMRI=", sz);
274 		(void) strlcat(*np, inst->ri_i.i_fmri, sz);
275 		np++;
276 	}
277 
278 	if (method) {
279 		sz = sizeof ("SMF_METHOD=") + strlen(method);
280 		*np = startd_alloc(sz);
281 		(void) strlcpy(*np, "SMF_METHOD=", sz);
282 		(void) strlcat(*np, method, sz);
283 		np++;
284 	}
285 
286 	sz = sizeof ("SMF_RESTARTER=") + strlen(SCF_SERVICE_STARTD);
287 	*np = startd_alloc(sz);
288 	(void) strlcpy(*np, "SMF_RESTARTER=", sz);
289 	(void) strlcat(*np, SCF_SERVICE_STARTD, sz);
290 	np++;
291 
292 	for (p = glob_envp; *p != NULL; p++) {
293 		if (valid_env_var(*p, inst, path)) {
294 			sz = strlen(*p) + 1;
295 			*np = startd_alloc(sz);
296 			(void) strlcpy(*np, *p, sz);
297 			np++;
298 		}
299 	}
300 
301 	if (env) {
302 		for (p = env; *p != NULL; p++) {
303 			char **dup_pos;
304 
305 			if (!valid_env_var(*p, inst, path))
306 				continue;
307 
308 			if ((dup_pos = find_dup(*p, nenv, inst)) != NULL) {
309 				startd_free(*dup_pos, strlen(*dup_pos) + 1);
310 				sz = strlen(*p) + 1;
311 				*dup_pos = startd_alloc(sz);
312 				(void) strlcpy(*dup_pos, *p, sz);
313 			} else {
314 				sz = strlen(*p) + 1;
315 				*np = startd_alloc(sz);
316 				(void) strlcpy(*np, *p, sz);
317 				np++;
318 			}
319 		}
320 	}
321 	*np = NULL;
322 
323 	return (nenv);
324 }
325