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