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 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 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 ** 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 ** 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