diff options
author | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 |
---|---|---|
committer | William Hubbs <w.d.hubbs@gmail.com> | 2022-04-06 10:51:55 -0500 |
commit | 391d12db48754861b5cecac92ee3321597ee02c1 (patch) | |
tree | b42fad5a31ca342de7b7ecf1fb78784194c1400c /src/shared/misc.c | |
parent | 0efc1b133e4182bd53cde78153bd8b5cc2e99448 (diff) |
migrate fully to meson build system
- drop old build system
- move shared include and source files to common directory
- drop "rc-" prefix from shared include and source files
- move executable-specific code to individual directories under src
- adjust top-level .gitignore file for new build system
This closes #489.
Diffstat (limited to 'src/shared/misc.c')
-rw-r--r-- | src/shared/misc.c | 492 |
1 files changed, 492 insertions, 0 deletions
diff --git a/src/shared/misc.c b/src/shared/misc.c new file mode 100644 index 00000000..d8e4d5e6 --- /dev/null +++ b/src/shared/misc.c @@ -0,0 +1,492 @@ +/* + * misc.c + * misc functions + */ + +/* + * Copyright (c) 2007-2015 The OpenRC Authors. + * See the Authors file at the top-level directory of this distribution and + * https://github.com/OpenRC/openrc/blob/HEAD/AUTHORS + * + * This file is part of OpenRC. It is subject to the license terms in + * the LICENSE file found in the top-level directory of this + * distribution and at https://github.com/OpenRC/openrc/blob/HEAD/LICENSE + * This file may not be copied, modified, propagated, or distributed + * except according to the terms contained in the LICENSE file. + */ + +#include <sys/file.h> +#include <sys/types.h> +#include <sys/utsname.h> + +#ifdef __linux__ +# include <sys/sysinfo.h> +#endif + +#include <sys/time.h> +#include <ctype.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> + +#include "einfo.h" +#include "queue.h" +#include "rc.h" +#include "misc.h" +#include "version.h" + +extern char **environ; + +bool +rc_conf_yesno(const char *setting) +{ + return rc_yesno(rc_conf_value (setting)); +} + +static const char *const env_whitelist[] = { + "EERROR_QUIET", "EINFO_QUIET", + "IN_BACKGROUND", "IN_DRYRUN", "IN_HOTPLUG", + "RC_DEBUG", "RC_NODEPS", + "LANG", "LC_MESSAGES", "TERM", + "EINFO_COLOR", "EINFO_VERBOSE", + NULL +}; + +void +env_filter(void) +{ + RC_STRINGLIST *env_allow; + RC_STRINGLIST *profile; + RC_STRINGLIST *env_list; + RC_STRING *env; + char *e; + size_t i = 0; + + /* Add the user defined list of vars */ + env_allow = rc_stringlist_split(rc_conf_value("rc_env_allow"), " "); + /* + * If '*' is an entry in rc_env_allow, do nothing as we are to pass + * through all environment variables. + */ + if (rc_stringlist_find(env_allow, "*")) + return; + profile = rc_config_load(RC_PROFILE_ENV); + + /* Copy the env and work from this so we can manipulate it safely */ + env_list = rc_stringlist_new(); + while (environ && environ[i]) { + env = rc_stringlist_add(env_list, environ[i++]); + e = strchr(env->value, '='); + if (e) + *e = '\0'; + } + + TAILQ_FOREACH(env, env_list, entries) { + /* Check the whitelist */ + for (i = 0; env_whitelist[i]; i++) { + if (strcmp(env_whitelist[i], env->value) == 0) + break; + } + if (env_whitelist[i]) + continue; + + /* Check our user defined list */ + if (rc_stringlist_find(env_allow, env->value)) + continue; + + /* OK, not allowed! */ + unsetenv(env->value); + } + + /* Now add anything missing from the profile */ + TAILQ_FOREACH(env, profile, entries) { + e = strchr(env->value, '='); + *e = '\0'; + if (!getenv(env->value)) + setenv(env->value, e + 1, 1); + } + + rc_stringlist_free(env_list); + rc_stringlist_free(env_allow); + rc_stringlist_free(profile); +} + +void +env_config(void) +{ + size_t pplen = strlen(RC_PATH_PREFIX); + char *path; + char *p; + char *e; + size_t l; + struct utsname uts; + FILE *fp; + char *token; + char *np; + char *npp; + char *tok; + const char *sys = rc_sys(); + char *buffer = NULL; + size_t size = 0; + + /* Ensure our PATH is prefixed with the system locations first + for a little extra security */ + path = getenv("PATH"); + if (!path) + setenv("PATH", RC_PATH_PREFIX, 1); + else if (strncmp (RC_PATH_PREFIX, path, pplen) != 0) { + l = strlen(path) + pplen + 3; + e = p = xmalloc(sizeof(char) * l); + p += snprintf(p, l, "%s", RC_PATH_PREFIX); + + /* Now go through the env var and only add bits not in our + * PREFIX */ + while ((token = strsep(&path, ":"))) { + np = npp = xstrdup(RC_PATH_PREFIX); + while ((tok = strsep(&npp, ":"))) + if (strcmp(tok, token) == 0) + break; + if (!tok) + p += snprintf(p, l - (p - e), ":%s", token); + free (np); + } + *p++ = '\0'; + unsetenv("PATH"); + setenv("PATH", e, 1); + free(e); + } + + setenv("RC_VERSION", VERSION, 1); + setenv("RC_LIBEXECDIR", RC_LIBEXECDIR, 1); + setenv("RC_SVCDIR", RC_SVCDIR, 1); + setenv("RC_TMPDIR", RC_SVCDIR "/tmp", 1); + setenv("RC_BOOTLEVEL", RC_LEVEL_BOOT, 1); + e = rc_runlevel_get(); + setenv("RC_RUNLEVEL", e, 1); + free(e); + + if ((fp = fopen(RC_KRUNLEVEL, "r"))) { + if (getline(&buffer, &size, fp) != -1) { + l = strlen (buffer) - 1; + if (buffer[l] == '\n') + buffer[l] = 0; + setenv("RC_DEFAULTLEVEL", buffer, 1); + } + fclose(fp); + } else + setenv("RC_DEFAULTLEVEL", RC_LEVEL_DEFAULT, 1); + + free(buffer); + if (sys) + setenv("RC_SYS", sys, 1); + +#ifdef PREFIX + setenv("RC_PREFIX", RC_PREFIX, 1); +#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) + setenv("RC_UNAME", uts.sysname, 1); + + /* Be quiet or verbose as necessary */ + if (rc_conf_yesno("rc_quiet")) + setenv("EINFO_QUIET", "YES", 1); + if (rc_conf_yesno("rc_verbose")) + setenv("EINFO_VERBOSE", "YES", 1); + + errno = 0; + if ((!rc_conf_yesno("rc_color") && errno == 0) || + rc_conf_yesno("rc_nocolor")) + setenv("EINFO_COLOR", "NO", 1); +} + +int +signal_setup(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof (sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + return sigaction(sig, &sa, NULL); +} + +int +signal_setup_restart(int sig, void (*handler)(int)) +{ + struct sigaction sa; + + memset(&sa, 0, sizeof (sa)); + sigemptyset(&sa.sa_mask); + sa.sa_handler = handler; + sa.sa_flags = SA_RESTART; + return sigaction(sig, &sa, NULL); +} + +int +svc_lock(const char *applet) +{ + char *file = NULL; + int fd; + + xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); + fd = open(file, O_WRONLY | O_CREAT | O_NONBLOCK, 0664); + free(file); + if (fd == -1) + return -1; + if (flock(fd, LOCK_EX | LOCK_NB) == -1) { + eerror("Call to flock failed: %s", strerror(errno)); + close(fd); + return -1; + } + return fd; +} + +int +svc_unlock(const char *applet, int fd) +{ + char *file = NULL; + + xasprintf(&file, RC_SVCDIR "/exclusive/%s", applet); + close(fd); + unlink(file); + free(file); + return -1; +} + +pid_t +exec_service(const char *service, const char *arg) +{ + char *file, sfd[32]; + int fd; + pid_t pid = -1; + sigset_t full; + sigset_t old; + struct sigaction sa; + + fd = svc_lock(basename_c(service)); + if (fd == -1) + return -1; + + file = rc_service_resolve(service); + if (!exists(file)) { + rc_service_mark(service, RC_SERVICE_STOPPED); + svc_unlock(basename_c(service), fd); + free(file); + return 0; + } + snprintf(sfd, sizeof(sfd), "%d", fd); + + /* We need to block signals until we have forked */ + memset(&sa, 0, sizeof (sa)); + sa.sa_handler = SIG_DFL; + sigemptyset(&sa.sa_mask); + sigfillset(&full); + sigprocmask(SIG_SETMASK, &full, &old); + + if ((pid = fork()) == 0) { + /* Restore default handlers */ + sigaction(SIGCHLD, &sa, NULL); + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGQUIT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGWINCH, &sa, NULL); + + /* Unmask signals */ + sigprocmask(SIG_SETMASK, &old, NULL); + + /* Safe to run now */ + execl(file, file, "--lockfd", sfd, arg, (char *) NULL); + fprintf(stderr, "unable to exec `%s': %s\n", + file, strerror(errno)); + svc_unlock(basename_c(service), fd); + _exit(EXIT_FAILURE); + } + + if (pid == -1) { + fprintf(stderr, "fork: %s\n",strerror (errno)); + svc_unlock(basename_c(service), fd); + } else + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD, 0) | FD_CLOEXEC); + + sigprocmask(SIG_SETMASK, &old, NULL); + free(file); + return pid; +} + +int +parse_mode(mode_t *mode, char *text) +{ + char *p; + unsigned long l; + + /* Check for a numeric mode */ + if ((*text - '0') < 8) { + l = strtoul(text, &p, 8); + if (*p || l > 07777U) { + errno = EINVAL; + return -1; + } + *mode = (mode_t) l; + return 0; + } + + /* We currently don't check g+w type stuff */ + errno = EINVAL; + return -1; +} + +int +is_writable(const char *path) +{ + if (access(path, W_OK) == 0) + return 1; + + return 0; +} + +RC_DEPTREE * _rc_deptree_load(int force, int *regen) +{ + int fd; + int retval; + int serrno = errno; + int merrno; + time_t t; + char file[PATH_MAX]; + struct stat st; + struct utimbuf ut; + FILE *fp; + + t = 0; + if (rc_deptree_update_needed(&t, file) || force != 0) { + /* Test if we have permission to update the deptree */ + fd = open(RC_DEPTREE_CACHE, O_WRONLY); + merrno = errno; + errno = serrno; + if (fd == -1 && merrno == EACCES) + return rc_deptree_load(); + close(fd); + + if (regen) + *regen = 1; + ebegin("Caching service dependencies"); + retval = rc_deptree_update() ? 0 : -1; + eend (retval, "Failed to update the dependency tree"); + + if (retval == 0) { + if (stat(RC_DEPTREE_CACHE, &st) != 0) { + eerror("stat(%s): %s", RC_DEPTREE_CACHE, strerror(errno)); + return NULL; + } + if (st.st_mtime < t) { + eerror("Clock skew detected with `%s'", file); + eerrorn("Adjusting mtime of `" RC_DEPTREE_CACHE + "' to %s", ctime(&t)); + fp = fopen(RC_DEPTREE_SKEWED, "w"); + if (fp != NULL) { + fprintf(fp, "%s\n", file); + fclose(fp); + } + ut.actime = t; + ut.modtime = t; + utime(RC_DEPTREE_CACHE, &ut); + } else { + if (exists(RC_DEPTREE_SKEWED)) + unlink(RC_DEPTREE_SKEWED); + } + } + if (force == -1 && regen != NULL) + *regen = retval; + } + return rc_deptree_load(); +} + +static const struct { + const char * const name; + RC_SERVICE bit; +} service_bits[] = { + { "service_started", RC_SERVICE_STARTED, }, + { "service_stopped", RC_SERVICE_STOPPED, }, + { "service_inactive", RC_SERVICE_INACTIVE, }, + { "service_starting", RC_SERVICE_STARTING, }, + { "service_stopping", RC_SERVICE_STOPPING, }, + { "service_hotplugged", RC_SERVICE_HOTPLUGGED, }, + { "service_wasinactive", RC_SERVICE_WASINACTIVE, }, + { "service_failed", RC_SERVICE_FAILED, }, + { "service_crashed", RC_SERVICE_CRASHED, }, +}; + +RC_SERVICE lookup_service_state(const char *service) +{ + size_t i; + for (i = 0; i < ARRAY_SIZE(service_bits); ++i) + if (!strcmp(service, service_bits[i].name)) + return service_bits[i].bit; + return 0; +} + +void from_time_t(char *time_string, time_t tv) +{ + strftime(time_string, 20, "%Y-%m-%d %H:%M:%S", localtime(&tv)); +} + +time_t to_time_t(char *timestring) +{ + int check = 0; + int year = 0; + int month = 0; + int day = 0; + int hour = 0; + int min = 0; + int sec = 0; + struct tm breakdown = {0}; + time_t result = -1; + + check = sscanf(timestring, "%4d-%2d-%2d %2d:%2d:%2d", + &year, &month, &day, &hour, &min, &sec); + if (check == 6) { + breakdown.tm_year = year - 1900; /* years since 1900 */ + breakdown.tm_mon = month - 1; + breakdown.tm_mday = day; + breakdown.tm_hour = hour; + breakdown.tm_min = min; + breakdown.tm_sec = sec; + breakdown.tm_isdst = -1; + result = mktime(&breakdown); + } + return result; +} + +pid_t get_pid(const char *applet,const char *pidfile) +{ + FILE *fp; + pid_t pid; + + if (!pidfile) + return -1; + + if ((fp = fopen(pidfile, "r")) == NULL) { + ewarnv("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); + return -1; + } + + if (fscanf(fp, "%d", &pid) != 1) { + ewarnv("%s: no pid found in `%s'", applet, pidfile); + fclose(fp); + return -1; + } + + fclose(fp); + + return pid; +} |