diff options
Diffstat (limited to 'src/rc/rc-misc.c')
-rw-r--r-- | src/rc/rc-misc.c | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/src/rc/rc-misc.c b/src/rc/rc-misc.c new file mode 100644 index 00000000..0d8b8c1f --- /dev/null +++ b/src/rc/rc-misc.c @@ -0,0 +1,353 @@ +/* + librc-misc.c + rc misc functions + */ + +/* + * Copyright 2007 Roy Marples + * All rights reserved + + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> + +#ifdef __linux__ +#include <sys/sysinfo.h> +#include <regex.h> +#endif + +#include <sys/utsname.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "rc.h" +#include "rc-misc.h" +#include "strlist.h" + +#define PROFILE_ENV "/etc/profile.env" +#define SYS_WHITELIST RC_LIBDIR "/conf.d/env_whitelist" +#define USR_WHITELIST "/etc/conf.d/env_whitelist" +#define RC_CONF "/etc/rc.conf" +#define RC_CONF_OLD "/etc/conf.d/rc" + +#define PATH_PREFIX RC_LIBDIR "/bin:/bin:/sbin:/usr/bin:/usr/sbin" + +static char **rc_conf = NULL; + +static void _free_rc_conf (void) +{ + rc_strlist_free (rc_conf); +} + +char *rc_conf_value (const char *setting) +{ + if (! rc_conf) { + char *line; + int i; + + rc_conf = rc_config_load (RC_CONF); + atexit (_free_rc_conf); + + /* Support old configs */ + if (exists (RC_CONF_OLD)) { + char **old = rc_config_load (RC_CONF_OLD); + rc_strlist_join (&rc_conf, old); + rc_strlist_free (old); + } + + /* Convert old uppercase to lowercase */ + STRLIST_FOREACH (rc_conf, line, i) { + char *p = line; + while (p && *p && *p != '=') { + if (isupper (*p)) + *p = tolower (*p); + p++; + } + } + } + + return (rc_config_value (rc_conf, setting)); +} + +bool rc_conf_yesno (const char *setting) +{ + return (rc_yesno (rc_conf_value (setting))); +} + +char **env_filter (void) +{ + char **env = NULL; + char **whitelist = NULL; + char *env_name = NULL; + char **profile = NULL; + int count = 0; + bool got_path = false; + char *env_var; + int env_len; + char *token; + char *sep; + char *e; + char *p; + int pplen = strlen (PATH_PREFIX); + + whitelist = rc_config_list (SYS_WHITELIST); + if (! whitelist) + fprintf (stderr, "system environment whitelist (" SYS_WHITELIST ") missing\n"); + + env = rc_config_list (USR_WHITELIST); + rc_strlist_join (&whitelist, env); + rc_strlist_free (env); + env = NULL; + + if (! whitelist) + return (NULL); + + if (exists (PROFILE_ENV)) + profile = rc_config_load (PROFILE_ENV); + + STRLIST_FOREACH (whitelist, env_name, count) { + char *space = strchr (env_name, ' '); + if (space) + *space = 0; + + env_var = getenv (env_name); + + if (! env_var && profile) { + env_len = strlen (env_name) + strlen ("export ") + 1; + p = xmalloc (sizeof (char) * env_len); + snprintf (p, env_len, "export %s", env_name); + env_var = rc_config_value (profile, p); + free (p); + } + + if (! env_var) + continue; + + /* Ensure our PATH is prefixed with the system locations first + for a little extra security */ + if (strcmp (env_name, "PATH") == 0 && + strncmp (PATH_PREFIX, env_var, pplen) != 0) + { + got_path = true; + env_len = strlen (env_name) + strlen (env_var) + pplen + 2; + e = p = xmalloc (sizeof (char) * env_len); + p += snprintf (e, env_len, "%s=%s", env_name, PATH_PREFIX); + + /* Now go through the env var and only add bits not in our PREFIX */ + sep = env_var; + while ((token = strsep (&sep, ":"))) { + char *np = xstrdup (PATH_PREFIX); + char *npp = np; + char *tok = NULL; + while ((tok = strsep (&npp, ":"))) + if (strcmp (tok, token) == 0) + break; + if (! tok) + p += snprintf (p, env_len - (p - e), ":%s", token); + free (np); + } + *p++ = 0; + } else { + env_len = strlen (env_name) + strlen (env_var) + 2; + e = xmalloc (sizeof (char) * env_len); + snprintf (e, env_len, "%s=%s", env_name, env_var); + } + + rc_strlist_add (&env, e); + free (e); + } + + /* We filtered the env but didn't get a PATH? Very odd. + However, we do need a path, so use a default. */ + if (! got_path) { + env_len = strlen ("PATH=") + strlen (PATH_PREFIX) + 2; + e = xmalloc (sizeof (char) * env_len); + snprintf (e, env_len, "PATH=%s", PATH_PREFIX); + rc_strlist_add (&env, e); + free (e); + } + + rc_strlist_free (whitelist); + rc_strlist_free (profile); + + return (env); +} + + /* Other systems may need this at some point, but for now it's Linux only */ +#ifdef __linux__ +static bool file_regex (const char *file, const char *regex) +{ + FILE *fp; + char *buffer; + regex_t re; + bool retval = false; + int result; + + if (! (fp = fopen (file, "r"))) + return (false); + + buffer = xmalloc (sizeof (char) * RC_LINEBUFFER); + if ((result = regcomp (&re, regex, REG_EXTENDED | REG_NOSUB)) != 0) { + fclose (fp); + regerror (result, &re, buffer, RC_LINEBUFFER); + fprintf (stderr, "file_regex: %s", buffer); + free (buffer); + return (false); + } + + while (fgets (buffer, RC_LINEBUFFER, fp)) { + if (regexec (&re, buffer, 0, NULL, 0) == 0) + { + retval = true; + break; + } + } + free (buffer); + fclose (fp); + regfree (&re); + + return (retval); +} +#endif + +char **env_config (void) +{ + char **env = NULL; + char *line; + int i; +#ifdef __linux__ + char sys[6]; +#endif + struct utsname uts; + FILE *fp; + char buffer[PATH_MAX]; + char *runlevel = rc_runlevel_get (); + char *p; + + /* One char less to drop the trailing / */ + i = strlen ("RC_LIBDIR=") + strlen (RC_LIBDIR) + 1; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_LIBDIR=" RC_LIBDIR); + rc_strlist_add (&env, line); + free (line); + + /* One char less to drop the trailing / */ + i = strlen ("RC_SVCDIR=") + strlen (RC_SVCDIR) + 1; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_SVCDIR=" RC_SVCDIR); + rc_strlist_add (&env, line); + free (line); + + rc_strlist_add (&env, "RC_BOOTLEVEL=" RC_LEVEL_BOOT); + + i = strlen ("RC_SOFTLEVEL=") + strlen (runlevel) + 1; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_SOFTLEVEL=%s", runlevel); + rc_strlist_add (&env, line); + free (line); + + if ((fp = fopen (RC_KSOFTLEVEL, "r"))) { + memset (buffer, 0, sizeof (buffer)); + if (fgets (buffer, sizeof (buffer), fp)) { + i = strlen (buffer) - 1; + if (buffer[i] == '\n') + buffer[i] = 0; + i += strlen ("RC_DEFAULTLEVEL=") + 2; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_DEFAULTLEVEL=%s", buffer); + rc_strlist_add (&env, line); + free (line); + } + fclose (fp); + } else + rc_strlist_add (&env, "RC_DEFAULTLEVEL=" RC_LEVEL_DEFAULT); + + +#ifdef __linux__ + /* Linux can run some funky stuff like Xen, VServer, UML, etc + We store this special system in RC_SYS so our scripts run fast */ + memset (sys, 0, sizeof (sys)); + + if (exists ("/proc/xen")) { + if ((fp = fopen ("/proc/xen/capabilities", "r"))) { + fclose (fp); + if (file_regex ("/proc/xen/capabilities", "control_d")) + snprintf (sys, sizeof (sys), "XEN0"); + } + if (! sys[0]) + snprintf (sys, sizeof (sys), "XENU"); + } else if (file_regex ("/proc/cpuinfo", "UML")) { + snprintf (sys, sizeof (sys), "UML"); + } else if (file_regex ("/proc/self/status", + "(s_context|VxID|envID):[[:space:]]*[1-9]")) + { + snprintf (sys, sizeof (sys), "VPS"); + } + + if (sys[0]) { + i = strlen ("RC_SYS=") + strlen (sys) + 2; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_SYS=%s", sys); + rc_strlist_add (&env, line); + free (line); + } + +#endif + + /* Some scripts may need to take a different code path if Linux/FreeBSD, etc + To save on calling uname, we store it in an environment variable */ + if (uname (&uts) == 0) { + i = strlen ("RC_UNAME=") + strlen (uts.sysname) + 2; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "RC_UNAME=%s", uts.sysname); + rc_strlist_add (&env, line); + free (line); + } + + /* Be quiet or verbose as necessary */ + if ((p = rc_conf_value ("rc_quiet"))) { + i = strlen ("EINFO_QUIET=") + strlen (p) + 1; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "EINFO_QUIET=%s", p); + rc_strlist_add (&env, line); + free (line); + } + if ((p = rc_conf_value ("rc_verbose"))) { + i = strlen ("EINFO_VERBOSE=") + strlen (p) + 1; + line = xmalloc (sizeof (char) * i); + snprintf (line, i, "EINFO_VERBOSE=%s", p); + rc_strlist_add (&env, line); + free (line); + } + + errno = 0; + if ((! rc_conf_yesno ("rc_color") && errno == 0) || + rc_conf_yesno ("rc_nocolor")) + rc_strlist_add (&env, "EINFO_COLOR=no"); + + free (runlevel); + return (env); +} |