Home | History | Annotate | Download | only in startd
      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