X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=6f9399b41808720cd5202fea8ff4a74fd96833f0;hp=fa34137bdca66edf0af68d801334e2f1e721921b;hb=e23a0ce8badd09aefa961a3a576bfe85f6ebbad7;hpb=35d2e7ec19f8d3960a14dc04642060ccee3faa43 diff --git a/src/util.c b/src/util.c index fa34137bd..6f9399b41 100644 --- a/src/util.c +++ b/src/util.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -47,6 +47,9 @@ #include #include #include +#include +#include +#include #include "macro.h" #include "util.h" @@ -54,6 +57,8 @@ #include "missing.h" #include "log.h" #include "strv.h" +#include "label.h" +#include "exit-status.h" bool streq_ptr(const char *a, const char *b) { @@ -499,7 +504,7 @@ finish: int read_one_line_file(const char *fn, char **line) { FILE *f; int r; - char t[2048], *c; + char t[LINE_MAX], *c; assert(fn); assert(line); @@ -525,6 +530,155 @@ finish: return r; } +int read_full_file(const char *fn, char **contents) { + FILE *f; + int r; + size_t n, l; + char *buf = NULL; + struct stat st; + + if (!(f = fopen(fn, "re"))) + return -errno; + + if (fstat(fileno(f), &st) < 0) { + r = -errno; + goto finish; + } + + n = st.st_size > 0 ? st.st_size : LINE_MAX; + l = 0; + + for (;;) { + char *t; + size_t k; + + if (!(t = realloc(buf, n+1))) { + r = -ENOMEM; + goto finish; + } + + buf = t; + k = fread(buf + l, 1, n - l, f); + + if (k <= 0) { + if (ferror(f)) { + r = -errno; + goto finish; + } + + break; + } + + l += k; + n *= 2; + + /* Safety check */ + if (n > 4*1024*1024) { + r = -E2BIG; + goto finish; + } + } + + if (buf) + buf[l] = 0; + else if (!(buf = calloc(1, 1))) { + r = -errno; + goto finish; + } + + *contents = buf; + buf = NULL; + + r = 0; + +finish: + fclose(f); + free(buf); + + return r; +} + +int parse_env_file( + const char *fname, + const char *separator, ...) { + + int r = 0; + char *contents, *p; + + assert(fname); + assert(separator); + + if ((r = read_full_file(fname, &contents)) < 0) + return r; + + p = contents; + for (;;) { + const char *key = NULL; + + p += strspn(p, separator); + p += strspn(p, WHITESPACE); + + if (!*p) + break; + + if (!strchr(COMMENTS, *p)) { + va_list ap; + char **value; + + va_start(ap, separator); + while ((key = va_arg(ap, char *))) { + size_t n; + char *v; + + value = va_arg(ap, char **); + + n = strlen(key); + if (strncmp(p, key, n) != 0 || + p[n] != '=') + continue; + + p += n + 1; + n = strcspn(p, separator); + + if (n >= 2 && + strchr(QUOTES, p[0]) && + p[n-1] == p[0]) + v = strndup(p+1, n-2); + else + v = strndup(p, n); + + if (!v) { + r = -ENOMEM; + va_end(ap); + goto fail; + } + + if (v[0] == '\0') { + /* return empty value strings as NULL */ + free(v); + v = NULL; + } + + free(*value); + *value = v; + + p += n; + + r ++; + break; + } + va_end(ap); + } + + if (!key) + p += strcspn(p, separator); + } + +fail: + free(contents); + return r; +} + char *truncate_nl(char *s) { assert(s); @@ -968,7 +1122,7 @@ char *file_in_same_dir(const char *path, const char *filename) { int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) { struct stat st; - if (mkdir(path, mode) >= 0) + if (label_mkdir(path, mode) >= 0) if (chmod_and_chown(path, mode, uid, gid) < 0) return -errno; @@ -1011,7 +1165,7 @@ int mkdir_parents(const char *path, mode_t mode) { if (!(t = strndup(path, e - path))) return -ENOMEM; - r = mkdir(t, mode); + r = label_mkdir(t, mode); free(t); if (r < 0 && errno != EEXIST) @@ -1027,7 +1181,7 @@ int mkdir_p(const char *path, mode_t mode) { if ((r = mkdir_parents(path, mode)) < 0) return r; - if (mkdir(path, mode) < 0 && errno != EEXIST) + if (label_mkdir(path, mode) < 0 && errno != EEXIST) return -errno; return 0; @@ -1527,6 +1681,8 @@ bool ignore_file(const char *filename) { return filename[0] == '.' || streq(filename, "lost+found") || + streq(filename, "aquota.user") || + streq(filename, "aquota.group") || endswith(filename, "~") || endswith(filename, ".rpmnew") || endswith(filename, ".rpmsave") || @@ -1655,6 +1811,63 @@ char *format_timestamp(char *buf, size_t l, usec_t t) { return buf; } +char *format_timestamp_pretty(char *buf, size_t l, usec_t t) { + usec_t n, d; + + n = now(CLOCK_REALTIME); + + if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t) + return NULL; + + d = n - t; + + if (d >= USEC_PER_YEAR) + snprintf(buf, l, "%llu years and %llu months ago", + (unsigned long long) (d / USEC_PER_YEAR), + (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH)); + else if (d >= USEC_PER_MONTH) + snprintf(buf, l, "%llu months and %llu days ago", + (unsigned long long) (d / USEC_PER_MONTH), + (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY)); + else if (d >= USEC_PER_WEEK) + snprintf(buf, l, "%llu weeks and %llu days ago", + (unsigned long long) (d / USEC_PER_WEEK), + (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY)); + else if (d >= 2*USEC_PER_DAY) + snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY)); + else if (d >= 25*USEC_PER_HOUR) + snprintf(buf, l, "1 day and %lluh ago", + (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR)); + else if (d >= 6*USEC_PER_HOUR) + snprintf(buf, l, "%lluh ago", + (unsigned long long) (d / USEC_PER_HOUR)); + else if (d >= USEC_PER_HOUR) + snprintf(buf, l, "%lluh %llumin ago", + (unsigned long long) (d / USEC_PER_HOUR), + (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE)); + else if (d >= 5*USEC_PER_MINUTE) + snprintf(buf, l, "%llumin ago", + (unsigned long long) (d / USEC_PER_MINUTE)); + else if (d >= USEC_PER_MINUTE) + snprintf(buf, l, "%llumin %llus ago", + (unsigned long long) (d / USEC_PER_MINUTE), + (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC)); + else if (d >= USEC_PER_SEC) + snprintf(buf, l, "%llus ago", + (unsigned long long) (d / USEC_PER_SEC)); + else if (d >= USEC_PER_MSEC) + snprintf(buf, l, "%llums ago", + (unsigned long long) (d / USEC_PER_MSEC)); + else if (d > 0) + snprintf(buf, l, "%lluus ago", + (unsigned long long) d); + else + snprintf(buf, l, "now"); + + buf[l-1] = 0; + return buf; +} + char *format_timespan(char *buf, size_t l, usec_t t) { static const struct { const char *suffix; @@ -1678,6 +1891,12 @@ char *format_timespan(char *buf, size_t l, usec_t t) { if (t == (usec_t) -1) return NULL; + if (t == 0) { + snprintf(p, l, "0"); + p[l-1] = 0; + return p; + } + /* The result of this function can be parsed with parse_usec */ for (i = 0; i < ELEMENTSOF(table); i++) { @@ -1798,23 +2017,29 @@ int read_one_char(FILE *f, char *ret, bool *need_nl) { } int ask(char *ret, const char *replies, const char *text, ...) { + bool on_tty; + assert(ret); assert(replies); assert(text); + on_tty = isatty(STDOUT_FILENO); + for (;;) { va_list ap; char c; int r; bool need_nl = true; - fputs("\x1B[1m", stdout); + if (on_tty) + fputs("\x1B[1m", stdout); va_start(ap, text); vprintf(text, ap); va_end(ap); - fputs("\x1B[0m", stdout); + if (on_tty) + fputs("\x1B[0m", stdout); fflush(stdout); @@ -1844,10 +2069,22 @@ int ask(char *ret, const char *replies, const char *text, ...) { int reset_terminal(int fd) { struct termios termios; int r = 0; + long arg; + + /* Set terminal to some sane defaults */ assert(fd >= 0); - /* Set terminal to some sane defaults */ + /* We leave locked terminal attributes untouched, so that + * Plymouth may set whatever it wants to set, and we don't + * interfere with that. */ + + /* Disable exclusive mode, just in case */ + ioctl(fd, TIOCNXCL); + + /* Enable console unicode mode */ + arg = K_UNICODE; + ioctl(fd, KDSKBMODE, &arg); if (tcgetattr(fd, &termios) < 0) { r = -errno; @@ -2012,26 +2249,34 @@ int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocst assert(notify >= 0); for (;;) { - struct inotify_event e; + uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX]; ssize_t l; + struct inotify_event *e; - if ((l = read(notify, &e, sizeof(e))) != sizeof(e)) { + if ((l = read(notify, &inotify_buffer, sizeof(inotify_buffer))) < 0) { - if (l < 0) { + if (errno == EINTR) + continue; - if (errno == EINTR) - continue; + r = -errno; + goto fail; + } + + e = (struct inotify_event*) inotify_buffer; - r = -errno; - } else + while (l > 0) { + size_t step; + + if (e->wd != wd || !(e->mask & IN_CLOSE)) { r = -EIO; + goto fail; + } - goto fail; - } + step = sizeof(struct inotify_event) + e->len; + assert(step <= (size_t) l); - if (e.wd != wd || !(e.mask & IN_CLOSE)) { - r = -errno; - goto fail; + e = (struct inotify_event*) ((uint8_t*) e + step); + l -= step; } break; @@ -2360,6 +2605,15 @@ int make_stdio(int fd) { return 0; } +int make_null_stdio(void) { + int null_fd; + + if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0) + return -errno; + + return make_stdio(null_fd); +} + bool is_clean_exit(int code, int status) { if (code == CLD_EXITED) @@ -2377,6 +2631,16 @@ bool is_clean_exit(int code, int status) { return false; } +bool is_clean_exit_lsb(int code, int status) { + + if (is_clean_exit(code, status)) + return true; + + return + code == CLD_EXITED && + (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED); +} + bool is_device_path(const char *path) { /* Returns true on paths that refer to a device, either in @@ -2472,18 +2736,6 @@ char* gethostname_malloc(void) { return strdup(u.sysname); } -int getmachineid_malloc(char **b) { - int r; - - assert(b); - - if ((r = read_one_line_file("/var/lib/dbus/machine-id", b)) < 0) - return r; - - strstrip(*b); - return 0; -} - char* getlogname_malloc(void) { uid_t uid; long bufsize; @@ -2522,11 +2774,12 @@ char* getlogname_malloc(void) { int getttyname_malloc(char **r) { char path[PATH_MAX], *p, *c; + int k; assert(r); - if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0) - return -errno; + if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0) + return -k; char_array_0(path); @@ -2552,7 +2805,8 @@ static int rm_rf_children(int fd, bool only_dirs) { if (!(d = fdopendir(fd))) { close_nointr_nofail(fd); - return -errno; + + return errno == ENOENT ? 0 : -errno; } for (;;) { @@ -2576,7 +2830,7 @@ static int rm_rf_children(int fd, bool only_dirs) { struct stat st; if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) { - if (ret == 0) + if (ret == 0 && errno != ENOENT) ret = -errno; continue; } @@ -2589,7 +2843,7 @@ static int rm_rf_children(int fd, bool only_dirs) { int subdir_fd; if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) { - if (ret == 0) + if (ret == 0 && errno != ENOENT) ret = -errno; continue; } @@ -2600,13 +2854,13 @@ static int rm_rf_children(int fd, bool only_dirs) { } if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) { - if (ret == 0) + if (ret == 0 && errno != ENOENT) ret = -errno; } } else if (!only_dirs) { if (unlinkat(fd, de->d_name, 0) < 0) { - if (ret == 0) + if (ret == 0 && errno != ENOENT) ret = -errno; } } @@ -2756,6 +3010,46 @@ void status_welcome(void) { status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */ free(r); + +#elif defined(TARGET_GENTOO) + char *r; + + if (read_one_line_file("/etc/gentoo-release", &r) < 0) + return; + + truncate_nl(r); + + status_printf("Welcome to \x1B[1;34m%s\x1B[0m!\n", r); /* Light Blue for Gentoo */ + + free(r); + +#elif defined(TARGET_DEBIAN) + char *r; + + if (read_one_line_file("/etc/debian_version", &r) < 0) + return; + + truncate_nl(r); + + status_printf("Welcome to Debian \x1B[1;31m%s\x1B[0m!\n", r); /* 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." #endif @@ -2764,7 +3058,7 @@ void status_welcome(void) { char *replace_env(const char *format, char **env) { enum { WORD, - DOLLAR, + CURLY, VARIABLE } state = WORD; @@ -2779,11 +3073,11 @@ char *replace_env(const char *format, char **env) { case WORD: if (*e == '$') - state = DOLLAR; + state = CURLY; break; - case DOLLAR: - if (*e == '(') { + case CURLY: + if (*e == '{') { if (!(k = strnappend(r, word, e-word-1))) goto fail; @@ -2807,19 +3101,19 @@ char *replace_env(const char *format, char **env) { break; case VARIABLE: - if (*e == ')') { - char *t; + if (*e == '}') { + const char *t; - if ((t = strv_env_get_with_length(env, word+2, e-word-2))) { - if (!(k = strappend(r, t))) - goto fail; + if (!(t = strv_env_get_with_length(env, word+2, e-word-2))) + t = ""; - free(r); - r = k; + if (!(k = strappend(r, t))) + goto fail; - word = e+1; - } + free(r); + r = k; + word = e+1; state = WORD; } break; @@ -2839,12 +3133,52 @@ fail: char **replace_env_argv(char **argv, char **env) { char **r, **i; - unsigned k = 0; + unsigned k = 0, l = 0; + + l = strv_length(argv); - if (!(r = new(char*, strv_length(argv)+1))) + if (!(r = new(char*, l+1))) return NULL; STRV_FOREACH(i, argv) { + + /* If $FOO appears as single word, replace it by the split up variable */ + if ((*i)[0] == '$' && (*i)[1] != '{') { + char *e; + char **w, **m; + unsigned q; + + if ((e = strv_env_get(env, *i+1))) { + + if (!(m = strv_split_quoted(e))) { + r[k] = NULL; + strv_free(r); + return NULL; + } + } else + m = NULL; + + q = strv_length(m); + l = l + q - 1; + + if (!(w = realloc(r, sizeof(char*) * (l+1)))) { + r[k] = NULL; + strv_free(r); + strv_free(m); + return NULL; + } + + r = w; + if (m) { + memcpy(r + k, m, q * sizeof(char*)); + free(m); + } + + k += q; + continue; + } + + /* If ${FOO} appears as part of a word, replace it by the variable as-is */ if (!(r[k++] = replace_env(*i, env))) { strv_free(r); return NULL; @@ -2869,7 +3203,7 @@ int columns(void) { struct winsize ws; zero(ws); - if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0) parsed_columns = ws.ws_col; } @@ -2898,6 +3232,391 @@ int running_in_chroot(void) { a.st_ino != b.st_ino; } +char *ellipsize(const char *s, unsigned length, unsigned percent) { + size_t l, x; + char *r; + + assert(s); + assert(percent <= 100); + assert(length >= 3); + + l = strlen(s); + + if (l <= 3 || l <= length) + return strdup(s); + + if (!(r = new0(char, length+1))) + return r; + + x = (length * percent) / 100; + + if (x > length - 3) + x = length - 3; + + memcpy(r, s, x); + r[x] = '.'; + r[x+1] = '.'; + r[x+2] = '.'; + memcpy(r + x + 3, + s + l - (length - x - 3), + length - x - 3); + + return r; +} + +int touch(const char *path) { + int fd; + + assert(path); + + if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0) + return -errno; + + close_nointr_nofail(fd); + return 0; +} + +char *unquote(const char *s, const char* quotes) { + size_t l; + assert(s); + + if ((l = strlen(s)) < 2) + return strdup(s); + + if (strchr(quotes, s[0]) && s[l-1] == s[0]) + return strndup(s+1, l-2); + + return strdup(s); +} + +int wait_for_terminate(pid_t pid, siginfo_t *status) { + assert(pid >= 1); + assert(status); + + for (;;) { + zero(*status); + + if (waitid(P_PID, pid, status, WEXITED) < 0) { + + if (errno == EINTR) + continue; + + return -errno; + } + + return 0; + } +} + +int wait_for_terminate_and_warn(const char *name, pid_t pid) { + int r; + siginfo_t status; + + assert(name); + assert(pid > 1); + + if ((r = wait_for_terminate(pid, &status)) < 0) { + log_warning("Failed to wait for %s: %s", name, strerror(-r)); + return r; + } + + 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; + } + + log_debug("%s succeeded.", name); + return 0; + + } else if (status.si_code == CLD_KILLED || + status.si_code == CLD_DUMPED) { + + log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status)); + return -EPROTO; + } + + log_warning("%s failed due to unknown reason.", name); + return -EPROTO; + +} + +void freeze(void) { + for (;;) + pause(); +} + +bool null_or_empty(struct stat *st) { + assert(st); + + if (S_ISREG(st->st_mode) && st->st_size <= 0) + return true; + + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) + return true; + + return false; +} + +DIR *xopendirat(int fd, const char *name) { + return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); +} + +int signal_from_string_try_harder(const char *s) { + int signo; + assert(s); + + if ((signo = signal_from_string(s)) <= 0) + if (startswith(s, "SIG")) + return signal_from_string(s+3); + + 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 + }; + + assert(message); + assert(_passphrase); + + if (flag_file) { + if ((notify = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) { + r = -errno; + goto finish; + } + + if (inotify_add_watch(notify, flag_file, IN_ATTRIB /* for the link count */) < 0) { + r = -errno; + goto finish; + } + } + + if ((ttyfd = open("/dev/tty", O_RDWR|O_NOCTTY|O_CLOEXEC)) >= 0) { + + if (tcgetattr(ttyfd, &old_termios) < 0) { + r = -errno; + goto finish; + } + + 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); + + new_termios = old_termios; + new_termios.c_lflag &= ~(ICANON|ECHO); + new_termios.c_cc[VMIN] = 1; + new_termios.c_cc[VTIME] = 0; + + if (tcsetattr(ttyfd, TCSADRAIN, &new_termios) < 0) { + r = -errno; + goto finish; + } + + reset_tty = true; + } + + zero(pollfd); + + pollfd[POLL_TTY].fd = ttyfd >= 0 ? ttyfd : STDIN_FILENO; + pollfd[POLL_TTY].events = POLLIN; + pollfd[POLL_INOTIFY].fd = notify; + pollfd[POLL_INOTIFY].events = POLLIN; + + for (;;) { + char c; + int sleep_for = -1, k; + ssize_t n; + + if (until > 0) { + usec_t y; + + y = now(CLOCK_MONOTONIC); + + if (y > until) { + r = -ETIMEDOUT; + goto finish; + } + + sleep_for = (int) ((until - y) / USEC_PER_MSEC); + } + + if (flag_file) + if (access(flag_file, F_OK) < 0) { + r = -errno; + goto finish; + } + + if ((k = poll(pollfd, notify > 0 ? 2 : 1, sleep_for)) < 0) { + + if (errno == EINTR) + continue; + + r = -errno; + goto finish; + } else if (k == 0) { + r = -ETIMEDOUT; + goto finish; + } + + if (notify > 0 && pollfd[POLL_INOTIFY].revents != 0) + flush_fd(notify); + + if (pollfd[POLL_TTY].revents == 0) + continue; + + if ((n = read(ttyfd >= 0 ? ttyfd : STDIN_FILENO, &c, 1)) < 0) { + + if (errno == EINTR || errno == EAGAIN) + continue; + + r = -errno; + goto finish; + + } else if (n == 0) + break; + + if (c == '\n') + break; + else if (c == 21) { + + while (p > 0) { + p--; + + if (ttyfd >= 0) + loop_write(ttyfd, "\b \b", 3, false); + } + + } else if (c == '\b' || c == 127) { + if (p > 0) { + p--; + + if (ttyfd >= 0) + loop_write(ttyfd, "\b \b", 3, false); + } + } else { + passphrase[p++] = c; + + if (ttyfd >= 0) + loop_write(ttyfd, "*", 1, false); + } + } + + if (ttyfd >= 0) + loop_write(ttyfd, "\n", 1, false); + + passphrase[p] = 0; + + if (!(*_passphrase = strdup(passphrase))) { + r = -ENOMEM; + goto finish; + } + + r = 0; + +finish: + if (notify >= 0) + close_nointr_nofail(notify); + + if (ttyfd >= 0) { + if (reset_tty) + tcsetattr(ttyfd, TCSADRAIN, &old_termios); + + close_nointr_nofail(ttyfd); + } + + return r; +} + +void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { + + assert(f); + assert(name); + assert(t); + + if (!dual_timestamp_is_set(t)) + return; + + fprintf(f, "%s=%llu %llu\n", + name, + (unsigned long long) t->realtime, + (unsigned long long) t->monotonic); +} + +void dual_timestamp_deserialize(const char *value, dual_timestamp *t) { + unsigned long long a, b; + + assert(value); + assert(t); + + 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; + } +} + + +char *fstab_node_to_udev_node(const char *p) { + 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); + + if (r < 0) + return NULL; + + return dn; + } + + if (startswith(p, "UUID=")) { + + if (!(u = unquote(p+5, "\"\'"))) + return NULL; + + t = xescape(u, "/ "); + free(u); + + if (!t) + return NULL; + + r = asprintf(&dn, "/dev/disk/by-uuid/%s", ascii_strlower(t)); + free(t); + + if (r < 0) + return NULL; + + return dn; + } + + return strdup(p); +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -3012,7 +3731,9 @@ static const char *const signal_table[] = { [SIGPIPE] = "PIPE", [SIGALRM] = "ALRM", [SIGTERM] = "TERM", - [SIGSTKFLT] = "STKFLT", +#ifdef SIGSTKFLT + [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */ +#endif [SIGCHLD] = "CHLD", [SIGCONT] = "CONT", [SIGSTOP] = "STOP",