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
init_env()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
valid_env_var(const char * var,const restarter_inst_t * inst,const char * path)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 **
find_dup(const char * var,char ** env,const restarter_inst_t * inst)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 **
set_smf_env(char ** env,size_t env_sz,const char * path,const restarter_inst_t * inst,const char * method)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