#include <linux/kd.h>
#include <dlfcn.h>
#include <sys/wait.h>
-#include <sys/capability.h>
#include <sys/time.h>
#include <glob.h>
#include <grp.h>
#include <sys/mman.h>
+#include <sys/vfs.h>
+#include <linux/magic.h>
#include "macro.h"
#include "util.h"
continue;
if (!(u = normalize_env_assignment(p))) {
- log_error("Out of memory");
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
free(u);
if (!t) {
- log_error("Out of memory");
- r = -ENOMEM;
+ r = log_oom();
goto finish;
}
if (h < 0)
return h;
- r = join("[", t, "]", NULL);
+ r = strjoin("[", t, "]", NULL);
free(t);
if (!r)
return r;
}
-char *cunescape_length(const char *s, size_t length) {
+char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
char *r, *t;
const char *f;
+ size_t pl;
assert(s);
- /* Undoes C style string escaping */
+ /* Undoes C style string escaping, and optionally prefixes it. */
- r = new(char, length+1);
+ pl = prefix ? strlen(prefix) : 0;
+
+ r = new(char, pl+length+1);
if (!r)
return r;
- for (f = s, t = r; f < s + length; f++) {
+ if (prefix)
+ memcpy(r, prefix, pl);
+
+ for (f = s, t = r + pl; f < s + length; f++) {
if (*f != '\\') {
*(t++) = *f;
return r;
}
+char *cunescape_length(const char *s, size_t length) {
+ return cunescape_length_with_prefix(s, length, NULL);
+}
+
char *cunescape(const char *s) {
+ assert(s);
+
return cunescape_length(s, strlen(s));
}
return t;
}
-bool ignore_file(const char *filename) {
+static bool ignore_file_allow_backup(const char *filename) {
assert(filename);
return
streq(filename, "lost+found") ||
streq(filename, "aquota.user") ||
streq(filename, "aquota.group") ||
- endswith(filename, "~") ||
endswith(filename, ".rpmnew") ||
endswith(filename, ".rpmsave") ||
endswith(filename, ".rpmorig") ||
endswith(filename, ".swp");
}
+bool ignore_file(const char *filename) {
+ assert(filename);
+
+ if (endswith(filename, "~"))
+ return false;
+
+ return ignore_file_allow_backup(filename);
+}
+
int fd_nonblock(int fd, bool nonblock) {
int flags;
*/
for (;;) {
- if ((fd = open(name, mode)) >= 0)
+ fd = open(name, mode);
+ if (fd >= 0)
break;
if (errno != EIO)
return -errno;
+ /* Max 1s in total */
if (c >= 20)
return -errno;
if (fd < 0)
return -errno;
- if ((r = isatty(fd)) < 0) {
+ r = isatty(fd);
+ if (r < 0) {
close_nointr_nofail(fd);
return -errno;
}
}
}
-int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
- int fd = -1, notify = -1, r, wd = -1;
+int acquire_terminal(
+ const char *name,
+ bool fail,
+ bool force,
+ bool ignore_tiocstty_eperm,
+ usec_t timeout) {
+
+ int fd = -1, notify = -1, r = 0, wd = -1;
+ usec_t ts = 0;
+ struct sigaction sa_old, sa_new;
assert(name);
* on the same tty as an untrusted user this should not be a
* problem. (Which he probably should not do anyway.) */
+ if (timeout != (usec_t) -1)
+ ts = now(CLOCK_MONOTONIC);
+
if (!fail && !force) {
- if ((notify = inotify_init1(IN_CLOEXEC)) < 0) {
+ notify = inotify_init1(IN_CLOEXEC | (timeout != (usec_t) -1 ? IN_NONBLOCK : 0));
+ if (notify < 0) {
r = -errno;
goto fail;
}
- if ((wd = inotify_add_watch(notify, name, IN_CLOSE)) < 0) {
+ wd = inotify_add_watch(notify, name, IN_CLOSE);
+ if (wd < 0) {
r = -errno;
goto fail;
}
}
for (;;) {
- if (notify >= 0)
- if ((r = flush_fd(notify)) < 0)
+ if (notify >= 0) {
+ r = flush_fd(notify);
+ if (r < 0)
goto fail;
+ }
/* 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|O_CLOEXEC)) < 0)
+ fd = open_terminal(name, O_RDWR|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
return fd;
+ /* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
+ * if we already own the tty. */
+ zero(sa_new);
+ sa_new.sa_handler = SIG_IGN;
+ sa_new.sa_flags = SA_RESTART;
+ assert_se(sigaction(SIGHUP, &sa_new, &sa_old) == 0);
+
/* First, try to get the tty */
- r = ioctl(fd, TIOCSCTTY, force);
+ if (ioctl(fd, TIOCSCTTY, force) < 0)
+ r = -errno;
+
+ assert_se(sigaction(SIGHUP, &sa_old, NULL) == 0);
/* Sometimes it makes sense to ignore TIOCSCTTY
* returning EPERM, i.e. when very likely we already
* are have this controlling terminal. */
- if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
+ if (r < 0 && r == -EPERM && ignore_tiocstty_eperm)
r = 0;
- if (r < 0 && (force || fail || errno != EPERM)) {
- r = -errno;
+ if (r < 0 && (force || fail || r != -EPERM)) {
goto fail;
}
ssize_t l;
struct inotify_event *e;
- if ((l = read(notify, inotify_buffer, sizeof(inotify_buffer))) < 0) {
+ if (timeout != (usec_t) -1) {
+ usec_t n;
+
+ n = now(CLOCK_MONOTONIC);
+ if (ts + timeout < n) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+
+ r = fd_wait_for_event(fd, POLLIN, ts + timeout - n);
+ if (r < 0)
+ goto fail;
+
+ if (r == 0) {
+ r = -ETIMEDOUT;
+ goto fail;
+ }
+ }
+
+ l = read(notify, inotify_buffer, sizeof(inotify_buffer));
+ if (l < 0) {
- if (errno == EINTR)
+ if (errno == EINTR || errno == EAGAIN)
continue;
r = -errno;
{ "m", USEC_PER_MINUTE },
{ "usec", 1ULL },
{ "us", 1ULL },
- { "", USEC_PER_SEC },
+ { "", USEC_PER_SEC }, /* default is sec */
};
const char *p;
return 0;
}
+int parse_nsec(const char *t, nsec_t *nsec) {
+ static const struct {
+ const char *suffix;
+ nsec_t nsec;
+ } table[] = {
+ { "sec", NSEC_PER_SEC },
+ { "s", NSEC_PER_SEC },
+ { "min", NSEC_PER_MINUTE },
+ { "hr", NSEC_PER_HOUR },
+ { "h", NSEC_PER_HOUR },
+ { "d", NSEC_PER_DAY },
+ { "w", NSEC_PER_WEEK },
+ { "msec", NSEC_PER_MSEC },
+ { "ms", NSEC_PER_MSEC },
+ { "m", NSEC_PER_MINUTE },
+ { "usec", NSEC_PER_USEC },
+ { "us", NSEC_PER_USEC },
+ { "nsec", 1ULL },
+ { "ns", 1ULL },
+ { "", 1ULL }, /* default is nsec */
+ };
+
+ const char *p;
+ nsec_t r = 0;
+
+ assert(t);
+ assert(nsec);
+
+ p = t;
+ do {
+ long long l;
+ char *e;
+ unsigned i;
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno != 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ r += (nsec_t) l * table[i].nsec;
+ p = e + strlen(table[i].suffix);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ } while (*p != 0);
+
+ *nsec = r;
+
+ return 0;
+}
+
int parse_bytes(const char *t, off_t *bytes) {
static const struct {
const char *suffix;
int make_null_stdio(void) {
int null_fd;
- if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+ null_fd = open("/dev/null", O_RDWR|O_NOCTTY);
+ if (null_fd < 0)
return -errno;
return make_stdio(null_fd);
uint64_t ull;
ssize_t r;
- if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
+ fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
goto fallback;
r = loop_read(fd, &ull, sizeof(ull), true);
return !isempty(u.nodename) && !streq(u.nodename, "(none)");
}
-char* getlogname_malloc(void) {
- uid_t uid;
+static char *lookup_uid(uid_t uid) {
long bufsize;
char *buf, *name;
struct passwd pwbuf, *pw = NULL;
- struct stat st;
-
- if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
- uid = st.st_uid;
- else
- uid = getuid();
/* Shortcut things to avoid NSS lookups */
if (uid == 0)
return strdup("root");
- if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+ bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ if (bufsize <= 0)
bufsize = 4096;
- if (!(buf = malloc(bufsize)))
+ buf = malloc(bufsize);
+ if (!buf)
return NULL;
if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
return name;
}
+char* getlogname_malloc(void) {
+ uid_t uid;
+ struct stat st;
+
+ if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+ uid = st.st_uid;
+ else
+ uid = getuid();
+
+ return lookup_uid(uid);
+}
+
+char *getusername_malloc(void) {
+ const char *e;
+
+ e = getenv("USER");
+ if (e)
+ return strdup(e);
+
+ return lookup_uid(getuid());
+}
+
int getttyname_malloc(int fd, char **r) {
char path[PATH_MAX], *c;
int k;
return 0;
}
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
+int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
DIR *d;
int ret = 0;
assert(fd >= 0);
/* This returns the first error we run into, but nevertheless
- * tries to go on */
+ * tries to go on. This closes the passed fd. */
d = fdopendir(fd);
if (!d) {
for (;;) {
struct dirent buf, *de;
- bool is_dir, keep_around = false;
+ bool is_dir, keep_around;
+ struct stat st;
int r;
r = readdir_r(d, &buf, &de);
if (streq(de->d_name, ".") || streq(de->d_name, ".."))
continue;
- if (de->d_type == DT_UNKNOWN) {
- struct stat st;
-
+ if (de->d_type == DT_UNKNOWN ||
+ honour_sticky ||
+ (de->d_type == DT_DIR && root_dev)) {
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
- if (honour_sticky)
- keep_around =
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-
is_dir = S_ISDIR(st.st_mode);
-
+ keep_around =
+ honour_sticky &&
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
} else {
- if (honour_sticky) {
- struct stat st;
-
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- keep_around =
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- }
-
is_dir = de->d_type == DT_DIR;
+ keep_around = false;
}
if (is_dir) {
int subdir_fd;
- subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
+ /* if root_dev is set, remove subdirectories only, if device is same as dir */
+ if (root_dev && st.st_dev != root_dev->st_dev)
+ continue;
+
+ subdir_fd = openat(fd, de->d_name,
+ O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (subdir_fd < 0) {
if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
- r = rm_rf_children(subdir_fd, only_dirs, honour_sticky);
+ r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
if (r < 0 && ret == 0)
ret = r;
return ret;
}
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- int fd;
- int r;
+int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
+ struct statfs s;
+
+ assert(fd >= 0);
+
+ if (fstatfs(fd, &s) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* We refuse to clean disk file systems with this call. This
+ * is extra paranoia just to be sure we never ever remove
+ * non-state data */
+
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ close_nointr_nofail(fd);
+ return -EPERM;
+ }
+
+ return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
+}
+
+static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
+ int fd, r;
+ struct statfs s;
assert(path);
+ /* We refuse to clean the root file system with this
+ * call. This is extra paranoia to never cause a really
+ * seriously broken system. */
+ if (path_equal(path, "/")) {
+ log_error("Attempted to remove entire root file system, and we can't allow that.");
+ return -EPERM;
+ }
+
fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
if (fd < 0) {
if (errno != ENOTDIR)
return -errno;
+ if (!dangerous) {
+ if (statfs(path, &s) < 0)
+ return -errno;
+
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ return -EPERM;
+ }
+ }
+
if (delete_root && !only_dirs)
if (unlink(path) < 0 && errno != ENOENT)
return -errno;
return 0;
}
- r = rm_rf_children(fd, only_dirs, honour_sticky);
+ if (!dangerous) {
+ if (fstatfs(fd, &s) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+ if (s.f_type != TMPFS_MAGIC &&
+ s.f_type != RAMFS_MAGIC) {
+ log_error("Attempted to remove disk file system, and we can't allow that.");
+ close_nointr_nofail(fd);
+ return -EPERM;
+ }
+ }
+
+ r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
if (delete_root) {
if (honour_sticky && file_is_priv_sticky(path) > 0)
return r;
}
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+ return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
+}
+
+int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
+ return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+}
+
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
assert(path);
return ws.ws_col;
}
-unsigned columns(void) {
- static __thread int parsed_columns = 0;
+static unsigned columns_cached(bool cached) {
+ static __thread int parsed_columns = 0, env_columns = -1;
const char *e;
- if (_likely_(parsed_columns > 0))
+ if (_likely_(parsed_columns > 0 && cached))
return parsed_columns;
- e = getenv("COLUMNS");
- if (e)
- parsed_columns = atoi(e);
+ if (_unlikely_(env_columns == -1)) {
+ e = getenv("COLUMNS");
+ if (e)
+ env_columns = atoi(e);
+ else
+ env_columns = 0;
+ }
- if (parsed_columns <= 0)
+ if (env_columns > 0) {
+ parsed_columns = env_columns;
+ return parsed_columns;
+ }
+
+ if (parsed_columns <= 0 || !cached)
parsed_columns = fd_columns(STDOUT_FILENO);
if (parsed_columns <= 0)
return parsed_columns;
}
+unsigned columns(void) {
+ return columns_cached(true);
+}
+
+unsigned columns_uncached(void) {
+ return columns_cached(false);
+}
+
int fd_lines(int fd) {
struct winsize ws;
zero(ws);
assert(name);
assert(pid > 1);
- if ((r = wait_for_terminate(pid, &status)) < 0) {
+ r = wait_for_terminate(pid, &status);
+ if (r < 0) {
log_warning("Failed to wait for %s: %s", name, strerror(-r));
return r;
}
log_warning("%s failed due to unknown reason.", name);
return -EPROTO;
-
}
_noreturn_ void freeze(void) {
}
}
-char *fstab_node_to_udev_node(const char *p) {
+static char *tag_to_udev_node(const char *tagvalue, const char *by) {
char *dn, *t, *u;
int r;
/* FIXME: to follow udev's logic 100% we need to leave valid
* UTF8 chars unescaped */
- if (startswith(p, "LABEL=")) {
-
- if (!(u = unquote(p+6, "\"\'")))
- return NULL;
-
- t = xescape(u, "/ ");
- free(u);
-
- if (!t)
- return NULL;
-
- r = asprintf(&dn, "/dev/disk/by-label/%s", t);
- free(t);
+ u = unquote(tagvalue, "\"\'");
+ if (u == NULL)
+ return NULL;
- if (r < 0)
- return NULL;
+ t = xescape(u, "/ ");
+ free(u);
- return dn;
- }
+ if (t == NULL)
+ return NULL;
- if (startswith(p, "UUID=")) {
+ r = asprintf(&dn, "/dev/disk/by-%s/%s", by, t);
+ free(t);
- if (!(u = unquote(p+5, "\"\'")))
- return NULL;
+ if (r < 0)
+ return NULL;
- t = xescape(u, "/ ");
- free(u);
+ return dn;
+}
- if (!t)
- return NULL;
+char *fstab_node_to_udev_node(const char *p) {
+ if (startswith(p, "LABEL="))
+ return tag_to_udev_node(p+6, "label");
- r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
- free(t);
+ if (startswith(p, "UUID="))
+ return tag_to_udev_node(p+5, "uuid");
- if (r < 0)
- return NULL;
+ if (startswith(p, "PARTUUID="))
+ return tag_to_udev_node(p+9, "partuuid");
- return dn;
- }
+ if (startswith(p, "PARTLABEL="))
+ return tag_to_udev_node(p+10, "partlabel");
return strdup(p);
}
bool dirent_is_file_with_suffix(const struct dirent *de, const char *suffix) {
assert(de);
- if (!dirent_is_file(de))
+ if (de->d_type != DT_REG &&
+ de->d_type != DT_LNK &&
+ de->d_type != DT_UNKNOWN)
+ return false;
+
+ if (ignore_file_allow_backup(de->d_name))
return false;
return endswith(de->d_name, suffix);
continue;
if (asprintf(&path, "%s/%s", directory, de->d_name) < 0) {
- log_error("Out of memory");
+ log_oom();
continue;
}
}
if ((path = hashmap_remove(pids, UINT_TO_PTR(si.si_pid)))) {
- if (!is_clean_exit(si.si_code, si.si_status)) {
+ if (!is_clean_exit(si.si_code, si.si_status, NULL)) {
if (si.si_code == CLD_EXITED)
log_error("%s exited with exit status %i.", path, si.si_status);
else
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;
-}
-
-void skip_syslog_pid(char **buf) {
- char *p;
-
- assert(buf);
- assert(*buf);
-
- p = *buf;
-
- if (*p != '[')
- return;
-
- p++;
- p += strspn(p, "0123456789");
-
- if (*p != ']')
- return;
-
- p++;
-
- *buf = p;
-}
-
-void skip_syslog_date(char **buf) {
- enum {
- LETTER,
- SPACE,
- NUMBER,
- SPACE_OR_NUMBER,
- COLON
- } sequence[] = {
- LETTER, LETTER, LETTER,
- SPACE,
- SPACE_OR_NUMBER, NUMBER,
- SPACE,
- SPACE_OR_NUMBER, NUMBER,
- COLON,
- SPACE_OR_NUMBER, NUMBER,
- COLON,
- SPACE_OR_NUMBER, NUMBER,
- SPACE
- };
-
- char *p;
- unsigned i;
-
- assert(buf);
- assert(*buf);
-
- p = *buf;
-
- for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
-
- if (!*p)
- return;
-
- switch (sequence[i]) {
-
- case SPACE:
- if (*p != ' ')
- return;
- break;
-
- case SPACE_OR_NUMBER:
- if (*p == ' ')
- break;
-
- /* fall through */
-
- case NUMBER:
- if (*p < '0' || *p > '9')
- return;
-
- break;
-
- case LETTER:
- if (!(*p >= 'A' && *p <= 'Z') &&
- !(*p >= 'a' && *p <= 'z'))
- return;
-
- break;
-
- case COLON:
- if (*p != ':')
- return;
- break;
-
- }
- }
-
- *buf = p;
-}
-
char* strshorten(char *s, size_t l) {
assert(s);
return 0;
}
-int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+int get_user_creds(
+ const char **username,
+ uid_t *uid, gid_t *gid,
+ const char **home,
+ const char **shell) {
+
struct passwd *p;
uid_t u;
if (home)
*home = "/root";
+
+ if (shell)
+ *shell = "/bin/sh";
+
return 0;
}
if (home)
*home = p->pw_dir;
+ if (shell)
+ *shell = p->pw_shell;
+
return 0;
}
return r;
}
-char *join(const char *x, ...) {
+char *strjoin(const char *x, ...) {
va_list ap;
size_t l;
char *r, *p;
}
bool in_initrd(void) {
- static bool checked=false;
- static bool is_in_initrd=false;
-
- if (!checked) {
- struct stat sb;
- if (stat("/", &sb) == 0) {
- is_in_initrd = (sb.st_dev == 1);
- checked = true;
- }
+ static int saved = -1;
+ struct statfs s;
+
+ if (saved >= 0)
+ return saved;
+
+ /* We make two checks here:
+ *
+ * 1. the flag file /etc/initrd-release must exist
+ * 2. the root file system must be a memory file system
+ *
+ * The second check is extra paranoia, since misdetecting an
+ * initrd can have bad bad consequences due the initrd
+ * emptying when transititioning to the main systemd.
+ */
+
+ saved = access("/etc/initrd-release", F_OK) >= 0 &&
+ statfs("/", &s) >= 0 &&
+ (s.f_type == TMPFS_MAGIC || s.f_type == RAMFS_MAGIC);
+
+ return saved;
+}
+
+void warn_melody(void) {
+ int fd;
+
+ fd = open("/dev/console", O_WRONLY|O_CLOEXEC|O_NOCTTY);
+ if (fd < 0)
+ return;
+
+ /* Yeah, this is synchronous. Kinda sucks. Bute well... */
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/440));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, (int)(1193180/220));
+ usleep(125*USEC_PER_MSEC);
+
+ ioctl(fd, KIOCSOUND, 0);
+ close_nointr_nofail(fd);
+}
+
+int make_console_stdio(void) {
+ int fd, r;
+
+ /* Make /dev/console the controlling terminal and stdin/stdout/stderr */
+
+ fd = acquire_terminal("/dev/console", false, true, true, (usec_t) -1);
+ if (fd < 0) {
+ log_error("Failed to acquire terminal: %s", strerror(-fd));
+ return fd;
+ }
+
+ r = make_stdio(fd);
+ if (r < 0) {
+ log_error("Failed to duplicate terminal fd: %s", strerror(-r));
+ return r;
+ }
+
+ return 0;
+}
+
+int get_home_dir(char **_h) {
+ char *h;
+ const char *e;
+ uid_t u;
+ struct passwd *p;
+
+ assert(_h);
+
+ /* Take the user specified one */
+ e = getenv("HOME");
+ if (e) {
+ h = strdup(e);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ h = strdup("/root");
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+ }
+
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno ? -errno : -ENOENT;
+
+ if (!path_is_absolute(p->pw_dir))
+ return -EINVAL;
+
+ h = strdup(p->pw_dir);
+ if (!h)
+ return -ENOMEM;
+
+ *_h = h;
+ return 0;
+}
+
+int get_shell(char **_sh) {
+ char *sh;
+ const char *e;
+ uid_t u;
+ struct passwd *p;
+
+ assert(_sh);
+
+ /* Take the user specified one */
+ e = getenv("SHELL");
+ if (e) {
+ sh = strdup(e);
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
+ }
+
+ /* Hardcode home directory for root to avoid NSS */
+ u = getuid();
+ if (u == 0) {
+ sh = strdup("/bin/sh");
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
}
- return is_in_initrd;
+ /* Check the database... */
+ errno = 0;
+ p = getpwuid(u);
+ if (!p)
+ return errno ? -errno : -ESRCH;
+
+ if (!path_is_absolute(p->pw_shell))
+ return -EINVAL;
+
+ sh = strdup(p->pw_shell);
+ if (!sh)
+ return -ENOMEM;
+
+ *_sh = sh;
+ return 0;
}