#include <linux/kd.h>
#include <dlfcn.h>
#include <sys/wait.h>
+#include <sys/capability.h>
+#include <sys/time.h>
+#include <linux/rtc.h>
#include "macro.h"
#include "util.h"
#include "strv.h"
#include "label.h"
#include "exit-status.h"
+#include "hashmap.h"
+
+size_t page_size(void) {
+ static __thread size_t pgsz = 0;
+ long r;
+
+ if (pgsz)
+ return pgsz;
+
+ assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
+
+ pgsz = (size_t) r;
+
+ return pgsz;
+}
bool streq_ptr(const char *a, const char *b) {
return ts;
}
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+ assert(ts);
+
+ ts->realtime = u;
+
+ if (u == 0)
+ ts->monotonic = 0;
+ else {
+ delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+ }
+
+ return ts;
+}
+
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
for (;;) {
int r;
- if ((r = close(fd)) >= 0)
+ r = close(fd);
+ if (r >= 0)
return r;
if (errno != EINTR)
- return r;
+ return -errno;
}
}
}
int parse_pid(const char *s, pid_t* ret_pid) {
- unsigned long ul;
+ unsigned long ul = 0;
pid_t pid;
int r;
int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
int r;
FILE *f;
- char fn[132], line[256], *p;
+ char fn[PATH_MAX], line[LINE_MAX], *p;
long unsigned ppid;
- assert(pid >= 0);
+ assert(pid > 0);
assert(_ppid);
assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
- fn[sizeof(fn)-1] = 0;
+ char_array_0(fn);
if (!(f = fopen(fn, "r")))
return -errno;
return 0;
}
+int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
+ int r;
+ FILE *f;
+ char fn[PATH_MAX], line[LINE_MAX], *p;
+
+ assert(pid > 0);
+ assert(st);
+
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
+ char_array_0(fn);
+
+ if (!(f = fopen(fn, "r")))
+ return -errno;
+
+ if (!(fgets(line, sizeof(line), f))) {
+ r = -errno;
+ fclose(f);
+ return r;
+ }
+
+ fclose(f);
+
+ /* Let's skip the pid and comm fields. The latter is enclosed
+ * in () but does not escape any () in its value, so let's
+ * skip over it manually */
+
+ if (!(p = strrchr(line, ')')))
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%*d " /* tty_nr */
+ "%*d " /* tpgid */
+ "%*u " /* flags */
+ "%*u " /* minflt */
+ "%*u " /* cminflt */
+ "%*u " /* majflt */
+ "%*u " /* cmajflt */
+ "%*u " /* utime */
+ "%*u " /* stime */
+ "%*d " /* cutime */
+ "%*d " /* cstime */
+ "%*d " /* priority */
+ "%*d " /* nice */
+ "%*d " /* num_threads */
+ "%*d " /* itrealvalue */
+ "%llu " /* starttime */,
+ st) != 1)
+ return -EIO;
+
+ return 0;
+}
+
int write_one_line_file(const char *fn, const char *line) {
FILE *f;
int r;
if (!(f = fopen(fn, "we")))
return -errno;
+ errno = 0;
if (fputs(line, f) < 0) {
r = -errno;
goto finish;
}
- r = 0;
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else
+ r = 0;
+
+finish:
+ fclose(f);
+ return r;
+}
+
+int fchmod_umask(int fd, mode_t m) {
+ mode_t u;
+ int r;
+
+ u = umask(0777);
+ r = fchmod(fd, m & (~u)) < 0 ? -errno : 0;
+ umask(u);
+
+ return r;
+}
+
+int write_one_line_file_atomic(const char *fn, const char *line) {
+ FILE *f;
+ int r;
+ char *p;
+
+ assert(fn);
+ assert(line);
+
+ r = fopen_temporary(fn, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ if (fputs(line, f) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else {
+ if (rename(p, fn) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
finish:
+ if (r < 0)
+ unlink(p);
+
fclose(f);
+ free(p);
+
return r;
}
goto finish;
}
+ truncate_nl(c);
+
*line = c;
r = 0;
return r;
}
-int read_full_file(const char *fn, char **contents) {
+int read_full_file(const char *fn, char **contents, size_t *size) {
FILE *f;
int r;
size_t n, l;
goto finish;
}
+ /* Safety check */
+ if (st.st_size > 4*1024*1024) {
+ r = -E2BIG;
+ goto finish;
+ }
+
n = st.st_size > 0 ? st.st_size : LINE_MAX;
l = 0;
*contents = buf;
buf = NULL;
+ if (size)
+ *size = l;
+
r = 0;
finish:
assert(fname);
assert(separator);
- if ((r = read_full_file(fname, &contents)) < 0)
+ if ((r = read_full_file(fname, &contents, NULL)) < 0)
return r;
p = contents;
return r;
}
+int load_env_file(
+ const char *fname,
+ char ***rl) {
+
+ FILE *f;
+ char **m = 0;
+ int r;
+
+ assert(fname);
+ assert(rl);
+
+ if (!(f = fopen(fname, "re")))
+ return -errno;
+
+ while (!feof(f)) {
+ char l[LINE_MAX], *p, *u;
+ char **t;
+
+ if (!fgets(l, sizeof(l), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ goto finish;
+ }
+
+ p = strstrip(l);
+
+ if (!*p)
+ continue;
+
+ if (strchr(COMMENTS, *p))
+ continue;
+
+ if (!(u = normalize_env_assignment(p))) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ t = strv_append(m, u);
+ free(u);
+
+ if (!t) {
+ log_error("Out of memory");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ strv_free(m);
+ m = t;
+ }
+
+ r = 0;
+
+ *rl = m;
+ m = NULL;
+
+finish:
+ if (f)
+ fclose(f);
+
+ strv_free(m);
+
+ return r;
+}
+
+int write_env_file(const char *fname, char **l) {
+ char **i, *p;
+ FILE *f;
+ int r;
+
+ r = fopen_temporary(fname, &f, &p);
+ if (r < 0)
+ return r;
+
+ fchmod_umask(fileno(f), 0644);
+
+ errno = 0;
+ STRV_FOREACH(i, l) {
+ fputs(*i, f);
+ fputc('\n', f);
+ }
+
+ fflush(f);
+
+ if (ferror(f)) {
+ if (errno != 0)
+ r = -errno;
+ else
+ r = -EIO;
+ } else {
+ if (rename(p, fname) < 0)
+ r = -errno;
+ else
+ r = 0;
+ }
+
+ if (r < 0)
+ unlink(p);
+
+ fclose(f);
+ free(p);
+
+ return r;
+}
+
char *truncate_nl(char *s) {
assert(s);
if (r < 0)
return r;
- truncate_nl(*name);
return 0;
}
return l;
}
+char **strv_path_remove_empty(char **l) {
+ char **f, **t;
+
+ if (!l)
+ return NULL;
+
+ for (f = t = l; *f; f++) {
+
+ if (dir_is_empty(*f) > 0) {
+ free(*f);
+ continue;
+ }
+
+ *(t++) = *f;
+ }
+
+ *t = NULL;
+ return l;
+}
+
int reset_all_signal_handlers(void) {
int sig;
if (ignore_file(de->d_name))
continue;
- if ((r = safe_atoi(de->d_name, &fd)) < 0)
- goto finish;
+ if (safe_atoi(de->d_name, &fd) < 0)
+ /* Let's better ignore this, just in case */
+ continue;
if (fd < 3)
continue;
continue;
}
- if ((r = close_nointr(fd)) < 0) {
+ if (close_nointr(fd) < 0) {
/* Valgrind has its own FD and doesn't want to have it closed */
- if (errno != EBADF)
- goto finish;
+ if (errno != EBADF && r == 0)
+ r = -errno;
}
}
- r = 0;
-
-finish:
closedir(d);
return r;
}
int chvt(int vt) {
int fd, r = 0;
- if ((fd = open("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ if ((fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
return -errno;
if (vt < 0) {
int read_one_char(FILE *f, char *ret, bool *need_nl) {
struct termios old_termios, new_termios;
char c;
- char line[1024];
+ char line[LINE_MAX];
assert(f);
assert(ret);
}
}
-int reset_terminal(int fd) {
+int reset_terminal_fd(int fd) {
struct termios termios;
int r = 0;
long arg;
return r;
}
+int reset_terminal(const char *name) {
+ int fd, r;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = reset_terminal_fd(fd);
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
int open_terminal(const char *name, int mode) {
int fd, r;
+ unsigned c = 0;
+
+ /*
+ * If a TTY is in the process of being closed opening it might
+ * cause EIO. This is horribly awful, but unlikely to be
+ * changed in the kernel. Hence we work around this problem by
+ * retrying a couple of times.
+ *
+ * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245
+ */
- if ((fd = open(name, mode)) < 0)
+ for (;;) {
+ if ((fd = open(name, mode)) >= 0)
+ break;
+
+ if (errno != EIO)
+ return -errno;
+
+ if (c >= 20)
+ return -errno;
+
+ usleep(50 * USEC_PER_MSEC);
+ c++;
+ }
+
+ if (fd < 0)
return -errno;
if ((r = isatty(fd)) < 0) {
pollfd.events = POLLIN;
for (;;) {
- char buf[1024];
+ char buf[LINE_MAX];
ssize_t l;
int r;
/* We pass here O_NOCTTY only so that we can check the return
* value TIOCSCTTY and have a reliable way to figure out if we
* successfully became the controlling process of the tty */
- if ((fd = open_terminal(name, O_RDWR|O_NOCTTY)) < 0)
- return -errno;
+ if ((fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC)) < 0)
+ return fd;
/* First, try to get the tty */
r = ioctl(fd, TIOCSCTTY, force);
if (notify >= 0)
close_nointr_nofail(notify);
- if ((r = reset_terminal(fd)) < 0)
+ if ((r = reset_terminal_fd(fd)) < 0)
log_warning("Failed to reset terminal: %s", strerror(-r));
return fd;
return 0;
}
-bool is_clean_exit(int code, int status) {
-
- if (code == CLD_EXITED)
- return status == 0;
-
- /* If a daemon does not implement handlers for some of the
- * signals that's not considered an unclean shutdown */
- if (code == CLD_KILLED)
- return
- status == SIGHUP ||
- status == SIGINT ||
- status == SIGTERM ||
- status == SIGPIPE;
-
- return false;
-}
-
-bool is_clean_exit_lsb(int code, int status) {
+int make_null_stdio(void) {
+ int null_fd;
- if (is_clean_exit(code, status))
- return true;
+ if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+ return -errno;
- return
- code == CLD_EXITED &&
- (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+ return make_stdio(null_fd);
}
bool is_device_path(const char *path) {
return name;
}
-int getttyname_malloc(char **r) {
- char path[PATH_MAX], *p, *c;
+int getttyname_malloc(int fd, char **r) {
+ char path[PATH_MAX], *c;
int k;
assert(r);
- if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0)
+ if ((k = ttyname_r(fd, path, sizeof(path))) != 0)
return -k;
char_array_0(path);
- p = path;
- if (startswith(path, "/dev/"))
- p += 5;
-
- if (!(c = strdup(p)))
+ if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path)))
return -ENOMEM;
*r = c;
return 0;
}
-static int rm_rf_children(int fd, bool only_dirs) {
- DIR *d;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on */
+int getttyname_harder(int fd, char **r) {
+ int k;
+ char *s;
- if (!(d = fdopendir(fd))) {
- close_nointr_nofail(fd);
+ if ((k = getttyname_malloc(fd, &s)) < 0)
+ return k;
- return errno == ENOENT ? 0 : -errno;
+ if (streq(s, "tty")) {
+ free(s);
+ return get_ctty(0, NULL, r);
}
- for (;;) {
- struct dirent buf, *de;
- bool is_dir;
- int r;
+ *r = s;
+ return 0;
+}
- if ((r = readdir_r(d, &buf, &de)) != 0) {
- if (ret == 0)
- ret = -r;
- break;
- }
+int get_ctty_devnr(pid_t pid, dev_t *d) {
+ int k;
+ char line[LINE_MAX], *p, *fn;
+ unsigned long ttynr;
+ FILE *f;
- if (!de)
- break;
+ if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
+ return -ENOMEM;
+
+ f = fopen(fn, "re");
+ free(fn);
+ if (!f)
+ return -errno;
+
+ if (!fgets(line, sizeof(line), f)) {
+ k = -errno;
+ fclose(f);
+ return k;
+ }
+
+ fclose(f);
+
+ p = strrchr(line, ')');
+ if (!p)
+ return -EIO;
+
+ p++;
+
+ if (sscanf(p, " "
+ "%*c " /* state */
+ "%*d " /* ppid */
+ "%*d " /* pgrp */
+ "%*d " /* session */
+ "%lu ", /* ttynr */
+ &ttynr) != 1)
+ return -EIO;
+
+ *d = (dev_t) ttynr;
+ return 0;
+}
+
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
+ int k;
+ char fn[PATH_MAX], *s, *b, *p;
+ dev_t devnr;
+
+ assert(r);
+
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
+ return k;
+
+ snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
+ char_array_0(fn);
+
+ if ((k = readlink_malloc(fn, &s)) < 0) {
+
+ if (k != -ENOENT)
+ return k;
+
+ /* This is an ugly hack */
+ if (major(devnr) == 136) {
+ if (asprintf(&b, "pts/%lu", (unsigned long) minor(devnr)) < 0)
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+ }
+
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
+
+ if (!(b = strdup(fn + 5)))
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+ }
+
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
+
+ b = strdup(p);
+ free(s);
+
+ if (!b)
+ return -ENOMEM;
+
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
+
+ return 0;
+}
+
+static int rm_rf_children(int fd, bool only_dirs) {
+ DIR *d;
+ int ret = 0;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on */
+
+ if (!(d = fdopendir(fd))) {
+ close_nointr_nofail(fd);
+
+ return errno == ENOENT ? 0 : -errno;
+ }
+
+ for (;;) {
+ struct dirent buf, *de;
+ bool is_dir;
+ int r;
+
+ if ((r = readdir_r(d, &buf, &de)) != 0) {
+ if (ret == 0)
+ ret = -r;
+ break;
+ }
+
+ if (!de)
+ break;
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
}
void status_welcome(void) {
+ char *pretty_name = NULL, *ansi_color = NULL;
+ const char *const_pretty = NULL, *const_color = NULL;
+ int r;
-#if defined(TARGET_FEDORA)
- char *r;
+ if ((r = parse_env_file("/etc/os-release", NEWLINE,
+ "PRETTY_NAME", &pretty_name,
+ "ANSI_COLOR", &ansi_color,
+ NULL)) < 0) {
- if (read_one_line_file("/etc/system-release", &r) < 0)
- return;
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+ }
- truncate_nl(r);
+#if defined(TARGET_FEDORA)
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) {
- /* This tries to mimic the color magic the old Red Hat sysinit
- * script did. */
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/system-release: %s", strerror(-r));
+ }
+ }
- if (startswith(r, "Red Hat"))
- status_printf("Welcome to \x1B[0;31m%s\x1B[0m!\n", r); /* Red for RHEL */
- else if (startswith(r, "Fedora"))
- status_printf("Welcome to \x1B[0;34m%s\x1B[0m!\n", r); /* Blue for Fedora */
- else
- status_printf("Welcome to %s!\n", r);
+ if (!ansi_color && pretty_name) {
- free(r);
+ /* This tries to mimic the color magic the old Red Hat sysinit
+ * script did. */
+
+ if (startswith(pretty_name, "Red Hat"))
+ const_color = "0;31"; /* Red for RHEL */
+ else if (startswith(pretty_name, "Fedora"))
+ const_color = "0;34"; /* Blue for Fedora */
+ }
#elif defined(TARGET_SUSE)
- char *r;
- if (read_one_line_file("/etc/SuSE-release", &r) < 0)
- return;
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) {
- truncate_nl(r);
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r));
+ }
+ }
- status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */
- free(r);
+ if (!ansi_color)
+ const_color = "0;32"; /* Green for openSUSE */
#elif defined(TARGET_GENTOO)
- char *r;
- if (read_one_line_file("/etc/gentoo-release", &r) < 0)
- return;
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r));
+ }
+ }
- truncate_nl(r);
+ if (!ansi_color)
+ const_color = "1;34"; /* Light Blue for Gentoo */
- status_printf("Welcome to \x1B[1;34m%s\x1B[0m!\n", r); /* Light Blue for Gentoo */
+#elif defined(TARGET_ALTLINUX)
+
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r));
+ }
+ }
+
+ if (!ansi_color)
+ const_color = "0;36"; /* Cyan for ALTLinux */
- free(r);
#elif defined(TARGET_DEBIAN)
- char *r;
- if (read_one_line_file("/etc/debian_version", &r) < 0)
- return;
+ if (!pretty_name) {
+ char *version;
- truncate_nl(r);
+ if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) {
- status_printf("Welcome to Debian \x1B[1;31m%s\x1B[0m!\n", r); /* Light Red for Debian */
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/debian_version: %s", strerror(-r));
+ } else {
+ pretty_name = strappend("Debian ", version);
+ free(version);
+
+ if (!pretty_name)
+ log_warning("Failed to allocate Debian version string.");
+ }
+ }
+
+ if (!ansi_color)
+ const_color = "1;31"; /* Light Red for Debian */
- free(r);
#elif defined(TARGET_UBUNTU)
- char *desc = NULL;
- char *codename = NULL;
- if (parse_env_file("/etc/lsb-release", NEWLINE,
- "DISTRIB_DESCRIPTION", &desc,
- "DISTRIB_CODENAME", &codename, NULL) < 0)
- return;
- if (desc && codename)
- /* Light Red for Ubuntu */
- status_printf("Welcome to \x1B[1;31m%s\x1B[0m (%s)\n",
- desc, codename);
- free(desc);
- free(codename);
-#elif defined(TARGET_ARCH)
- status_printf("Welcome to \x1B[1;36mArch Linux\x1B[0m!\n"); /* Cyan for Arch */
-#else
-#warning "You probably should add a welcome text logic here."
+ if ((r = parse_env_file("/etc/lsb-release", NEWLINE,
+ "DISTRIB_DESCRIPTION", &pretty_name,
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/lsb-release: %s", strerror(-r));
+ }
+
+ if (!ansi_color)
+ const_color = "0;33"; /* Orange/Brown for Ubuntu */
+
+#elif defined(TARGET_MANDRIVA)
+
+ if (!pretty_name) {
+ char *s, *p;
+
+ if ((r = read_one_line_file("/etc/mandriva-release", &s) < 0)) {
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/mandriva-release: %s", strerror(-r));
+ } else {
+ p = strstr(s, " release ");
+ if (p) {
+ *p = '\0';
+ p += 9;
+ p[strcspn(p, " ")] = '\0';
+
+ /* This corresponds to standard rc.sysinit */
+ if (asprintf(&pretty_name, "%s\x1B[0;39m %s", s, p) > 0)
+ const_color = "1;36";
+ else
+ log_warning("Failed to allocate Mandriva version string.");
+ } else
+ log_warning("Failed to parse /etc/mandriva-release");
+ free(s);
+ }
+ }
+#elif defined(TARGET_MEEGO)
+
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/meego-release", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/meego-release: %s", strerror(-r));
+ }
+ }
+
+ if (!ansi_color)
+ const_color = "1;35"; /* Bright Magenta for MeeGo */
#endif
+
+ if (!pretty_name && !const_pretty)
+ const_pretty = "Linux";
+
+ if (!ansi_color && !const_color)
+ const_color = "1";
+
+ status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n",
+ const_color ? const_color : ansi_color,
+ const_pretty ? const_pretty : pretty_name);
+
+ free(ansi_color);
+ free(pretty_name);
}
char *replace_env(const char *format, char **env) {
assert(path);
- if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0)
+ if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0644)) < 0)
return -errno;
close_nointr_nofail(fd);
return strdup(s);
}
+char *normalize_env_assignment(const char *s) {
+ char *name, *value, *p, *r;
+
+ p = strchr(s, '=');
+
+ if (!p) {
+ if (!(r = strdup(s)))
+ return NULL;
+
+ return strstrip(r);
+ }
+
+ if (!(name = strndup(s, p - s)))
+ return NULL;
+
+ if (!(p = strdup(p+1))) {
+ free(name);
+ return NULL;
+ }
+
+ value = unquote(strstrip(p), QUOTES);
+ free(p);
+
+ if (!value) {
+ free(name);
+ return NULL;
+ }
+
+ if (asprintf(&r, "%s=%s", name, value) < 0)
+ r = NULL;
+
+ free(value);
+ free(name);
+
+ return r;
+}
+
int wait_for_terminate(pid_t pid, siginfo_t *status) {
assert(pid >= 1);
assert(status);
if (status.si_code == CLD_EXITED) {
if (status.si_status != 0) {
log_warning("%s failed with error code %i.", name, status.si_status);
- return -EPROTO;
+ return status.si_status;
}
log_debug("%s succeeded.", name);
}
void freeze(void) {
+
+ /* Make sure nobody waits for us on a socket anymore */
+ close_all_fds(NULL, 0);
+
+ sync();
+
for (;;)
pause();
}
return false;
}
-DIR *xopendirat(int fd, const char *name) {
- return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+DIR *xopendirat(int fd, const char *name, int flags) {
+ int nfd;
+ DIR *d;
+
+ if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+ return NULL;
+
+ if (!(d = fdopendir(nfd))) {
+ close_nointr_nofail(nfd);
+ return NULL;
+ }
+
+ return d;
}
int signal_from_string_try_harder(const char *s) {
return signo;
}
-int ask_password_tty(const char *message, usec_t until, const char *flag_file, char **_passphrase) {
- struct termios old_termios, new_termios;
- char passphrase[LINE_MAX];
- size_t p = 0;
- int r, ttyfd = -1, notify = -1;
- struct pollfd pollfd[2];
- bool reset_tty = false;
- enum {
- POLL_TTY,
- POLL_INOTIFY
- };
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
- assert(message);
- assert(_passphrase);
+ assert(f);
+ assert(name);
+ assert(t);
- if (flag_file) {
- if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
- r = -errno;
- goto finish;
- }
+ if (!dual_timestamp_is_set(t))
+ return;
- if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) {
- r = -errno;
- goto finish;
- }
- }
+ fprintf(f, "%s=%llu %llu\n",
+ name,
+ (unsigned long long) t->realtime,
+ (unsigned long long) t->monotonic);
+}
- if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) {
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+ unsigned long long a, b;
- if (tcgetattr(ttyfd, &old_termios) < 0) {
- r = -errno;
- goto finish;
- }
+ assert(value);
+ assert(t);
- loop_write(ttyfd, "\x1B[1m", 4, false);
- loop_write(ttyfd, message, strlen(message), false);
- loop_write(ttyfd, ": ", 2, false);
- loop_write(ttyfd, "\x1B[0m", 4, false);
+ if (sscanf(value, "%lli %llu", &a, &b) != 2)
+ log_debug("Failed to parse finish timestamp value %s", value);
+ else {
+ t->realtime = a;
+ t->monotonic = b;
+ }
+}
- new_termios = old_termios;
- new_termios.c_lflag &= ~(ICANON|ECHO);
- new_termios.c_cc[VMIN] = 1;
- new_termios.c_cc[VTIME] = 0;
+char *fstab_node_to_udev_node(const char *p) {
+ char *dn, *t, *u;
+ int r;
- if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) {
- r = -errno;
- goto finish;
- }
+ /* FIXME: to follow udev's logic 100% we need to leave valid
+ * UTF8 chars unescaped */
- reset_tty = true;
- }
+ if (startswith(p, "LABEL=")) {
- zero(pollfd);
+ if (!(u = unquote(p+6, "\"\'")))
+ return NULL;
- pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO;
- pollfd[POLL_TTY].events = POLLIN;
- pollfd[POLL_INOTIFY].fd = notify;
- pollfd[POLL_INOTIFY].events = POLLIN;
+ t = xescape(u, "/ ");
+ free(u);
- for (;;) {
- char c;
- int sleep_for = -1, k;
- ssize_t n;
+ if (!t)
+ return NULL;
- if (until > 0) {
- usec_t y;
+ r = asprintf(&dn, "/dev/disk/by-label/%s", t);
+ free(t);
- y = now(CLOCK_MONOTONIC);
+ if (r < 0)
+ return NULL;
- if (y > until) {
- r = -ETIMEDOUT;
- goto finish;
- }
+ return dn;
+ }
- sleep_for = (int) ((until - y) / USEC_PER_MSEC);
- }
+ if (startswith(p, "UUID=")) {
- if (flag_file)
- if (access(flag_file, F_OK) < 0) {
- r = -errno;
- goto finish;
- }
+ if (!(u = unquote(p+5, "\"\'")))
+ return NULL;
- if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) {
+ t = xescape(u, "/ ");
+ free(u);
- if (errno == EINTR)
- continue;
+ if (!t)
+ return NULL;
- r = -errno;
- goto finish;
- } else if (k == 0) {
- r = -ETIMEDOUT;
- goto finish;
- }
+ r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
+ free(t);
- if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0)
- flush_fd(notify);
+ if (r < 0)
+ return NULL;
- if (pollfd[POLL_TTY].revents == 0)
- continue;
+ return dn;
+ }
- if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) {
+ return strdup(p);
+}
- if (errno == EINTR || errno == EAGAIN)
- continue;
+void filter_environ(const char *prefix) {
+ int i, j;
+ assert(prefix);
- r = -errno;
- goto finish;
+ if (!environ)
+ return;
- } else if (n == 0)
- break;
+ for (i = 0, j = 0; environ[i]; i++) {
- if (c == '\n')
- break;
- else if (c == 21) {
+ if (startswith(environ[i], prefix))
+ continue;
- while (p > 0) {
- p--;
+ environ[j++] = environ[i];
+ }
- if (ttyfd >= 0)
- loop_write(ttyfd, "\b \b", 3, false);
- }
+ environ[j] = NULL;
+}
- } else if (c == '\b' || c == 127) {
- if (p > 0) {
- p--;
+bool tty_is_vc(const char *tty) {
+ assert(tty);
- if (ttyfd >= 0)
- loop_write(ttyfd, "\b \b", 3, false);
- }
- } else {
- passphrase[p++] = c;
+ if (startswith(tty, "/dev/"))
+ tty += 5;
- if (ttyfd >= 0)
- loop_write(ttyfd, "*", 1, false);
- }
- }
+ return vtnr_from_tty(tty) >= 0;
+}
- if (ttyfd >= 0)
- loop_write(ttyfd, "\n", 1, false);
+int vtnr_from_tty(const char *tty) {
+ int i, r;
- passphrase[p] = 0;
+ assert(tty);
- if (!(*_passphrase = strdup(passphrase))) {
- r = -ENOMEM;
- goto finish;
- }
+ if (startswith(tty, "/dev/"))
+ tty += 5;
- r = 0;
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
-finish:
- if (notify >= 0)
- close_nointr_nofail(notify);
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
- if (ttyfd >= 0) {
- if (reset_tty)
- tcsetattr(ttyfd, TCSADRAIN, &old_termios);
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
- close_nointr_nofail(ttyfd);
- }
+ if (i < 0 || i > 63)
+ return -EINVAL;
- return r;
+ return i;
+}
+
+const char *default_term_for_tty(const char *tty) {
+ char *active = NULL;
+ const char *term;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ /* Resolve where /dev/console is pointing when determining
+ * TERM */
+ if (streq(tty, "console"))
+ if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
+ /* If multiple log outputs are configured the
+ * last one is what /dev/console points to */
+ if ((tty = strrchr(active, ' ')))
+ tty++;
+ else
+ tty = active;
+ }
+
+ term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100";
+ free(active);
+
+ return term;
+}
+
+/* Returns a short identifier for the various VM implementations */
+int detect_vm(const char **id) {
+
+#if defined(__i386__) || defined(__x86_64__)
+
+ /* Both CPUID and DMI are x86 specific interfaces... */
+
+ static const char *const dmi_vendors[] = {
+ "/sys/class/dmi/id/sys_vendor",
+ "/sys/class/dmi/id/board_vendor",
+ "/sys/class/dmi/id/bios_vendor"
+ };
+
+ static const char dmi_vendor_table[] =
+ "QEMU\0" "qemu\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMware\0" "vmware\0"
+ "VMW\0" "vmware\0"
+ "Microsoft Corporation\0" "microsoft\0"
+ "innotek GmbH\0" "oracle\0"
+ "Xen\0" "xen\0"
+ "Bochs\0" "bochs\0";
+
+ static const char cpuid_vendor_table[] =
+ "XenVMMXenVMM\0" "xen\0"
+ "KVMKVMKVM\0" "kvm\0"
+ /* http://kb.vmware.com/selfservice/microsites/search.do?language=en_US&cmd=displayKC&externalId=1009458 */
+ "VMwareVMware\0" "vmware\0"
+ /* http://msdn.microsoft.com/en-us/library/ff542428.aspx */
+ "Microsoft Hv\0" "microsoft\0";
+
+ uint32_t eax, ecx;
+ union {
+ uint32_t sig32[3];
+ char text[13];
+ } sig;
+ unsigned i;
+ const char *j, *k;
+ bool hypervisor;
+
+ /* http://lwn.net/Articles/301888/ */
+ zero(sig);
+
+#if defined (__i386__)
+#define REG_a "eax"
+#define REG_b "ebx"
+#elif defined (__amd64__)
+#define REG_a "rax"
+#define REG_b "rbx"
+#endif
+
+ /* First detect whether there is a hypervisor */
+ eax = 1;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=c" (ecx)
+ : "0" (eax)
+ );
+
+ hypervisor = !!(ecx & 0x80000000U);
+
+ if (hypervisor) {
+
+ /* There is a hypervisor, see what it is */
+ eax = 0x40000000U;
+ __asm__ __volatile__ (
+ /* ebx/rbx is being used for PIC! */
+ " push %%"REG_b" \n\t"
+ " cpuid \n\t"
+ " mov %%ebx, %1 \n\t"
+ " pop %%"REG_b" \n\t"
+
+ : "=a" (eax), "=r" (sig.sig32[0]), "=c" (sig.sig32[1]), "=d" (sig.sig32[2])
+ : "0" (eax)
+ );
+
+ NULSTR_FOREACH_PAIR(j, k, cpuid_vendor_table)
+ if (streq(sig.text, j)) {
+
+ if (id)
+ *id = k;
+
+ return 1;
+ }
+ }
+
+ for (i = 0; i < ELEMENTSOF(dmi_vendors); i++) {
+ char *s;
+ int r;
+ const char *found = NULL;
+
+ if ((r = read_one_line_file(dmi_vendors[i], &s)) < 0) {
+ if (r != -ENOENT)
+ return r;
+
+ continue;
+ }
+
+ NULSTR_FOREACH_PAIR(j, k, dmi_vendor_table)
+ if (startswith(s, j))
+ found = k;
+ free(s);
+
+ if (found) {
+ if (id)
+ *id = found;
+
+ return 1;
+ }
+ }
+
+ if (hypervisor) {
+ if (id)
+ *id = "other";
+
+ return 1;
+ }
+
+#endif
+ return 0;
+}
+
+int detect_container(const char **id) {
+ FILE *f;
+
+ /* Unfortunately many of these operations require root access
+ * in one way or another */
+
+ if (geteuid() != 0)
+ return -EPERM;
+
+ if (running_in_chroot() > 0) {
+
+ if (id)
+ *id = "chroot";
+
+ return 1;
+ }
+
+ /* /proc/vz exists in container and outside of the container,
+ * /proc/bc only outside of the container. */
+ if (access("/proc/vz", F_OK) >= 0 &&
+ access("/proc/bc", F_OK) < 0) {
+
+ if (id)
+ *id = "openvz";
+
+ return 1;
+ }
+
+ if ((f = fopen("/proc/self/cgroup", "r"))) {
+
+ for (;;) {
+ char line[LINE_MAX], *p;
+
+ if (!fgets(line, sizeof(line), f))
+ break;
+
+ if (!(p = strchr(strstrip(line), ':')))
+ continue;
+
+ if (strncmp(p, ":ns:", 4))
+ continue;
+
+ if (!streq(p, ":ns:/")) {
+ fclose(f);
+
+ if (id)
+ *id = "pidns";
+
+ return 1;
+ }
+ }
+
+ fclose(f);
+ }
+
+ return 0;
+}
+
+/* Returns a short identifier for the various VM/container implementations */
+int detect_virtualization(const char **id) {
+ static __thread const char *cached_id = NULL;
+ const char *_id;
+ int r;
+
+ if (cached_id) {
+
+ if (cached_id == (const char*) -1)
+ return 0;
+
+ if (id)
+ *id = cached_id;
+
+ return 1;
+ }
+
+ if ((r = detect_container(&_id)) != 0)
+ goto finish;
+
+ r = detect_vm(&_id);
+
+finish:
+ if (r > 0) {
+ cached_id = _id;
+
+ if (id)
+ *id = _id;
+ } else if (r == 0)
+ cached_id = (const char*) -1;
+
+ return r;
+}
+
+bool dirent_is_file(struct dirent *de) {
+ assert(de);
+
+ if (ignore_file(de->d_name))
+ return false;
+
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ return true;
+}
+
+void execute_directory(const char *directory, DIR *d, char *argv[]) {
+ DIR *_d = NULL;
+ struct dirent *de;
+ Hashmap *pids = NULL;
+
+ assert(directory);
+
+ /* Executes all binaries in a directory in parallel and waits
+ * until all they all finished. */
+
+ if (!d) {
+ if (!(_d = opendir(directory))) {
+
+ if (errno == ENOENT)
+ return;
+
+ log_error("Failed to enumerate directory %s: %m", directory);
+ return;
+ }
+
+ d = _d;
+ }
+
+ if (!(pids = hashmap_new(trivial_hash_func, trivial_compare_func))) {
+ log_error("Failed to allocate set.");
+ goto finish;
+ }
+
+ while ((de = readdir(d))) {
+ char *path;
+ pid_t pid;
+ int k;
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
+ log_error("Out of memory");
+ continue;
+ }
+
+ if ((pid = fork()) < 0) {
+ log_error("Failed to fork: %m");
+ free(path);
+ continue;
+ }
+
+ if (pid == 0) {
+ char *_argv[2];
+ /* Child */
+
+ if (!argv) {
+ _argv[0] = path;
+ _argv[1] = NULL;
+ argv = _argv;
+ } else
+ if (!argv[0])
+ argv[0] = path;
+
+ execv(path, argv);
+
+ log_error("Failed to execute %s: %m", path);
+ _exit(EXIT_FAILURE);
+ }
+
+ log_debug("Spawned %s as %lu", path, (unsigned long) pid);
+
+ if ((k = hashmap_put(pids, UINT_TO_PTR(pid), path)) < 0) {
+ log_error("Failed to add PID to set: %s", strerror(-k));
+ free(path);
+ }
+ }
+
+ while (!hashmap_isempty(pids)) {
+ siginfo_t si;
+ char *path;
+
+ zero(si);
+ if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("waitid() failed: %m");
+ goto finish;
+ }
+
+ if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
+ if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (si.si_code == CLD_EXITED)
+ log_error("%s exited with exit status %i.", path, si.si_status);
+ else
+ log_error("%s terminated by signal %s.", path, signal_to_string(si.si_status));
+ } else
+ log_debug("%s exited successfully.", path);
+
+ free(path);
+ }
+ }
+
+finish:
+ if (_d)
+ closedir(_d);
+
+ if (pids)
+ hashmap_free_free(pids);
+}
+
+int kill_and_sigcont(pid_t pid, int sig) {
+ int r;
+
+ r = kill(pid, sig) < 0 ? -errno : 0;
+
+ if (r >= 0)
+ kill(pid, SIGCONT);
+
+ return r;
+}
+
+bool nulstr_contains(const char*nulstr, const char *needle) {
+ const char *i;
+
+ if (!nulstr)
+ return false;
+
+ NULSTR_FOREACH(i, nulstr)
+ if (streq(i, needle))
+ return true;
+
+ return false;
+}
+
+bool plymouth_running(void) {
+ return access("/run/plymouth/pid", F_OK) >= 0;
+}
+
+void parse_syslog_priority(char **p, int *priority) {
+ int a = 0, b = 0, c = 0;
+ int k;
+
+ assert(p);
+ assert(*p);
+ assert(priority);
+
+ if ((*p)[0] != '<')
+ return;
+
+ if (!strchr(*p, '>'))
+ return;
+
+ if ((*p)[2] == '>') {
+ c = undecchar((*p)[1]);
+ k = 3;
+ } else if ((*p)[3] == '>') {
+ b = undecchar((*p)[1]);
+ c = undecchar((*p)[2]);
+ k = 4;
+ } else if ((*p)[4] == '>') {
+ a = undecchar((*p)[1]);
+ b = undecchar((*p)[2]);
+ c = undecchar((*p)[3]);
+ k = 5;
+ } else
+ return;
+
+ if (a < 0 || b < 0 || c < 0)
+ return;
+
+ *priority = a*100+b*10+c;
+ *p += k;
+}
+
+int have_effective_cap(int value) {
+ cap_t cap;
+ cap_flag_value_t fv;
+ int r;
+
+ if (!(cap = cap_get_proc()))
+ return -errno;
+
+ if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0)
+ r = -errno;
+ else
+ r = fv == CAP_SET;
+
+ cap_free(cap);
+ return r;
+}
+
+char* strshorten(char *s, size_t l) {
+ assert(s);
+
+ if (l < strlen(s))
+ s[l] = 0;
+
+ return s;
+}
+
+static bool hostname_valid_char(char c) {
+ return
+ (c >= 'a' && c <= 'z') ||
+ (c >= 'A' && c <= 'Z') ||
+ (c >= '0' && c <= '9') ||
+ c == '-' ||
+ c == '_' ||
+ c == '.';
+}
+
+bool hostname_is_valid(const char *s) {
+ const char *p;
+
+ if (isempty(s))
+ return false;
+
+ for (p = s; *p; p++)
+ if (!hostname_valid_char(*p))
+ return false;
+
+ if (p-s > HOST_NAME_MAX)
+ return false;
+
+ return true;
+}
+
+char* hostname_cleanup(char *s) {
+ char *p, *d;
+
+ for (p = s, d = s; *p; p++)
+ if ((*p >= 'a' && *p <= 'z') ||
+ (*p >= 'A' && *p <= 'Z') ||
+ (*p >= '0' && *p <= '9') ||
+ *p == '-' ||
+ *p == '_' ||
+ *p == '.')
+ *(d++) = *p;
+
+ *d = 0;
+
+ strshorten(s, HOST_NAME_MAX);
+ return s;
+}
+
+int pipe_eof(int fd) {
+ struct pollfd pollfd;
+ int r;
+
+ zero(pollfd);
+ pollfd.fd = fd;
+ pollfd.events = POLLIN|POLLHUP;
+
+ r = poll(&pollfd, 1, 0);
+ if (r < 0)
+ return -errno;
+
+ if (r == 0)
+ return 0;
+
+ return pollfd.revents & POLLHUP;
+}
+
+int fopen_temporary(const char *path, FILE **_f, char **_temp_path) {
+ FILE *f;
+ char *t;
+ const char *fn;
+ size_t k;
+ int fd;
+
+ assert(path);
+ assert(_f);
+ assert(_temp_path);
+
+ t = new(char, strlen(path) + 1 + 6 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ fn = file_name_from_path(path);
+ k = fn-path;
+ memcpy(t, path, k);
+ t[k] = '.';
+ stpcpy(stpcpy(t+k+1, fn), "XXXXXX");
+
+ fd = mkostemp(t, O_WRONLY|O_CLOEXEC);
+ if (fd < 0) {
+ free(t);
+ return -errno;
+ }
+
+ f = fdopen(fd, "we");
+ if (!f) {
+ unlink(t);
+ free(t);
+ return -errno;
+ }
+
+ *_f = f;
+ *_temp_path = t;
+
+ return 0;
+}
+
+int terminal_vhangup_fd(int fd) {
+ assert(fd >= 0);
+
+ if (ioctl(fd, TIOCVHANGUP) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int terminal_vhangup(const char *name) {
+ int fd, r;
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = terminal_vhangup_fd(fd);
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+int vt_disallocate(const char *name) {
+ int fd, r;
+ unsigned u;
+
+ /* Deallocate the VT if possible. If not possible
+ * (i.e. because it is the active one), at least clear it
+ * entirely (including the scrollback buffer) */
+
+ if (!startswith(name, "/dev/"))
+ return -EINVAL;
+
+ if (!tty_is_vc(name)) {
+ /* So this is not a VT. I guess we cannot deallocate
+ * it then. But let's at least clear the screen */
+
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ loop_write(fd, "\033[H\033[2J", 7, false); /* clear screen */
+ close_nointr_nofail(fd);
+
+ return 0;
+ }
+
+ if (!startswith(name, "/dev/tty"))
+ return -EINVAL;
+
+ r = safe_atou(name+8, &u);
+ if (r < 0)
+ return r;
+
+ if (u <= 0)
+ return -EINVAL;
+
+ /* Try to deallocate */
+ fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ r = ioctl(fd, VT_DISALLOCATE, u);
+ close_nointr_nofail(fd);
+
+ if (r >= 0)
+ return 0;
+
+ if (errno != EBUSY)
+ return -errno;
+
+ /* Couldn't deallocate, so let's clear it fully with
+ * scrollback */
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
+ return fd;
+
+ /* Requires Linux 2.6.40 */
+ loop_write(fd, "\033[H\033[3J", 7, false); /* clear screen including scrollback */
+ close_nointr_nofail(fd);
+
+ return 0;
+}
+
+
+static int file_is_conf(const struct dirent *d, const char *suffix) {
+ assert(d);
+
+ if (ignore_file(d->d_name))
+ return 0;
+
+ if (d->d_type != DT_REG &&
+ d->d_type != DT_LNK &&
+ d->d_type != DT_UNKNOWN)
+ return 0;
+
+ return endswith(d->d_name, suffix);
+}
+
+static int files_add(Hashmap *h, const char *path, const char *suffix) {
+ DIR *dir;
+ struct dirent *de;
+ int r = 0;
+
+ dir = opendir(path);
+ if (!dir) {
+ if (errno == ENOENT)
+ return 0;
+ return -errno;
+ }
+
+ for (de = readdir(dir); de; de = readdir(dir)) {
+ char *p, *f;
+ const char *base;
+
+ if (!file_is_conf(de, suffix))
+ continue;
+
+ if (asprintf(&p, "%s/%s", path, de->d_name) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ f = canonicalize_file_name(p);
+ if (!f) {
+ log_error("Failed to canonicalize file name '%s': %m", p);
+ free(p);
+ continue;
+ }
+ free(p);
+
+ log_debug("found: %s\n", f);
+ base = f + strlen(path) + 1;
+ if (hashmap_put(h, base, f) <= 0)
+ free(f);
+ }
+
+finish:
+ closedir(dir);
+ return r;
+}
+
+static int base_cmp(const void *a, const void *b) {
+ const char *s1, *s2;
+
+ s1 = *(char * const *)a;
+ s2 = *(char * const *)b;
+ return strcmp(file_name_from_path(s1), file_name_from_path(s2));
+}
+
+int conf_files_list(char ***strv, const char *suffix, const char *dir, ...) {
+ Hashmap *fh = NULL;
+ char **dirs = NULL;
+ char **files = NULL;
+ char **p;
+ va_list ap;
+ int r = 0;
+
+ va_start(ap, dir);
+ dirs = strv_new_ap(dir, ap);
+ va_end(ap);
+ if (!dirs) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ if (!strv_path_canonicalize(dirs)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+ if (!strv_uniq(dirs)) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ fh = hashmap_new(string_hash_func, string_compare_func);
+ if (!fh) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ STRV_FOREACH(p, dirs) {
+ if (files_add(fh, *p, suffix) < 0) {
+ log_error("Failed to search for files.");
+ r = -EINVAL;
+ goto finish;
+ }
+ }
+
+ files = hashmap_get_strv(fh);
+ if (files == NULL) {
+ log_error("Failed to compose list of files.");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ qsort(files, hashmap_size(fh), sizeof(char *), base_cmp);
+
+finish:
+ strv_free(dirs);
+ hashmap_free(fh);
+ *strv = files;
+ return r;
+}
+
+int hwclock_is_localtime(void) {
+ FILE *f;
+ bool local = false;
+
+ /*
+ * The third line of adjtime is "UTC" or "LOCAL" or nothing.
+ * # /etc/adjtime
+ * 0.0 0 0
+ * 0
+ * UTC
+ */
+ f = fopen("/etc/adjtime", "re");
+ if (f) {
+ char line[LINE_MAX];
+ bool b;
+
+ b = fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f) &&
+ fgets(line, sizeof(line), f);
+
+ fclose(f);
+
+ if (!b)
+ return -EIO;
+
+
+ truncate_nl(line);
+ local = streq(line, "LOCAL");
+
+ } else if (errno != -ENOENT)
+ return -errno;
+
+ return local;
+}
+
+int hwclock_apply_localtime_delta(void) {
+ const struct timeval *tv_null = NULL;
+ struct timespec ts;
+ struct tm *tm;
+ int minuteswest;
+ struct timezone tz;
+
+ assert_se(clock_gettime(CLOCK_REALTIME, &ts) == 0);
+ assert_se(tm = localtime(&ts.tv_sec));
+ minuteswest = tm->tm_gmtoff / 60;
+
+ tz.tz_minuteswest = -minuteswest;
+ tz.tz_dsttime = 0; /* DST_NONE*/
+
+ /*
+ * If the hardware clock does not run in UTC, but in local time:
+ * The very first time we set the kernel's timezone, it will warp
+ * the clock so that it runs in UTC instead of local time.
+ */
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+
+ return minuteswest;
+}
+
+int hwclock_reset_localtime_delta(void) {
+ const struct timeval *tv_null = NULL;
+ struct timezone tz;
+
+ tz.tz_minuteswest = 0;
+ tz.tz_dsttime = 0; /* DST_NONE*/
+
+ if (settimeofday(tv_null, &tz) < 0)
+ return -errno;
+
+ return 0;
+}
+
+int hwclock_get_time(struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ assert(tm);
+
+ fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ /* This leaves the timezone fields of struct tm
+ * uninitialized! */
+ if (ioctl(fd, RTC_RD_TIME, tm) < 0)
+ err = -errno;
+
+ /* We don't now daylight saving, so we reset this in order not
+ * to confused mktime(). */
+ tm->tm_isdst = -1;
+
+ close_nointr_nofail(fd);
+
+ return err;
+}
+
+int hwclock_set_time(const struct tm *tm) {
+ int fd;
+ int err = 0;
+
+ assert(tm);
+
+ fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ if (fd < 0)
+ return -errno;
+
+ if (ioctl(fd, RTC_SET_TIME, tm) < 0)
+ err = -errno;
+
+ close_nointr_nofail(fd);
+
+ return err;
+}
+
+int copy_file(const char *from, const char *to) {
+ int r, fdf, fdt;
+
+ assert(from);
+ assert(to);
+
+ fdf = open(from, O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fdf < 0)
+ return -errno;
+
+ fdt = open(to, O_WRONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOCTTY, 0644);
+ if (fdt < 0) {
+ close_nointr_nofail(fdf);
+ return -errno;
+ }
+
+ for (;;) {
+ char buf[PIPE_BUF];
+ ssize_t n, k;
+
+ n = read(fdf, buf, sizeof(buf));
+ if (n < 0) {
+ r = -errno;
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+ unlink(to);
+
+ return r;
+ }
+
+ if (n == 0)
+ break;
+
+ errno = 0;
+ k = loop_write(fdt, buf, n, false);
+ if (n != k) {
+ r = k < 0 ? k : (errno ? -errno : -EIO);
+
+ close_nointr_nofail(fdf);
+ close_nointr(fdt);
+
+ unlink(to);
+ return r;
+ }
+ }
+
+ close_nointr_nofail(fdf);
+ r = close_nointr(fdt);
+
+ if (r < 0) {
+ unlink(to);
+ return r;
+ }
+
+ return 0;
+}
+
+int symlink_or_copy(const char *from, const char *to) {
+ char *pf = NULL, *pt = NULL;
+ struct stat a, b;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (parent_of_path(from, &pf) < 0 ||
+ parent_of_path(to, &pt) < 0) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ if (stat(pf, &a) < 0 ||
+ stat(pt, &b) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ if (a.st_dev != b.st_dev) {
+ free(pf);
+ free(pt);
+
+ return copy_file(from, to);
+ }
+
+ if (symlink(from, to) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(pf);
+ free(pt);
+
+ return r;
+}
+
+int symlink_or_copy_atomic(const char *from, const char *to) {
+ char *t, *x;
+ const char *fn;
+ size_t k;
+ unsigned long long ull;
+ unsigned i;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ t = new(char, strlen(to) + 1 + 16 + 1);
+ if (!t)
+ return -ENOMEM;
+
+ fn = file_name_from_path(to);
+ k = fn-to;
+ memcpy(t, to, k);
+ t[k] = '.';
+ x = stpcpy(t+k+1, fn);
+
+ ull = random_ull();
+ for (i = 0; i < 16; i++) {
+ *(x++) = hexchar(ull & 0xF);
+ ull >>= 4;
+ }
+
+ *x = 0;
+
+ r = symlink_or_copy(from, t);
+ if (r < 0) {
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ if (rename(t, to) < 0) {
+ r = -errno;
+ unlink(t);
+ free(t);
+ return r;
+ }
+
+ free(t);
+ return r;
+}
+
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+ char *p, *s;
+ uint32_t u;
+ int r;
+
+ assert(pid >= 1);
+ assert(id);
+
+ if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+ return -ENOENT;
+
+ if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(s, &u);
+ free(s);
+
+ if (r < 0)
+ return r;
+
+ if (u == (uint32_t) -1 || u <= 0)
+ return -ENOENT;
+
+ *id = u;
+ return 0;
+}
+
+bool display_is_local(const char *display) {
+ assert(display);
+
+ return
+ display[0] == ':' &&
+ display[1] >= '0' &&
+ display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
}
static const char *const ioprio_class_table[] = {
DEFINE_STRING_TABLE_LOOKUP(sigchld_code, int);
-static const char *const log_facility_table[LOG_NFACILITIES] = {
+static const char *const log_facility_unshifted_table[LOG_NFACILITIES] = {
[LOG_FAC(LOG_KERN)] = "kern",
[LOG_FAC(LOG_USER)] = "user",
[LOG_FAC(LOG_MAIL)] = "mail",
[LOG_FAC(LOG_LOCAL7)] = "local7"
};
-DEFINE_STRING_TABLE_LOOKUP(log_facility, int);
+DEFINE_STRING_TABLE_LOOKUP(log_facility_unshifted, int);
static const char *const log_level_table[] = {
[LOG_EMERG] = "emerg",