}
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) {
}
int reset_all_signal_handlers(void) {
- int sig;
+ int sig, r = 0;
for (sig = 1; sig < _NSIG; sig++) {
struct sigaction sa = {
.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;
}
c++;
}
- if (fd < 0)
- return -errno;
-
r = isatty(fd);
if (r < 0) {
safe_close(fd);
* ended our handle will be dead. It's important that
* we do this after sleeping, so that we don't enter
* an endless loop. */
- safe_close(fd);
+ fd = safe_close(fd);
}
safe_close(notify);
}
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)
return !isempty(u.nodename) && !streq(u.nodename, "(none)");
}
-static char *lookup_uid(uid_t uid) {
+char *lookup_uid(uid_t uid) {
long bufsize;
char *name;
_cleanup_free_ char *buf = NULL;
if (emax < 3)
emax = 3;
- e = ellipsize(s, emax, 75);
+ e = ellipsize(s, emax, 50);
if (e) {
free(s);
s = e;
c = 0;
e = getenv("COLUMNS");
if (e)
- safe_atoi(e, &c);
+ (void) safe_atoi(e, &c);
if (c <= 0)
c = fd_columns(STDOUT_FILENO);
l = 0;
e = getenv("LINES");
if (e)
- safe_atou(e, &l);
+ (void) safe_atou(e, &l);
if (l <= 0)
l = fd_lines(STDOUT_FILENO);
_cleanup_hashmap_free_free_ Hashmap *pids = NULL;
_cleanup_closedir_ DIR *_d = NULL;
struct dirent *de;
- sigset_t ss;
/* We fork this all off from a child process so that
* we can somewhat cleanly make use of SIGALRM to set
* a time limit */
reset_all_signal_handlers();
-
- assert_se(sigemptyset(&ss) == 0);
- assert_se(sigprocmask(SIG_SETMASK, &ss, NULL) == 0);
+ reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
}
}
- pids = hashmap_new(NULL, NULL);
+ pids = hashmap_new(NULL);
if (!pids) {
log_oom();
_exit(EXIT_FAILURE);
_exit(EXIT_FAILURE);
}
-
log_debug("Spawned %s as " PID_FMT ".", path, pid);
r = hashmap_put(pids, UINT_TO_PTR(pid), path);
return loaded;
}
-int strdup_or_null(const char *a, char **b) {
- char *c;
-
- assert(b);
-
- if (!a) {
- *b = NULL;
- return 0;
- }
-
- c = strdup(a);
- if (!c)
- return -ENOMEM;
-
- *b = c;
- return 0;
-}
-
int prot_from_flags(int flags) {
switch (flags & O_ACCMODE) {
}
int fork_agent(pid_t *pid, const int except[], unsigned n_except, const char *path, ...) {
- pid_t parent_pid, agent_pid;
- int fd;
bool stdout_is_tty, stderr_is_tty;
+ pid_t parent_pid, agent_pid;
+ sigset_t ss, saved_ss;
unsigned n, i;
va_list ap;
char **l;
assert(pid);
assert(path);
- parent_pid = getpid();
-
/* Spawns a temporary TTY agent, making sure it goes away when
* we go away */
+ parent_pid = getpid();
+
+ /* First we temporarily block all signals, so that the new
+ * child has them blocked initially. This way, we can be sure
+ * that SIGTERMs are not lost we might send to the agent. */
+ assert_se(sigfillset(&ss) >= 0);
+ assert_se(sigprocmask(SIG_SETMASK, &ss, &saved_ss) >= 0);
+
agent_pid = fork();
- if (agent_pid < 0)
+ if (agent_pid < 0) {
+ assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
return -errno;
+ }
if (agent_pid != 0) {
+ assert_se(sigprocmask(SIG_SETMASK, &saved_ss, NULL) >= 0);
*pid = agent_pid;
return 0;
}
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
+ /* 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();
+
/* Check whether our parent died before we were able
- * to set the death signal */
+ * to set the death signal and unblock the signals */
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
stderr_is_tty = isatty(STDERR_FILENO);
if (!stdout_is_tty || !stderr_is_tty) {
+ int fd;
+
/* Detach from stdout/stderr. and reopen
* /dev/tty for them. This is important to
* ensure that when systemctl is started via
path_kill_slashes(cleaned);
- done = set_new(string_hash_func, string_compare_func);
+ done = set_new(&string_hash_ops);
if (!done)
return -ENOMEM;
bool top_autofs = false;
char *x;
- todo = set_new(string_hash_func, string_compare_func);
+ todo = set_new(&string_hash_ops);
if (!todo)
return -ENOMEM;
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") ||
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;
+}