#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 "process-util.h"
#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;
char quotechars[2] = {*current, '\0'};
*l = strcspn_escaped(current + 1, quotechars);
- if (current[*l + 1] == '\0' ||
+ if (current[*l + 1] == '\0' || current[*l + 1] != quotechars[0] ||
(current[*l + 2] && !strchr(separator, current[*l + 2]))) {
/* right quote missing or garbage at the end */
*state = current;
return NULL;
}
- assert(current[*l + 1] == quotechars[0]);
*state = current++ + *l + 2;
} else if (quoted) {
*l = strcspn_escaped(current, separator);
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);
errno = 0;
- while (nbytes > 0) {
+ do {
ssize_t k;
k = write(fd, p, nbytes);
return -errno;
}
- if (k == 0) /* Can't really happen */
+ if (nbytes > 0 && k == 0) /* Can't really happen */
return -EIO;
p += k;
nbytes -= k;
- }
+ } while (nbytes > 0);
return 0;
}
int parse_size(const char *t, off_t base, off_t *size) {
- /* Soo, sometimes we want to parse IEC binary suffxies, and
+ /* Soo, sometimes we want to parse IEC binary suffixes, and
* sometimes SI decimal suffixes. This function can parse
* both. Which one is the right way depends on the
* context. Wikipedia suggests that SI is customary for
}
}
-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* gethostname_malloc(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- if (!isempty(u.nodename) && !streq(u.nodename, "(none)"))
- return strdup(u.nodename);
-
- return strdup(u.sysname);
-}
-
-bool hostname_is_set(void) {
- struct utsname u;
-
- assert_se(uname(&u) >= 0);
-
- return !isempty(u.nodename) && !streq(u.nodename, "(none)");
-}
-
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);
return s;
}
-static bool hostname_valid_char(char c) {
- return
- (c >= 'a' && c <= 'z') ||
- (c >= 'A' && c <= 'Z') ||
- (c >= '0' && c <= '9') ||
- c == '-' ||
- c == '_' ||
- c == '.';
-}
-
-bool hostname_is_valid(const char *s) {
- const char *p;
- bool dot;
-
- if (isempty(s))
- return false;
-
- /* Doesn't accept empty hostnames, hostnames with trailing or
- * leading dots, and hostnames with multiple dots in a
- * sequence. Also ensures that the length stays below
- * HOST_NAME_MAX. */
-
- for (p = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- return false;
-
- dot = true;
- } else {
- if (!hostname_valid_char(*p))
- return false;
-
- dot = false;
- }
- }
-
- if (dot)
- return false;
-
- if (p-s > HOST_NAME_MAX)
- return false;
-
- return true;
-}
-
-char* hostname_cleanup(char *s, bool lowercase) {
- char *p, *d;
- bool dot;
-
- for (p = s, d = s, dot = true; *p; p++) {
- if (*p == '.') {
- if (dot)
- continue;
-
- *(d++) = '.';
- dot = true;
- } else if (hostname_valid_char(*p)) {
- *(d++) = lowercase ? tolower(*p) : *p;
- dot = false;
- }
-
- }
-
- if (dot && d > s)
- d[-1] = 0;
- else
- *d = 0;
-
- strshorten(s, HOST_NAME_MAX);
-
- return s;
-}
-
bool machine_name_is_valid(const char *s) {
if (!hostname_is_valid(s))
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;
return 0;
}
+int symlink_idempotent(const char *from, const char *to) {
+ _cleanup_free_ char *p = NULL;
+ int r;
+
+ assert(from);
+ assert(to);
+
+ if (symlink(from, to) < 0) {
+ if (errno != EEXIST)
+ return -errno;
+
+ r = readlink_malloc(to, &p);
+ if (r < 0)
+ return r;
+
+ if (!streq(p, from))
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
int mknod_atomic(const char *path, mode_t mode, dev_t dev) {
_cleanup_free_ char *t = NULL;
int 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 */
return -errno;
}
- if (setresgid(0, 0, 0) < 0)
- return -errno;
-
- if (setgroups(0, NULL) < 0)
- return -errno;
-
- if (setresuid(0, 0, 0) < 0)
- return -errno;
-
- return 0;
+ return reset_uid_gid();
}
int getpeercred(int fd, struct ucred *ucred) {
#ifdef O_TMPFILE
/* Try O_TMPFILE first, if it is supported */
- fd = open(path, flags|O_TMPFILE, S_IRUSR|S_IWUSR);
+ fd = open(path, flags|O_TMPFILE|O_EXCL, S_IRUSR|S_IWUSR);
if (fd >= 0)
return fd;
#endif
return PER_LINUX;
#endif
- /* personality(7) documents that 0xffffffffUL is used for
- * querying the current personality, hence let's use that here
- * as error indicator. */
- return 0xffffffffUL;
+ return PERSONALITY_INVALID;
}
const char* personality_to_string(unsigned long p) {
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++) {
return 0;
}
-/* make sure the hostname is not "localhost" */
-bool is_localhost(const char *hostname) {
- assert(hostname);
-
- /* 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") ||
- endswith(hostname, ".localdomain.");
-}
-
int take_password_lock(const char *root) {
struct flock flock = {
return !!S_ISDIR(st.st_mode);
}
+int is_device_node(const char *path) {
+ struct stat info;
+
+ if (lstat(path, &info) < 0)
+ return -errno;
+
+ return !!(S_ISBLK(info.st_mode) || S_ISCHR(info.st_mode));
+}
+
int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
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;
/* Replaces a string pointer with an strdup()ed new string,
* possibly freeing the old one. */
+ if (streq_ptr(*p, s))
+ return 0;
+
if (s) {
t = strdup(s);
if (!t)
free(*p);
*p = t;
- return 0;
-}
-
-int sethostname_idempotent(const char *s) {
- int r;
- char buf[HOST_NAME_MAX + 1] = {};
-
- assert(s);
-
- r = gethostname(buf, sizeof(buf));
- if (r < 0)
- return -errno;
-
- if (streq(buf, s))
- return 0;
-
- r = sethostname(s, strlen(s));
- if (r < 0)
- return -errno;
-
return 1;
}
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;
return 0;
}
+int same_fd(int a, int b) {
+ struct stat sta, stb;
+ pid_t pid;
+ int r, fa, fb;
+
+ assert(a >= 0);
+ assert(b >= 0);
+
+ /* Compares two file descriptors. Note that semantics are
+ * quite different depending on whether we have kcmp() or we
+ * don't. If we have kcmp() this will only return true for
+ * dup()ed file descriptors, but not otherwise. If we don't
+ * have kcmp() this will also return true for two fds of the same
+ * file, created by separate open() calls. Since we use this
+ * call mostly for filtering out duplicates in the fd store
+ * this difference hopefully doesn't matter too much. */
+
+ if (a == b)
+ return true;
+
+ /* Try to use kcmp() if we have it. */
+ pid = getpid();
+ r = kcmp(pid, pid, KCMP_FILE, a, b);
+ if (r == 0)
+ return true;
+ if (r > 0)
+ return false;
+ if (errno != ENOSYS)
+ return -errno;
+
+ /* We don't have kcmp(), use fstat() instead. */
+ if (fstat(a, &sta) < 0)
+ return -errno;
+
+ if (fstat(b, &stb) < 0)
+ return -errno;
+
+ if ((sta.st_mode & S_IFMT) != (stb.st_mode & S_IFMT))
+ return false;
+
+ /* We consider all device fds different, since two device fds
+ * might refer to quite different device contexts even though
+ * they share the same inode and backing dev_t. */
+
+ if (S_ISCHR(sta.st_mode) || S_ISBLK(sta.st_mode))
+ return false;
+
+ if (sta.st_dev != stb.st_dev || sta.st_ino != stb.st_ino)
+ return false;
+
+ /* The fds refer to the same inode on disk, let's also check
+ * if they have the same fd flags. This is useful to
+ * distinguish the read and write side of a pipe created with
+ * pipe(). */
+ fa = fcntl(a, F_GETFL);
+ if (fa < 0)
+ return -errno;
+
+ fb = fcntl(b, F_GETFL);
+ if (fb < 0)
+ return -errno;
+
+ return fa == fb;
+}
+
int chattr_fd(int fd, unsigned value, unsigned mask) {
unsigned old_attr, new_attr;
struct stat st;
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));
}
*ret = (mode_t) l;
return 0;
}
+
+int mount_move_root(const char *path) {
+ assert(path);
+
+ if (chdir(path) < 0)
+ return -errno;
+
+ if (mount(path, "/", NULL, MS_MOVE, NULL) < 0)
+ return -errno;
+
+ if (chroot(".") < 0)
+ return -errno;
+
+ if (chdir("/") < 0)
+ return -errno;
+
+ return 0;
+}
+
+int reset_uid_gid(void) {
+
+ if (setgroups(0, NULL) < 0)
+ return -errno;
+
+ if (setresgid(0, 0, 0) < 0)
+ return -errno;
+
+ if (setresuid(0, 0, 0) < 0)
+ return -errno;
+
+ return 0;
+}