#include <linux/fs.h>
/* When we include libgen.h because we need dirname() we immediately
- * undefine basename() since libgen.h defines it as a macro to the XDG
- * version which is really broken. */
+ * undefine basename() since libgen.h defines it as a macro to the POSIX
+ * version which is really broken. We prefer GNU basename(). */
#include <libgen.h>
#undef basename
#include "random-util.h"
#include "terminal-util.h"
#include "hostname-util.h"
+#include "signal-util.h"
/* Put this test here for a lack of better place */
assert_cc(EAGAIN == EWOULDBLOCK);
return (char*) s + sl - pl;
}
+char* endswith_no_case(const char *s, const char *postfix) {
+ size_t sl, pl;
+
+ assert(s);
+ assert(postfix);
+
+ sl = strlen(s);
+ pl = strlen(postfix);
+
+ if (pl == 0)
+ return (char*) s + sl;
+
+ if (sl < pl)
+ return NULL;
+
+ if (strcasecmp(s + sl - pl, postfix) != 0)
+ return NULL;
+
+ return (char*) s + sl - pl;
+}
+
char* first_word(const char *s, const char *word) {
size_t sl, wl;
const char *p;
return 0;
}
-int reset_all_signal_handlers(void) {
- int sig, r = 0;
-
- for (sig = 1; sig < _NSIG; sig++) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .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 && 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;
-}
-
char *strstrip(char *s) {
char *e;
int a, b, c;
uint32_t m;
- if (length != (size_t) -1 && length < 4)
+ if (length != (size_t) -1 && length < 3)
return -EINVAL;
a = unoctchar(p[0]);
}
}
-int sigaction_many(const struct sigaction *sa, ...) {
- va_list ap;
- int r = 0, sig;
-
- va_start(ap, sa);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int ignore_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_IGN,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
-int default_signals(int sig, ...) {
- struct sigaction sa = {
- .sa_handler = SIG_DFL,
- .sa_flags = SA_RESTART,
- };
- va_list ap;
- int r = 0;
-
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
-
- va_start(ap, sig);
- while ((sig = va_arg(ap, int)) > 0)
- if (sigaction(sig, &sa, NULL) < 0)
- r = -errno;
- va_end(ap);
-
- return r;
-}
-
void safe_close_pair(int p[]) {
assert(p);
}
}
-void sigset_add_many(sigset_t *ss, ...) {
- va_list ap;
- int sig;
-
- assert(ss);
-
- va_start(ap, ss);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(ss, sig) == 0);
- va_end(ap);
-}
-
-int sigprocmask_many(int how, ...) {
- va_list ap;
- sigset_t ss;
- int sig;
-
- assert_se(sigemptyset(&ss) == 0);
-
- va_start(ap, how);
- while ((sig = va_arg(ap, int)) > 0)
- assert_se(sigaddset(&ss, sig) == 0);
- va_end(ap);
-
- if (sigprocmask(how, &ss, NULL) < 0)
- return -errno;
-
- return 0;
-}
char *lookup_uid(uid_t uid) {
long bufsize;
char *name;
return d;
}
-int signal_from_string_try_harder(const char *s) {
- int signo;
- assert(s);
-
- signo = signal_from_string(s);
- if (signo <= 0)
- if (startswith(s, "SIG"))
- return signal_from_string(s+3);
-
- return signo;
-}
-
static char *tag_to_udev_node(const char *tagvalue, const char *by) {
_cleanup_free_ char *t = NULL, *u = NULL;
size_t enc_len;
/* 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();
- reset_signal_mask();
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
assert_se(prctl(PR_SET_PDEATHSIG, SIGTERM) == 0);
assert(_f);
assert(_temp_path);
- r = tempfn_xxxxxx(path, &t);
+ r = tempfn_xxxxxx(path, NULL, &t);
if (r < 0)
return r;
assert(from);
assert(to);
- r = tempfn_random(to, &t);
+ r = tempfn_random(to, NULL, &t);
if (r < 0)
return r;
assert(path);
- r = tempfn_random(path, &t);
+ r = tempfn_random(path, NULL, &t);
if (r < 0)
return r;
assert(path);
- r = tempfn_random(path, &t);
+ r = tempfn_random(path, NULL, &t);
if (r < 0)
return r;
DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff);
-static const char *const __signal_table[] = {
- [SIGHUP] = "HUP",
- [SIGINT] = "INT",
- [SIGQUIT] = "QUIT",
- [SIGILL] = "ILL",
- [SIGTRAP] = "TRAP",
- [SIGABRT] = "ABRT",
- [SIGBUS] = "BUS",
- [SIGFPE] = "FPE",
- [SIGKILL] = "KILL",
- [SIGUSR1] = "USR1",
- [SIGSEGV] = "SEGV",
- [SIGUSR2] = "USR2",
- [SIGPIPE] = "PIPE",
- [SIGALRM] = "ALRM",
- [SIGTERM] = "TERM",
-#ifdef SIGSTKFLT
- [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
-#endif
- [SIGCHLD] = "CHLD",
- [SIGCONT] = "CONT",
- [SIGSTOP] = "STOP",
- [SIGTSTP] = "TSTP",
- [SIGTTIN] = "TTIN",
- [SIGTTOU] = "TTOU",
- [SIGURG] = "URG",
- [SIGXCPU] = "XCPU",
- [SIGXFSZ] = "XFSZ",
- [SIGVTALRM] = "VTALRM",
- [SIGPROF] = "PROF",
- [SIGWINCH] = "WINCH",
- [SIGIO] = "IO",
- [SIGPWR] = "PWR",
- [SIGSYS] = "SYS"
-};
-
-DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
-
-const char *signal_to_string(int signo) {
- static thread_local char buf[sizeof("RTMIN+")-1 + DECIMAL_STR_MAX(int) + 1];
- const char *name;
-
- name = __signal_to_string(signo);
- if (name)
- return name;
-
- if (signo >= SIGRTMIN && signo <= SIGRTMAX)
- snprintf(buf, sizeof(buf), "RTMIN+%d", signo - SIGRTMIN);
- else
- snprintf(buf, sizeof(buf), "%d", signo);
-
- return buf;
-}
-
-int signal_from_string(const char *s) {
- int signo;
- int offset = 0;
- unsigned u;
-
- signo = __signal_from_string(s);
- if (signo > 0)
- return signo;
-
- if (startswith(s, "RTMIN+")) {
- s += 6;
- offset = SIGRTMIN;
- }
- if (safe_atou(s, &u) >= 0) {
- signo = (int) u + offset;
- if (signo > 0 && signo < _NSIG)
- return signo;
- }
- return -EINVAL;
-}
-
bool kexec_loaded(void) {
bool loaded = false;
char *s;
/* 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();
+ (void) reset_all_signal_handlers();
+ (void) reset_signal_mask();
/* Check whether our parent died before we were able
* to set the death signal and unblock the signals */
if (param) {
- r = write_string_file(REBOOT_PARAM_FILE, param);
+ r = write_string_file(REBOOT_PARAM_FILE, param, WRITE_STRING_FILE_CREATE);
if (r < 0)
log_error("Failed to write reboot param to "
REBOOT_PARAM_FILE": %s", strerror(-r));
while ((x = set_steal_first(todo))) {
r = set_consume(done, x);
- if (r == -EEXIST)
+ if (r == -EEXIST || r == 0)
continue;
if (r < 0)
return r;
return 0;
}
-int tempfn_xxxxxx(const char *p, char **ret) {
+int tempfn_xxxxxx(const char *p, const char *extra, char **ret) {
const char *fn;
char *t;
* /foo/bar/waldo
*
* Into this:
- * /foo/bar/.#waldoXXXXXX
+ * /foo/bar/.#<extra>waldoXXXXXX
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
- t = new(char, strlen(p) + 2 + 6 + 1);
+ if (extra == NULL)
+ extra = "";
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 6 + 1);
if (!t)
return -ENOMEM;
- strcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn), "XXXXXX");
+ strcpy(stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn), "XXXXXX");
*ret = path_kill_slashes(t);
return 0;
}
-int tempfn_random(const char *p, char **ret) {
+int tempfn_random(const char *p, const char *extra, char **ret) {
const char *fn;
char *t, *x;
uint64_t u;
* /foo/bar/waldo
*
* Into this:
- * /foo/bar/.#waldobaa2a261115984a9
+ * /foo/bar/.#<extra>waldobaa2a261115984a9
*/
fn = basename(p);
if (!filename_is_valid(fn))
return -EINVAL;
- t = new(char, strlen(p) + 2 + 16 + 1);
+ if (!extra)
+ extra = "";
+
+ t = new(char, strlen(p) + 2 + strlen(extra) + 16 + 1);
if (!t)
return -ENOMEM;
- x = stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), fn);
+ x = stpcpy(stpcpy(stpcpy(mempcpy(t, p, fn - p), ".#"), extra), fn);
u = random_u64();
for (i = 0; i < 16; i++) {
return 0;
}
-int tempfn_random_child(const char *p, char **ret) {
+int tempfn_random_child(const char *p, const char *extra, char **ret) {
char *t, *x;
uint64_t u;
unsigned i;
/* Turns this:
* /foo/bar/waldo
* Into this:
- * /foo/bar/waldo/.#3c2b6219aa75d7d0
+ * /foo/bar/waldo/.#<extra>3c2b6219aa75d7d0
*/
- t = new(char, strlen(p) + 3 + 16 + 1);
+ if (!extra)
+ extra = "";
+
+ t = new(char, strlen(p) + 3 + strlen(extra) + 16 + 1);
if (!t)
return -ENOMEM;
- x = stpcpy(stpcpy(t, p), "/.#");
+ x = stpcpy(stpcpy(stpcpy(t, p), "/.#"), extra);
u = random_u64();
for (i = 0; i < 16; i++) {
break;
- case VALUE_ESCAPE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- }
-
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (flags & UNQUOTE_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
- return -EINVAL;
-
- (*p) += r - 1;
-
- if (c != 0)
- s[sz++] = c; /* normal explicit char */
- else
- sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
- } else
- s[sz++] = c;
-
- state = VALUE;
- break;
-
case SINGLE_QUOTE:
if (c == 0) {
if (flags & UNQUOTE_RELAX)
break;
- case SINGLE_QUOTE_ESCAPE:
- if (c == 0) {
- if (flags & UNQUOTE_RELAX)
- goto finish;
- return -EINVAL;
- }
-
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
- if (flags & UNQUOTE_CUNESCAPE) {
- uint32_t u;
-
- r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
- return -EINVAL;
-
- (*p) += r - 1;
-
- if (c != 0)
- s[sz++] = c;
- else
- sz += utf8_encode_unichar(s + sz, u);
- } else
- s[sz++] = c;
-
- state = SINGLE_QUOTE;
- break;
-
case DOUBLE_QUOTE:
if (c == 0)
return -EINVAL;
break;
+ case SINGLE_QUOTE_ESCAPE:
case DOUBLE_QUOTE_ESCAPE:
+ case VALUE_ESCAPE:
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
+ return -ENOMEM;
+
if (c == 0) {
+ if ((flags & UNQUOTE_CUNESCAPE_RELAX) &&
+ (state == VALUE_ESCAPE || flags & UNQUOTE_RELAX)) {
+ /* If we find an unquoted trailing backslash and we're in
+ * UNQUOTE_CUNESCAPE_RELAX mode, keep it verbatim in the
+ * output.
+ *
+ * Unbalanced quotes will only be allowed in UNQUOTE_RELAX
+ * mode, UNQUOTE_CUNESCAP_RELAX mode does not allow them.
+ */
+ s[sz++] = '\\';
+ goto finish;
+ }
if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+7))
- return -ENOMEM;
-
if (flags & UNQUOTE_CUNESCAPE) {
uint32_t u;
r = cunescape_one(*p, (size_t) -1, &c, &u);
- if (r < 0)
+ if (r < 0) {
+ if (flags & UNQUOTE_CUNESCAPE_RELAX) {
+ s[sz++] = '\\';
+ s[sz++] = c;
+ goto end_escape;
+ }
return -EINVAL;
+ }
(*p) += r - 1;
if (c != 0)
- s[sz++] = c;
+ s[sz++] = c; /* normal explicit char */
else
- sz += utf8_encode_unichar(s + sz, u);
+ sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */
} else
s[sz++] = c;
- state = DOUBLE_QUOTE;
+end_escape:
+ state = (state == SINGLE_QUOTE_ESCAPE) ? SINGLE_QUOTE :
+ (state == DOUBLE_QUOTE_ESCAPE) ? DOUBLE_QUOTE :
+ VALUE;
break;
case SPACE:
return 1;
}
+int unquote_first_word_and_warn(
+ const char **p,
+ char **ret,
+ UnquoteFlags flags,
+ const char *unit,
+ const char *filename,
+ unsigned line,
+ const char *rvalue) {
+ /* Try to unquote it, if it fails, warn about it and try again but this
+ * time using UNQUOTE_CUNESCAPE_RELAX to keep the backslashes verbatim
+ * in invalid escape sequences. */
+ const char *save;
+ int r;
+
+ save = *p;
+ r = unquote_first_word(p, ret, flags);
+ if (r < 0 && !(flags&UNQUOTE_CUNESCAPE_RELAX)) {
+ /* Retry it with UNQUOTE_CUNESCAPE_RELAX. */
+ *p = save;
+ r = unquote_first_word(p, ret, flags|UNQUOTE_CUNESCAPE_RELAX);
+ if (r < 0)
+ log_syntax(unit, LOG_ERR, filename, line, EINVAL,
+ "Unbalanced quoting in command line, ignoring: \"%s\"", rvalue);
+ else
+ log_syntax(unit, LOG_WARNING, filename, line, EINVAL,
+ "Invalid escape sequences in command line: \"%s\"", rvalue);
+ }
+ return r;
+}
+
int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
va_list ap;
char **l;
if (recvmsg(pair[0], &mh, MSG_NOSIGNAL|MSG_CMSG_CLOEXEC) < 0)
return -errno;
- for (cmsg = CMSG_FIRSTHDR(&mh); cmsg; cmsg = CMSG_NXTHDR(&mh, cmsg))
+ CMSG_FOREACH(cmsg, &mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
int *fds;
unsigned n_fds;
assert(mh);
- for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg))
+ CMSG_FOREACH(cmsg, mh)
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
close_many((int*) CMSG_DATA(cmsg), (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int));
}