X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=0939ea9456bde70eaf193a65002ffe8d0665774c;hp=da8a6c33361863a08d32b60c39393e3675bec237;hb=f6943caa53ac486cc8de580160954ba2b8ffd504;hpb=c24eb49e6aecd6de2ad450083e826d4c9d9c75b6 diff --git a/src/util.c b/src/util.c index da8a6c333..0939ea945 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. @@ -48,6 +48,8 @@ #include #include #include +#include +#include #include "macro.h" #include "util.h" @@ -55,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) { @@ -500,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); @@ -526,6 +530,149 @@ 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 *seperator, ...) { + + int r = 0; + char *contents, *p; + + assert(fname); + assert(seperator); + + if ((r = read_full_file(fname, &contents)) < 0) + return r; + + p = contents; + for (;;) { + const char *key = NULL; + + p += strspn(p, seperator); + p += strspn(p, WHITESPACE); + + if (!*p) + break; + + if (!strchr(COMMENTS, *p)) { + va_list ap; + char **value; + + va_start(ap, seperator); + 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, seperator); + + 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; + } + + free(*value); + *value = v; + + p += n; + + r ++; + break; + } + va_end(ap); + } + + if (!key) + p += strcspn(p, seperator); + } + +fail: + free(contents); + return r; +} + char *truncate_nl(char *s) { assert(s); @@ -969,7 +1116,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; @@ -1012,7 +1159,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) @@ -1028,7 +1175,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; @@ -1656,6 +1803,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; @@ -1679,6 +1883,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++) { @@ -1799,23 +2009,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); @@ -1851,9 +2067,9 @@ int reset_terminal(int fd) { assert(fd >= 0); - /* First, unlock termios */ - zero(termios); - ioctl(fd, TIOCSLCKTRMIOS, &termios); + /* 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); @@ -2390,6 +2606,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 @@ -2485,18 +2711,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; @@ -2535,11 +2749,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); @@ -2770,6 +2985,32 @@ 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_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 @@ -2822,18 +3063,18 @@ char *replace_env(const char *format, char **env) { case VARIABLE: if (*e == '}') { - char *t; + 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; @@ -2863,36 +3104,39 @@ char **replace_env_argv(char **argv, char **env) { STRV_FOREACH(i, argv) { /* If $FOO appears as single word, replace it by the split up variable */ - if ((*i)[0] == '$') { - char *e = strv_env_get(env, *i+1); + if ((*i)[0] == '$' && (*i)[1] != '{') { + char *e; + char **w, **m; + unsigned q; - if (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; + 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; - } + if (!(w = realloc(r, sizeof(char*) * (l+1)))) { + r[k] = NULL; + strv_free(r); + strv_free(m); + return NULL; + } - r = w; + r = w; + if (m) { memcpy(r + k, m, q * sizeof(char*)); free(m); - - k += q; - continue; } + + k += q; + continue; } /* If ${FOO} appears as part of a word, replace it by the variable as-is */ @@ -2920,7 +3164,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; } @@ -2981,6 +3225,83 @@ char *ellipsize(const char *s, unsigned length, unsigned percent) { 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; + +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", @@ -3095,7 +3416,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",