xref: /illumos-gate/usr/src/cmd/svc/startd/env.c (revision b8aa3def2e2531e693fba6d1f00a74339a4a663d)
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  * Copyright 2021 OmniOS Community Edition (OmniOSce) Association.
25  */
26 
27 #include <assert.h>
28 #include <definit.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 **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 	void		*dstate;
61 	const char	*tokp;
62 	int		i;
63 
64 	glob_env_n = 16;
65 	glob_envp = startd_alloc(sizeof (*glob_envp) * glob_env_n);
66 
67 	glob_envp[0] = startd_alloc((unsigned)(strlen(DEF_PATH)+2));
68 	(void) strcpy(glob_envp[0], DEF_PATH);
69 
70 	if (definit_open(DEFINIT_DEFAULT_FILE, &dstate) != 0) {
71 		uu_warn("Cannot open %s. Environment not initialized.\n",
72 		    DEFINIT_DEFAULT_FILE);
73 
74 		glob_envp[1] = NULL;
75 		return;
76 	}
77 
78 	i = 1;
79 	while ((tokp = definit_token(dstate)) != NULL) {
80 		size_t length = strlen(tokp);
81 
82 		/*
83 		 * init already started us with this umask, and we
84 		 * handled it in startd.c, so just skip it.
85 		 */
86 		if (strncmp(tokp, "CMASK=", 6) == 0 ||
87 		    strncmp(tokp, "SMF_", 4) == 0) {
88 			continue;
89 		}
90 
91 		glob_envp[i] = startd_alloc((unsigned)(length + 1));
92 		(void) strcpy(glob_envp[i], tokp);
93 
94 		/*
95 		 * Double the environment size whenever it is
96 		 * full.
97 		 */
98 		if (++i == glob_env_n) {
99 			char **newp;
100 
101 			glob_env_n *= 2;
102 			newp = startd_alloc(sizeof (*glob_envp) * glob_env_n);
103 			(void) memcpy(newp, glob_envp,
104 			    sizeof (*glob_envp) * glob_env_n / 2);
105 			startd_free(glob_envp,
106 			    sizeof (*glob_envp) * glob_env_n / 2);
107 			glob_envp = newp;
108 		}
109 	}
110 
111 	/* Append a null pointer to the environment array to mark its end. */
112 	glob_envp[i] = NULL;
113 
114 	definit_close(dstate);
115 
116 	/*
117 	 * Get the zonename once; it is used to set SMF_ZONENAME for methods.
118 	 */
119 	(void) getzonenamebyid(getzoneid(), zonename, sizeof (zonename));
120 
121 }
122 
123 static int
124 valid_env_var(const char *var, const restarter_inst_t *inst, const char *path)
125 {
126 	char *cp = strchr(var, '=');
127 
128 	if (cp == NULL || cp == var) {
129 		if (inst != NULL)
130 			log_instance(inst, B_FALSE, "Invalid environment "
131 			    "variable \"%s\".", var);
132 		return (0);
133 	} else if (strncmp(var, "SMF_", 4) == 0) {
134 		if (inst != NULL)
135 			log_instance(inst, B_FALSE, "Invalid environment "
136 			    "variable \"%s\"; \"SMF_\" prefix is reserved.",
137 			    var);
138 		return (0);
139 	} else if (path != NULL && strncmp(var, "PATH=", 5) == 0) {
140 		return (0);
141 	}
142 
143 	return (1);
144 }
145 
146 static char **
147 find_dup(const char *var, char **env, const restarter_inst_t *inst)
148 {
149 	char **p;
150 	char *tmp;
151 
152 	for (p = env; *p != NULL; p++) {
153 		assert((tmp = strchr(*p, '=')) != NULL);
154 		tmp++;
155 		if (strncmp(*p, var, tmp - *p) == 0)
156 			break;
157 	}
158 
159 	if (*p == NULL)
160 		return (NULL);
161 
162 	/*
163 	 * The first entry in the array can be ignored when it is the
164 	 * default path.
165 	 */
166 	if (inst != NULL && p != env &&
167 	    strncmp(*p, DEF_PATH, strlen(DEF_PATH)) != 0) {
168 		log_instance(inst, B_FALSE, "Ignoring duplicate "
169 		    "environment variable \"%s\".", *p);
170 	}
171 
172 	return (p);
173 }
174 
175 /*
176  * Create an environment which is appropriate for spawning an SMF
177  * aware process. The new environment will consist of the values from
178  * the global environment as modified by the supplied (local) environment.
179  *
180  * In order to preserve the correctness of the new environment,
181  * various checks are performed on the local environment (init_env()
182  * is relied upon to ensure the global environment is correct):
183  *
184  * - All SMF_ entries are ignored. All SMF_ entries should be provided
185  *   by this function.
186  * - Duplicates in the entry are eliminated.
187  * - Malformed entries are eliminated.
188  *
189  * Detected errors are logged as warnings to the appropriate instance
190  * logfile, since a single bad entry should not be enough to prevent
191  * an SMF_ functional environment from being created. The faulty entry
192  * is then ignored when building the environment.
193  *
194  * If env is NULL, then the return is an environment which contains
195  * all default values.
196  *
197  * If "path" is non-NULL, it will silently over-ride any previous
198  * PATH environment variable.
199  *
200  * NB: The returned env and strings are allocated using startd_alloc().
201  */
202 char **
203 set_smf_env(char **env, size_t env_sz, const char *path,
204     const restarter_inst_t *inst, const char *method)
205 {
206 	char **nenv;
207 	char **p, **np;
208 	size_t nenv_size;
209 	size_t sz;
210 
211 	/*
212 	 * Max. of glob_env, env, four SMF_ variables,
213 	 * path, and terminating NULL.
214 	 */
215 	nenv_size = glob_env_n + env_sz + 4 + 1 + 1;
216 
217 	nenv = startd_zalloc(sizeof (char *) * nenv_size);
218 
219 	np = nenv;
220 
221 	if (path != NULL) {
222 		sz = strlen(path) + 1;
223 		*np = startd_alloc(sz);
224 		(void) strlcpy(*np, path, sz);
225 		np++;
226 	}
227 
228 	if (inst) {
229 		sz = sizeof ("SMF_FMRI=") + strlen(inst->ri_i.i_fmri);
230 		*np = startd_alloc(sz);
231 		(void) strlcpy(*np, "SMF_FMRI=", sz);
232 		(void) strlcat(*np, inst->ri_i.i_fmri, sz);
233 		np++;
234 	}
235 
236 	if (method) {
237 		sz = sizeof ("SMF_METHOD=") + strlen(method);
238 		*np = startd_alloc(sz);
239 		(void) strlcpy(*np, "SMF_METHOD=", sz);
240 		(void) strlcat(*np, method, sz);
241 		np++;
242 	}
243 
244 	sz = sizeof ("SMF_RESTARTER=") + strlen(SCF_SERVICE_STARTD);
245 	*np = startd_alloc(sz);
246 	(void) strlcpy(*np, "SMF_RESTARTER=", sz);
247 	(void) strlcat(*np, SCF_SERVICE_STARTD, sz);
248 	np++;
249 
250 	sz = sizeof ("SMF_ZONENAME=") + strlen(zonename);
251 	*np = startd_alloc(sz);
252 	(void) strlcpy(*np, "SMF_ZONENAME=", sz);
253 	(void) strlcat(*np, zonename, sz);
254 	np++;
255 
256 	for (p = glob_envp; *p != NULL; p++) {
257 		if (valid_env_var(*p, inst, path)) {
258 			sz = strlen(*p) + 1;
259 			*np = startd_alloc(sz);
260 			(void) strlcpy(*np, *p, sz);
261 			np++;
262 		}
263 	}
264 
265 	if (env) {
266 		for (p = env; *p != NULL; p++) {
267 			char **dup_pos;
268 
269 			if (!valid_env_var(*p, inst, path))
270 				continue;
271 
272 			if ((dup_pos = find_dup(*p, nenv, inst)) != NULL) {
273 				startd_free(*dup_pos, strlen(*dup_pos) + 1);
274 				sz = strlen(*p) + 1;
275 				*dup_pos = startd_alloc(sz);
276 				(void) strlcpy(*dup_pos, *p, sz);
277 			} else {
278 				sz = strlen(*p) + 1;
279 				*np = startd_alloc(sz);
280 				(void) strlcpy(*np, *p, sz);
281 				np++;
282 			}
283 		}
284 	}
285 	*np = NULL;
286 
287 	return (nenv);
288 }
289