X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=98c07163da28e8e871ef976392300c67f8d94bbe;hp=3d16cd1a13a5c52d4a30d955a5235adcefe8c3c7;hb=1dedb74a2e1d840b531b76b01a76979f3b57456b;hpb=58d617429dcfb6fb1ea0f7b6cbce82266ccbb1aa diff --git a/src/shared/util.c b/src/shared/util.c index 3d16cd1a1..98c07163d 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -175,25 +175,24 @@ char* first_word(const char *s, const char *word) { } int close_nointr(int fd) { - int r; - assert(fd >= 0); - r = close(fd); - if (r >= 0) - return r; - else if (errno == EINTR) - /* - * Just ignore EINTR; a retry loop is the wrong - * thing to do on Linux. - * - * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html - * https://bugzilla.gnome.org/show_bug.cgi?id=682819 - * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR - * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain - */ + + if (close(fd) >= 0) return 0; - else - return -errno; + + /* + * Just ignore EINTR; a retry loop is the wrong thing to do on + * Linux. + * + * http://lkml.indiana.edu/hypermail/linux/kernel/0509.1/0877.html + * https://bugzilla.gnome.org/show_bug.cgi?id=682819 + * http://utcc.utoronto.ca/~cks/space/blog/unix/CloseEINTR + * https://sites.google.com/site/michaelsafyan/software-engineering/checkforeintrwheninvokingclosethinkagain + */ + if (errno == EINTR) + return 0; + + return -errno; } int safe_close(int fd) { @@ -938,7 +937,7 @@ int readlink_and_canonicalize(const char *p, char **r) { } int reset_all_signal_handlers(void) { - int sig; + int sig, r = 0; for (sig = 1; sig < _NSIG; sig++) { struct sigaction sa = { @@ -946,16 +945,29 @@ int reset_all_signal_handlers(void) { .sa_flags = SA_RESTART, }; + /* These two cannot be caught... */ if (sig == SIGKILL || sig == SIGSTOP) continue; /* On Linux the first two RT signals are reserved by * glibc, and sigaction() will return EINVAL for them. */ if ((sigaction(sig, &sa, NULL) < 0)) - if (errno != EINVAL) - return -errno; + if (errno != EINVAL && r == 0) + r = -errno; } + return r; +} + +int reset_signal_mask(void) { + sigset_t ss; + + if (sigemptyset(&ss) < 0) + return -errno; + + if (sigprocmask(SIG_SETMASK, &ss, NULL) < 0) + return -errno; + return 0; } @@ -2084,12 +2096,14 @@ fail: } int release_terminal(void) { - int r = 0; - struct sigaction sa_old, sa_new = { + static const struct sigaction sa_new = { .sa_handler = SIG_IGN, .sa_flags = SA_RESTART, }; - _cleanup_close_ int fd; + + _cleanup_close_ int fd = -1; + struct sigaction sa_old; + int r = 0; fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC); if (fd < 0) @@ -3940,7 +3954,6 @@ void execute_directory(const char *directory, DIR *d, usec_t timeout, char *argv _exit(EXIT_FAILURE); } - log_debug("Spawned %s as " PID_FMT ".", path, pid); r = hashmap_put(pids, UINT_TO_PTR(pid), path); @@ -5130,6 +5143,12 @@ int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *pa /* Don't leak fds to the agent */ close_all_fds(except, n_except); + /* Make sure we actually can kill the agent, if we need to, in + * case somebody invoked us from a shell script that trapped + * SIGTERM or so... */ + reset_all_signal_handlers(); + reset_signal_mask(); + stdout_is_tty = isatty(STDOUT_FILENO); stderr_is_tty = isatty(STDERR_FILENO); @@ -6868,11 +6887,13 @@ char *tempfn_random(const char *p) { bool is_localhost(const char *hostname) { assert(hostname); - /* This tries to identify local hostnames described in RFC6761 - * plus the redhatism of .localdomain */ + /* This tries to identify local host and domain names + * described in RFC6761 plus the redhatism of .localdomain */ return streq(hostname, "localhost") || streq(hostname, "localhost.") || + streq(hostname, "localdomain.") || + streq(hostname, "localdomain") || endswith(hostname, ".localhost") || endswith(hostname, ".localhost.") || endswith(hostname, ".localdomain") || @@ -6918,3 +6939,241 @@ int take_password_lock(const char *root) { return fd; } + +int is_symlink(const char *path) { + struct stat info; + + if (lstat(path, &info) < 0) + return -errno; + + if (S_ISLNK(info.st_mode)) + return 1; + + return 0; +} + +int unquote_first_word(const char **p, char **ret) { + _cleanup_free_ char *s = NULL; + size_t allocated = 0, sz = 0; + + enum { + START, + VALUE, + VALUE_ESCAPE, + SINGLE_QUOTE, + SINGLE_QUOTE_ESCAPE, + DOUBLE_QUOTE, + DOUBLE_QUOTE_ESCAPE, + SPACE, + } state = START; + + assert(p); + assert(*p); + assert(ret); + + /* Parses the first word of a string, and returns it in + * *ret. Removes all quotes in the process. When parsing fails + * (because of an uneven number of quotes or similar), leaves + * the pointer *p at the first invalid character. */ + + for (;;) { + char c = **p; + + switch (state) { + + case START: + if (c == 0) + goto finish; + else if (strchr(WHITESPACE, c)) + break; + + state = VALUE; + /* fallthrough */ + + case VALUE: + if (c == 0) + goto finish; + else if (c == '\'') + state = SINGLE_QUOTE; + else if (c == '\\') + state = VALUE_ESCAPE; + else if (c == '\"') + state = DOUBLE_QUOTE; + else if (strchr(WHITESPACE, c)) + state = SPACE; + else { + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + } + + break; + + case VALUE_ESCAPE: + if (c == 0) + return -EINVAL; + + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + state = VALUE; + + break; + + case SINGLE_QUOTE: + if (c == 0) + return -EINVAL; + else if (c == '\'') + state = VALUE; + else if (c == '\\') + state = SINGLE_QUOTE_ESCAPE; + else { + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + } + + break; + + case SINGLE_QUOTE_ESCAPE: + if (c == 0) + return -EINVAL; + + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + state = SINGLE_QUOTE; + break; + + case DOUBLE_QUOTE: + if (c == 0) + return -EINVAL; + else if (c == '\"') + state = VALUE; + else if (c == '\\') + state = DOUBLE_QUOTE_ESCAPE; + else { + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + } + + break; + + case DOUBLE_QUOTE_ESCAPE: + if (c == 0) + return -EINVAL; + + if (!GREEDY_REALLOC(s, allocated, sz+2)) + return -ENOMEM; + + s[sz++] = c; + state = DOUBLE_QUOTE; + break; + + case SPACE: + if (c == 0) + goto finish; + if (!strchr(WHITESPACE, c)) + goto finish; + + break; + } + + (*p) ++; + } + +finish: + if (!s) { + *ret = NULL; + return 0; + } + + s[sz] = 0; + *ret = s; + s = NULL; + + return 1; +} + +int unquote_many_words(const char **p, ...) { + va_list ap; + char **l; + int n = 0, i, c, r; + + /* Parses a number of words from a string, stripping any + * quotes if necessary. */ + + assert(p); + + /* Count how many words are expected */ + va_start(ap, p); + for (;;) { + if (!va_arg(ap, char **)) + break; + n++; + } + va_end(ap); + + if (n <= 0) + return 0; + + /* Read all words into a temporary array */ + l = newa0(char*, n); + for (c = 0; c < n; c++) { + + r = unquote_first_word(p, &l[c]); + if (r < 0) { + int j; + + for (j = 0; j < c; j++) + free(l[j]); + + return r; + } + + if (r == 0) + break; + } + + /* If we managed to parse all words, return them in the passed + * in parameters */ + va_start(ap, p); + for (i = 0; i < n; i++) { + char **v; + + v = va_arg(ap, char **); + assert(v); + + *v = l[i]; + } + va_end(ap); + + return c; +} + +int free_and_strdup(char **p, const char *s) { + char *t; + + assert(p); + + /* Replaces a string pointer with an strdup()ed new string, + * possibly freeing the old one. */ + + if (s) { + t = strdup(s); + if (!t) + return -ENOMEM; + } else + t = NULL; + + free(*p); + *p = t; + + return 0; +}