along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
-#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
+#include <libintl.h>
#include <stdio.h>
#include <syslog.h>
#include <sched.h>
#include <pwd.h>
#include <netinet/ip.h>
#include <linux/kd.h>
-#include <dlfcn.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <glob.h>
#include <sys/auxv.h>
#endif
+#include "config.h"
#include "macro.h"
#include "util.h"
#include "ioprio.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
-#include "label.h"
#include "mkdir.h"
#include "path-util.h"
#include "exit-status.h"
#include "virt.h"
#include "def.h"
#include "sparse-endian.h"
+#include "formats-util.h"
+#include "process-util.h"
+
+/* Put this test here for a lack of better place */
+assert_cc(EAGAIN == EWOULDBLOCK);
int saved_argc = 0;
char **saved_argv = NULL;
return (char*) p;
}
-static size_t cescape_char(char c, char *buf) {
+size_t cescape_char(char c, char *buf) {
char * buf_old = buf;
switch (c) {
return current;
}
-int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
- int r;
- _cleanup_free_ char *line = NULL;
- long unsigned ppid;
- const char *p;
-
- assert(pid >= 0);
- assert(_ppid);
-
- if (pid == 0) {
- *_ppid = getppid();
- return 0;
- }
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- /* Let's skip the pid and comm fields. The latter is enclosed
- * in () but does not escape any () in its value, so let's
- * skip over it manually */
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " "
- "%*c " /* state */
- "%lu ", /* ppid */
- &ppid) != 1)
- return -EIO;
-
- if ((long unsigned) (pid_t) ppid != ppid)
- return -ERANGE;
-
- *_ppid = (pid_t) ppid;
-
- return 0;
-}
-
int fchmod_umask(int fd, mode_t m) {
mode_t u;
int r;
return s;
}
-int get_process_state(pid_t pid) {
- const char *p;
- char state;
- int r;
- _cleanup_free_ char *line = NULL;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "stat");
- r = read_one_line_file(p, &line);
- if (r < 0)
- return r;
-
- p = strrchr(line, ')');
- if (!p)
- return -EIO;
-
- p++;
-
- if (sscanf(p, " %c", &state) != 1)
- return -EIO;
-
- return (unsigned char) state;
-}
-
-int get_process_comm(pid_t pid, char **name) {
- const char *p;
- int r;
-
- assert(name);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "comm");
-
- r = read_one_line_file(p, name);
- if (r == -ENOENT)
- return -ESRCH;
-
- return r;
-}
-
-int get_process_cmdline(pid_t pid, size_t max_length, bool comm_fallback, char **line) {
- _cleanup_fclose_ FILE *f = NULL;
- char *r = NULL, *k;
- const char *p;
- int c;
-
- assert(line);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cmdline");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- if (max_length == 0) {
- size_t len = 0, allocated = 0;
-
- while ((c = getc(f)) != EOF) {
-
- if (!GREEDY_REALLOC(r, allocated, len+2)) {
- free(r);
- return -ENOMEM;
- }
-
- r[len++] = isprint(c) ? c : ' ';
- }
-
- if (len > 0)
- r[len-1] = 0;
-
- } else {
- bool space = false;
- size_t left;
-
- r = new(char, max_length);
- if (!r)
- return -ENOMEM;
-
- k = r;
- left = max_length;
- while ((c = getc(f)) != EOF) {
-
- if (isprint(c)) {
- if (space) {
- if (left <= 4)
- break;
-
- *(k++) = ' ';
- left--;
- space = false;
- }
-
- if (left <= 4)
- break;
-
- *(k++) = (char) c;
- left--;
- } else
- space = true;
- }
-
- if (left <= 4) {
- size_t n = MIN(left-1, 3U);
- memcpy(k, "...", n);
- k[n] = 0;
- } else
- *k = 0;
- }
-
- /* Kernel threads have no argv[] */
- if (isempty(r)) {
- _cleanup_free_ char *t = NULL;
- int h;
-
- free(r);
-
- if (!comm_fallback)
- return -ENOENT;
-
- h = get_process_comm(pid, &t);
- if (h < 0)
- return h;
-
- r = strjoin("[", t, "]", NULL);
- if (!r)
- return -ENOMEM;
- }
-
- *line = r;
- return 0;
-}
-
-int is_kernel_thread(pid_t pid) {
- const char *p;
- size_t count;
- char c;
- bool eof;
- FILE *f;
-
- if (pid == 0)
- return 0;
-
- assert(pid > 0);
-
- p = procfs_file_alloca(pid, "cmdline");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- count = fread(&c, 1, 1, f);
- eof = feof(f);
- fclose(f);
-
- /* Kernel threads have an empty cmdline */
-
- if (count <= 0)
- return eof ? 1 : -errno;
-
- return 0;
-}
-
-int get_process_capeff(pid_t pid, char **capeff) {
- const char *p;
-
- assert(capeff);
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "status");
-
- return get_status_field(p, "\nCapEff:", capeff);
-}
-
-static int get_process_link_contents(const char *proc_file, char **name) {
- int r;
-
- assert(proc_file);
- assert(name);
-
- r = readlink_malloc(proc_file, name);
- if (r < 0)
- return r == -ENOENT ? -ESRCH : r;
-
- return 0;
-}
-
-int get_process_exe(pid_t pid, char **name) {
- const char *p;
- char *d;
- int r;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "exe");
- r = get_process_link_contents(p, name);
- if (r < 0)
- return r;
-
- d = endswith(*name, " (deleted)");
- if (d)
- *d = '\0';
-
- return 0;
-}
-
-static int get_process_id(pid_t pid, const char *field, uid_t *uid) {
- _cleanup_fclose_ FILE *f = NULL;
- char line[LINE_MAX];
- const char *p;
-
- assert(field);
- assert(uid);
-
- if (pid == 0)
- return getuid();
-
- p = procfs_file_alloca(pid, "status");
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- FOREACH_LINE(line, f, return -errno) {
- char *l;
-
- l = strstrip(line);
-
- if (startswith(l, field)) {
- l += strlen(field);
- l += strspn(l, WHITESPACE);
-
- l[strcspn(l, WHITESPACE)] = 0;
-
- return parse_uid(l, uid);
- }
- }
-
- return -EIO;
-}
-
-int get_process_uid(pid_t pid, uid_t *uid) {
- return get_process_id(pid, "Uid:", uid);
-}
-
-int get_process_gid(pid_t pid, gid_t *gid) {
- assert_cc(sizeof(uid_t) == sizeof(gid_t));
- return get_process_id(pid, "Gid:", gid);
-}
-
-int get_process_cwd(pid_t pid, char **cwd) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "cwd");
-
- return get_process_link_contents(p, cwd);
-}
-
-int get_process_root(pid_t pid, char **root) {
- const char *p;
-
- assert(pid >= 0);
-
- p = procfs_file_alloca(pid, "root");
-
- return get_process_link_contents(p, root);
-}
-
-int get_process_environ(pid_t pid, char **env) {
- _cleanup_fclose_ FILE *f = NULL;
- _cleanup_free_ char *outcome = NULL;
- int c;
- const char *p;
- size_t allocated = 0, sz = 0;
-
- assert(pid >= 0);
- assert(env);
-
- p = procfs_file_alloca(pid, "environ");
-
- f = fopen(p, "re");
- if (!f)
- return -errno;
-
- while ((c = fgetc(f)) != EOF) {
- if (!GREEDY_REALLOC(outcome, allocated, sz + 5))
- return -ENOMEM;
-
- if (c == '\0')
- outcome[sz++] = '\n';
- else
- sz += cescape_char(c, outcome + sz);
- }
-
- outcome[sz] = '\0';
- *env = outcome;
- outcome = NULL;
-
- return 0;
-}
-
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
assert(s);
- /* Does C style string escaping. */
+ /* Does C style string escaping. May be reversed with
+ * cunescape(). */
r = new(char, strlen(s)*4 + 1);
if (!r)
return r;
}
-char *cunescape_length_with_prefix(const char *s, size_t length, const char *prefix) {
+static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) {
+ int r = 1;
+
+ assert(p);
+ assert(*p);
+ assert(ret);
+
+ /* Unescapes C style. Returns the unescaped character in ret,
+ * unless we encountered a \u sequence in which case the full
+ * unicode character is returned in ret_unicode, instead. */
+
+ if (length != (size_t) -1 && length < 1)
+ return -EINVAL;
+
+ switch (p[0]) {
+
+ case 'a':
+ *ret = '\a';
+ break;
+ case 'b':
+ *ret = '\b';
+ break;
+ case 'f':
+ *ret = '\f';
+ break;
+ case 'n':
+ *ret = '\n';
+ break;
+ case 'r':
+ *ret = '\r';
+ break;
+ case 't':
+ *ret = '\t';
+ break;
+ case 'v':
+ *ret = '\v';
+ break;
+ case '\\':
+ *ret = '\\';
+ break;
+ case '"':
+ *ret = '"';
+ break;
+ case '\'':
+ *ret = '\'';
+ break;
+
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *ret = ' ';
+ break;
+
+ case 'x': {
+ /* hexadecimal encoding */
+ int a, b;
+
+ if (length != (size_t) -1 && length < 3)
+ return -EINVAL;
+
+ a = unhexchar(p[1]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unhexchar(p[2]);
+ if (b < 0)
+ return -EINVAL;
+
+ /* Don't allow NUL bytes */
+ if (a == 0 && b == 0)
+ return -EINVAL;
+
+ *ret = (char) ((a << 4U) | b);
+ r = 3;
+ break;
+ }
+
+ case 'u': {
+ /* C++11 style 16bit unicode */
+
+ int a[4];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 5)
+ return -EINVAL;
+
+ for (i = 0; i < 4; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 5;
+ break;
+ }
+
+ case 'U': {
+ /* C++11 style 32bit unicode */
+
+ int a[8];
+ unsigned i;
+ uint32_t c;
+
+ if (length != (size_t) -1 && length < 9)
+ return -EINVAL;
+
+ for (i = 0; i < 8; i++) {
+ a[i] = unhexchar(p[1 + i]);
+ if (a[i] < 0)
+ return a[i];
+ }
+
+ c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
+ ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7];
+
+ /* Don't allow 0 chars */
+ if (c == 0)
+ return -EINVAL;
+
+ /* Don't allow invalid code points */
+ if (!unichar_is_valid(c))
+ return -EINVAL;
+
+ if (c < 128)
+ *ret = c;
+ else {
+ if (!ret_unicode)
+ return -EINVAL;
+
+ *ret = 0;
+ *ret_unicode = c;
+ }
+
+ r = 9;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7': {
+ /* octal encoding */
+ int a, b, c;
+ uint32_t m;
+
+ if (length != (size_t) -1 && length < 4)
+ return -EINVAL;
+
+ a = unoctchar(p[0]);
+ if (a < 0)
+ return -EINVAL;
+
+ b = unoctchar(p[1]);
+ if (b < 0)
+ return -EINVAL;
+
+ c = unoctchar(p[2]);
+ if (c < 0)
+ return -EINVAL;
+
+ /* don't allow NUL bytes */
+ if (a == 0 && b == 0 && c == 0)
+ return -EINVAL;
+
+ /* Don't allow bytes above 255 */
+ m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
+ if (m > 255)
+ return -EINVAL;
+
+ *ret = m;
+ r = 3;
+ break;
+ }
+
+ default:
+ return -EINVAL;
+ }
+
+ return r;
+}
+
+int cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
char *r, *t;
const char *f;
size_t pl;
assert(s);
+ assert(ret);
/* Undoes C style string escaping, and optionally prefixes it. */
r = new(char, pl+length+1);
if (!r)
- return NULL;
+ return -ENOMEM;
if (prefix)
memcpy(r, prefix, pl);
for (f = s, t = r + pl; f < s + length; f++) {
- size_t remaining = s + length - f;
+ size_t remaining;
+ uint32_t u;
+ char c;
+ int k;
+
+ remaining = s + length - f;
assert(remaining > 0);
- if (*f != '\\') { /* a literal literal */
+ if (*f != '\\') {
+ /* A literal literal, copy verbatim */
*(t++) = *f;
continue;
}
- if (--remaining == 0) { /* copy trailing backslash verbatim */
- *(t++) = *f;
- break;
- }
-
- f++;
-
- switch (*f) {
-
- case 'a':
- *(t++) = '\a';
- break;
- case 'b':
- *(t++) = '\b';
- break;
- case 'f':
- *(t++) = '\f';
- break;
- case 'n':
- *(t++) = '\n';
- break;
- case 'r':
- *(t++) = '\r';
- break;
- case 't':
- *(t++) = '\t';
- break;
- case 'v':
- *(t++) = '\v';
- break;
- case '\\':
- *(t++) = '\\';
- break;
- case '"':
- *(t++) = '"';
- break;
- case '\'':
- *(t++) = '\'';
- break;
-
- case 's':
- /* This is an extension of the XDG syntax files */
- *(t++) = ' ';
- break;
-
- case 'x': {
- /* hexadecimal encoding */
- int a = -1, b = -1;
-
- if (remaining >= 2) {
- a = unhexchar(f[1]);
- b = unhexchar(f[2]);
- }
-
- if (a < 0 || b < 0 || (a == 0 && b == 0)) {
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = 'x';
- } else {
- *(t++) = (char) ((a << 4) | b);
- f += 2;
+ if (remaining == 1) {
+ if (flags & UNESCAPE_RELAX) {
+ /* A trailing backslash, copy verbatim */
+ *(t++) = *f;
+ continue;
}
- break;
+ free(r);
+ return -EINVAL;
}
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7': {
- /* octal encoding */
- int a = -1, b = -1, c = -1;
-
- if (remaining >= 3) {
- a = unoctchar(f[0]);
- b = unoctchar(f[1]);
- c = unoctchar(f[2]);
- }
-
- if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
+ k = cunescape_one(f + 1, remaining - 1, &c, &u);
+ if (k < 0) {
+ if (flags & UNESCAPE_RELAX) {
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
- *(t++) = f[0];
- } else {
- *(t++) = (char) ((a << 6) | (b << 3) | c);
- f += 2;
+ continue;
}
- break;
+ free(r);
+ return k;
}
- default:
- /* Invalid escape code, let's take it literal then */
- *(t++) = '\\';
- *(t++) = *f;
- break;
- }
+ if (c != 0)
+ /* Non-Unicode? Let's encode this directly */
+ *(t++) = c;
+ else
+ /* Unicode? Then let's encode this in UTF-8 */
+ t += utf8_encode_unichar(t, u);
+
+ f += k;
}
*t = 0;
- return r;
-}
-char *cunescape_length(const char *s, size_t length) {
- return cunescape_length_with_prefix(s, length, NULL);
+ *ret = r;
+ return t - r;
}
-char *cunescape(const char *s) {
- assert(s);
+int cunescape_length(const char *s, size_t length, UnescapeFlags flags, char **ret) {
+ return cunescape_length_with_prefix(s, length, NULL, flags, ret);
+}
- return cunescape_length(s, strlen(s));
+int cunescape(const char *s, UnescapeFlags flags, char **ret) {
+ return cunescape_length(s, strlen(s), flags, ret);
}
char *xescape(const char *s, const char *bad) {
/* Escapes all chars in bad, in addition to \ and all special
* chars, in \xFF style escaping. May be reversed with
- * cunescape. */
+ * cunescape(). */
r = new(char, strlen(s) * 4 + 1);
if (!r)
bool fstype_is_network(const char *fstype) {
static const char table[] =
+ "afs\0"
"cifs\0"
"smbfs\0"
"sshfs\0"
return n;
}
+int loop_read_exact(int fd, void *buf, size_t nbytes, bool do_poll) {
+ ssize_t n;
+
+ n = loop_read(fd, buf, nbytes, do_poll);
+ if (n < 0)
+ return n;
+ if ((size_t) n != nbytes)
+ return -EIO;
+ return 0;
+}
+
int loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) {
const uint8_t *p = buf;
int dev_urandom(void *p, size_t n) {
static int have_syscall = -1;
- int r, fd;
- ssize_t k;
+
+ _cleanup_close_ int fd = -1;
+ int r;
/* Gathers some randomness from the kernel. This call will
* never block, and will always return some data from the
return -errno;
} else
/* too short read? */
- return -EIO;
+ return -ENODATA;
}
fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0)
return errno == ENOENT ? -ENOSYS : -errno;
- k = loop_read(fd, p, n, true);
- safe_close(fd);
-
- if (k < 0)
- return (int) k;
- if ((size_t) k != n)
- return -EIO;
-
- return 0;
+ return loop_read_exact(fd, p, n, true);
}
void initialize_srand(void) {
int getttyname_harder(int fd, char **r) {
int k;
- char *s;
+ char *s = NULL;
k = getttyname_malloc(fd, &s);
if (k < 0)
/* This is an ugly hack */
if (major(devnr) == 136) {
- asprintf(&b, "pts/%u", minor(devnr));
- goto finish;
- }
-
- /* Probably something like the ptys which have no
- * symlink in /dev/char. Let's return something
- * vaguely useful. */
-
- b = strdup(fn + 5);
- goto finish;
- }
-
- if (startswith(s, "/dev/"))
- p = s + 5;
- else if (startswith(s, "../"))
- p = s + 3;
- else
- p = s;
-
- b = strdup(p);
-
-finish:
- if (!b)
- return -ENOMEM;
-
- *r = b;
- if (_devnr)
- *_devnr = devnr;
-
- return 0;
-}
-
-int rm_rf_children_dangerous(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- _cleanup_closedir_ DIR *d = NULL;
- int ret = 0;
-
- assert(fd >= 0);
-
- /* This returns the first error we run into, but nevertheless
- * tries to go on. This closes the passed fd. */
-
- d = fdopendir(fd);
- if (!d) {
- safe_close(fd);
-
- return errno == ENOENT ? 0 : -errno;
- }
-
- for (;;) {
- struct dirent *de;
- bool is_dir, keep_around;
- struct stat st;
- int r;
-
- errno = 0;
- de = readdir(d);
- if (!de) {
- if (errno != 0 && ret == 0)
- ret = -errno;
- return ret;
- }
-
- if (streq(de->d_name, ".") || streq(de->d_name, ".."))
- continue;
-
- if (de->d_type == DT_UNKNOWN ||
- honour_sticky ||
- (de->d_type == DT_DIR && root_dev)) {
- if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- is_dir = S_ISDIR(st.st_mode);
- keep_around =
- honour_sticky &&
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
- } else {
- is_dir = de->d_type == DT_DIR;
- keep_around = false;
- }
-
- if (is_dir) {
- int subdir_fd;
-
- /* if root_dev is set, remove subdirectories only, if device is same as dir */
- if (root_dev && st.st_dev != root_dev->st_dev)
- continue;
-
- subdir_fd = openat(fd, de->d_name,
- O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (subdir_fd < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- continue;
- }
-
- r = rm_rf_children_dangerous(subdir_fd, only_dirs, honour_sticky, root_dev);
- if (r < 0 && ret == 0)
- ret = r;
-
- if (!keep_around)
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
-
- } else if (!only_dirs && !keep_around) {
-
- if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- }
- }
-}
-
-_pure_ static int is_temporary_fs(struct statfs *s) {
- assert(s);
-
- return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
- F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
-}
-
-int is_fd_on_temporary_fs(int fd) {
- struct statfs s;
-
- if (fstatfs(fd, &s) < 0)
- return -errno;
-
- return is_temporary_fs(&s);
-}
-
-int rm_rf_children(int fd, bool only_dirs, bool honour_sticky, struct stat *root_dev) {
- struct statfs s;
-
- assert(fd >= 0);
-
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
- }
-
- /* We refuse to clean disk file systems with this call. This
- * is extra paranoia just to be sure we never ever remove
- * non-state data */
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
-
- return rm_rf_children_dangerous(fd, only_dirs, honour_sticky, root_dev);
-}
-
-static int file_is_priv_sticky(const char *p) {
- struct stat st;
-
- assert(p);
-
- if (lstat(p, &st) < 0)
- return -errno;
-
- return
- (st.st_uid == 0 || st.st_uid == getuid()) &&
- (st.st_mode & S_ISVTX);
-}
-
-static int rm_rf_internal(const char *path, bool only_dirs, bool delete_root, bool honour_sticky, bool dangerous) {
- int fd, r;
- struct statfs s;
-
- assert(path);
-
- /* We refuse to clean the root file system with this
- * call. This is extra paranoia to never cause a really
- * seriously broken system. */
- if (path_equal(path, "/")) {
- log_error("Attempted to remove entire root file system, and we can't allow that.");
- return -EPERM;
- }
-
- fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME);
- if (fd < 0) {
-
- if (errno != ENOTDIR && errno != ELOOP)
- return -errno;
-
- if (!dangerous) {
- if (statfs(path, &s) < 0)
- return -errno;
-
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- return -EPERM;
- }
- }
-
- if (delete_root && !only_dirs)
- if (unlink(path) < 0 && errno != ENOENT)
- return -errno;
-
- return 0;
- }
+ if (asprintf(&b, "pts/%u", minor(devnr)) < 0)
+ return -ENOMEM;
+ } else {
+ /* Probably something like the ptys which have no
+ * symlink in /dev/char. Let's return something
+ * vaguely useful. */
- if (!dangerous) {
- if (fstatfs(fd, &s) < 0) {
- safe_close(fd);
- return -errno;
+ b = strdup(fn + 5);
+ if (!b)
+ return -ENOMEM;
}
+ } else {
+ if (startswith(s, "/dev/"))
+ p = s + 5;
+ else if (startswith(s, "../"))
+ p = s + 3;
+ else
+ p = s;
- if (!is_temporary_fs(&s)) {
- log_error("Attempted to remove disk file system, and we can't allow that.");
- safe_close(fd);
- return -EPERM;
- }
+ b = strdup(p);
+ if (!b)
+ return -ENOMEM;
}
- r = rm_rf_children_dangerous(fd, only_dirs, honour_sticky, NULL);
- if (delete_root) {
+ *r = b;
+ if (_devnr)
+ *_devnr = devnr;
- if (honour_sticky && file_is_priv_sticky(path) > 0)
- return r;
+ return 0;
+}
- if (rmdir(path) < 0 && errno != ENOENT) {
- if (r == 0)
- r = -errno;
- }
- }
+bool is_temporary_fs(const struct statfs *s) {
+ assert(s);
- return r;
+ return F_TYPE_EQUAL(s->f_type, TMPFS_MAGIC) ||
+ F_TYPE_EQUAL(s->f_type, RAMFS_MAGIC);
}
-int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, false);
-}
+int fd_is_temporary_fs(int fd) {
+ struct statfs s;
+
+ if (fstatfs(fd, &s) < 0)
+ return -errno;
-int rm_rf_dangerous(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
- return rm_rf_internal(path, only_dirs, delete_root, honour_sticky, true);
+ return is_temporary_fs(&s);
}
int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
/* If $FOO appears as single word, replace it by the split up variable */
if ((*i)[0] == '$' && (*i)[1] != '{') {
char *e;
- char **w, **m;
+ char **w, **m = NULL;
unsigned q;
e = strv_env_get(env, *i+1);
if (e) {
int r;
- r = strv_split_quoted(&m, e, true);
+ r = strv_split_quoted(&m, e, UNQUOTE_RELAX);
if (r < 0) {
ret[k] = NULL;
strv_free(ret);
return touch_file(path, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0);
}
-char *unquote(const char *s, const char* quotes) {
+static char *unquote(const char *s, const char* quotes) {
size_t l;
assert(s);
/* This is rather stupid, simply removes the heading and
* trailing quotes if there is one. Doesn't care about
- * escaping or anything. We should make this smarter one
- * day... */
+ * escaping or anything.
+ *
+ * DON'T USE THIS FOR NEW CODE ANYMORE!*/
l = strlen(s);
if (l < 2)
return strdup(s);
}
-char *normalize_env_assignment(const char *s) {
- _cleanup_free_ char *value = NULL;
- const char *eq;
- char *p, *name;
-
- eq = strchr(s, '=');
- if (!eq) {
- char *r, *t;
-
- r = strdup(s);
- if (!r)
- return NULL;
-
- t = strstrip(r);
- if (t != r)
- memmove(r, t, strlen(t) + 1);
-
- return r;
- }
-
- name = strndupa(s, eq - s);
- p = strdupa(eq + 1);
-
- value = unquote(strstrip(p), QUOTES);
- if (!value)
- return NULL;
-
- return strjoin(strstrip(name), "=", value, NULL);
-}
-
-int wait_for_terminate(pid_t pid, siginfo_t *status) {
- siginfo_t dummy;
-
- assert(pid >= 1);
-
- if (!status)
- status = &dummy;
-
- for (;;) {
- zero(*status);
-
- if (waitid(P_PID, pid, status, WEXITED) < 0) {
-
- if (errno == EINTR)
- continue;
-
- return -errno;
- }
-
- return 0;
- }
-}
-
-/*
- * Return values:
- * < 0 : wait_for_terminate() failed to get the state of the
- * process, the process was terminated by a signal, or
- * failed for an unknown reason.
- * >=0 : The process terminated normally, and its exit code is
- * returned.
- *
- * That is, success is indicated by a return value of zero, and an
- * error is indicated by a non-zero value.
- *
- * A warning is emitted if the process terminates abnormally,
- * and also if it returns non-zero unless check_exit_code is true.
- */
-int wait_for_terminate_and_warn(const char *name, pid_t pid, bool check_exit_code) {
- int r;
- siginfo_t status;
-
- assert(name);
- assert(pid > 1);
-
- r = wait_for_terminate(pid, &status);
- if (r < 0)
- return log_warning_errno(r, "Failed to wait for %s: %m", name);
-
- if (status.si_code == CLD_EXITED) {
- if (status.si_status != 0)
- log_full(check_exit_code ? LOG_WARNING : LOG_DEBUG,
- "%s failed with error code %i.", name, status.si_status);
- else
- log_debug("%s succeeded.", name);
-
- return status.si_status;
- } 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;
-}
-
noreturn void freeze(void) {
/* Make sure nobody waits for us on a socket anymore */
_cleanup_free_ char *t = NULL, *u = NULL;
size_t enc_len;
- u = unquote(tagvalue, "\"\'");
+ u = unquote(tagvalue, QUOTES);
if (!u)
return NULL;
if (null_or_empty_path(path)) {
log_debug("%s is empty (a mask).", path);
continue;
- } else
- log_debug("%s will be executed.", path);
+ }
pid = fork();
if (pid < 0) {
wait_for_terminate_and_warn(name, executor_pid, true);
}
-int kill_and_sigcont(pid_t pid, int sig) {
- int r;
-
- r = kill(pid, sig) < 0 ? -errno : 0;
-
- if (r >= 0)
- kill(pid, SIGCONT);
-
- return r;
-}
-
bool nulstr_contains(const char*nulstr, const char *needle) {
const char *i;
return 0;
}
-int getenv_for_pid(pid_t pid, const char *field, char **_value) {
- _cleanup_fclose_ FILE *f = NULL;
- char *value = NULL;
- int r;
- bool done = false;
- size_t l;
- const char *path;
-
- assert(pid >= 0);
- assert(field);
- assert(_value);
-
- path = procfs_file_alloca(pid, "environ");
-
- f = fopen(path, "re");
- if (!f)
- return -errno;
-
- l = strlen(field);
- r = 0;
-
- do {
- char line[LINE_MAX];
- unsigned i;
-
- for (i = 0; i < sizeof(line)-1; i++) {
- int c;
-
- c = getc(f);
- if (_unlikely_(c == EOF)) {
- done = true;
- break;
- } else if (c == 0)
- break;
-
- line[i] = c;
- }
- line[i] = 0;
-
- if (memcmp(line, field, l) == 0 && line[l] == '=') {
- value = strdup(line + l + 1);
- if (!value)
- return -ENOMEM;
-
- r = 1;
- break;
- }
-
- } while (!done);
-
- *_value = value;
- return r;
-}
-
bool http_etag_is_valid(const char *etag) {
if (isempty(etag))
return false;
return NULL;
}
+void init_gettext(void) {
+ setlocale(LC_ALL, "");
+ textdomain(GETTEXT_PACKAGE);
+}
+
bool is_locale_utf8(void) {
const char *set;
static int cached_answer = -1;
d = opendir("/sys/class/power_supply");
if (!d)
- return -errno;
+ return errno == ENOENT ? true : -errno;
for (;;) {
struct dirent *de;
_cleanup_free_ char *word = NULL;
char *value = NULL;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
_cleanup_free_ char *word = NULL;
const char *e;
- r = unquote_first_word(&p, &word, true);
+ r = unquote_first_word(&p, &word, UNQUOTE_RELAX);
if (r < 0)
return r;
if (r == 0)
return 0;
}
-bool pid_is_unwaited(pid_t pid) {
- /* Checks whether a PID is still valid at all, including a zombie */
-
- if (pid <= 0)
- return false;
-
- if (kill(pid, 0) >= 0)
- return true;
-
- return errno != ESRCH;
-}
-
-bool pid_is_alive(pid_t pid) {
- int r;
-
- /* Checks whether a PID is still valid and not a zombie */
-
- if (pid <= 0)
- return false;
-
- r = get_process_state(pid);
- if (r == -ENOENT || r == 'Z')
- return false;
-
- return true;
-}
-
int getpeercred(int fd, struct ucred *ucred) {
socklen_t n = sizeof(struct ucred);
struct ucred u;
if (isempty(s)) {
free(s);
- return -ENOTSUP;
+ return -EOPNOTSUPP;
}
*ret = s;
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
if (!path_startswith(p, prefix))
continue;
continue;
}
- p = cunescape(path);
- if (!p)
- return -ENOMEM;
+ r = cunescape(path, UNESCAPE_RELAX, &p);
+ if (r < 0)
+ return r;
/* Let's ignore autofs mounts. If they aren't
* triggered yet, we want to avoid triggering
return !!S_ISDIR(st.st_mode);
}
-int unquote_first_word(const char **p, char **ret, bool relax) {
+int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) {
_cleanup_free_ char *s = NULL;
size_t allocated = 0, sz = 0;
+ int r;
enum {
START,
case VALUE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
- state = VALUE;
+ 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 (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
} else if (c == '\'')
case SINGLE_QUOTE_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ 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_ESCAPE:
if (c == 0) {
- if (relax)
+ if (flags & UNQUOTE_RELAX)
goto finish;
return -EINVAL;
}
- if (!GREEDY_REALLOC(s, allocated, sz+2))
+ if (!GREEDY_REALLOC(s, allocated, sz+7))
return -ENOMEM;
- s[sz++] = c;
+ 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 = DOUBLE_QUOTE;
break;
return 1;
}
-int unquote_many_words(const char **p, ...) {
+int unquote_many_words(const char **p, UnquoteFlags flags, ...) {
va_list ap;
char **l;
int n = 0, i, c, r;
assert(p);
/* Count how many words are expected */
- va_start(ap, p);
+ va_start(ap, flags);
for (;;) {
if (!va_arg(ap, char **))
break;
l = newa0(char*, n);
for (c = 0; c < n; c++) {
- r = unquote_first_word(p, &l[c], false);
+ r = unquote_first_word(p, &l[c], flags);
if (r < 0) {
int j;
/* If we managed to parse all words, return them in the passed
* in parameters */
- va_start(ap, p);
+ va_start(ap, flags);
for (i = 0; i < n; i++) {
char **v;
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
- * distuingish 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, bool b, unsigned mask) {
+int chattr_fd(int fd, unsigned value, unsigned mask) {
unsigned old_attr, new_attr;
assert(fd >= 0);
if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0)
return -errno;
- if (b)
- new_attr = old_attr | mask;
- else
- new_attr = old_attr & ~mask;
-
+ new_attr = (old_attr & ~mask) | (value & mask);
if (new_attr == old_attr)
return 0;
if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0)
return -errno;
- return 0;
+ return 1;
}
-int chattr_path(const char *p, bool b, unsigned mask) {
+int chattr_path(const char *p, unsigned value, unsigned mask) {
_cleanup_close_ int fd = -1;
assert(p);
if (fd < 0)
return -errno;
- return chattr_fd(fd, b, mask);
+ return chattr_fd(fd, value, mask);
}
int read_attr_fd(int fd, unsigned *ret) {
return -1;
}
+
+void cmsg_close_all(struct msghdr *mh) {
+ struct cmsghdr *cmsg;
+
+ assert(mh);
+
+ for (cmsg = CMSG_FIRSTHDR(mh); cmsg; cmsg = CMSG_NXTHDR(mh, cmsg))
+ 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));
+}
+
+int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char *newpath) {
+ struct stat buf;
+ int ret;
+
+ ret = renameat2(olddirfd, oldpath, newdirfd, newpath, RENAME_NOREPLACE);
+ if (ret >= 0)
+ return 0;
+
+ /* Even though renameat2() exists since Linux 3.15, btrfs added
+ * support for it later. If it is not implemented, fallback to another
+ * method. */
+ if (errno != EINVAL)
+ return -errno;
+
+ /* The link()/unlink() fallback does not work on directories. But
+ * renameat() without RENAME_NOREPLACE gives the same semantics on
+ * directories, except when newpath is an *empty* directory. This is
+ * good enough. */
+ ret = fstatat(olddirfd, oldpath, &buf, AT_SYMLINK_NOFOLLOW);
+ if (ret >= 0 && S_ISDIR(buf.st_mode)) {
+ ret = renameat(olddirfd, oldpath, newdirfd, newpath);
+ return ret >= 0 ? 0 : -errno;
+ }
+
+ /* If it is not a directory, use the link()/unlink() fallback. */
+ ret = linkat(olddirfd, oldpath, newdirfd, newpath, 0);
+ if (ret < 0)
+ return -errno;
+
+ ret = unlinkat(olddirfd, oldpath, 0);
+ if (ret < 0) {
+ /* backup errno before the following unlinkat() alters it */
+ ret = errno;
+ (void) unlinkat(newdirfd, newpath, 0);
+ errno = ret;
+ return -errno;
+ }
+
+ return 0;
+}
+
+char *shell_maybe_quote(const char *s) {
+ const char *p;
+ char *r, *t;
+
+ assert(s);
+
+ /* Encloses a string in double quotes if necessary to make it
+ * OK as shell string. */
+
+ for (p = s; *p; p++)
+ if (*p <= ' ' ||
+ *p >= 127 ||
+ strchr(SHELL_NEED_QUOTES, *p))
+ break;
+
+ if (!*p)
+ return strdup(s);
+
+ r = new(char, 1+strlen(s)*2+1+1);
+ if (!r)
+ return NULL;
+
+ t = r;
+ *(t++) = '"';
+ t = mempcpy(t, s, p - s);
+
+ for (; *p; p++) {
+
+ if (strchr(SHELL_NEED_ESCAPE, *p))
+ *(t++) = '\\';
+
+ *(t++) = *p;
+ }
+
+ *(t++)= '"';
+ *t = 0;
+
+ return r;
+}
+
+int parse_mode(const char *s, mode_t *ret) {
+ char *x;
+ long l;
+
+ assert(s);
+ assert(ret);
+
+ errno = 0;
+ l = strtol(s, &x, 8);
+ if (errno != 0)
+ return -errno;
+
+ if (!x || x == s || *x)
+ return -EINVAL;
+ if (l < 0 || l > 07777)
+ return -ERANGE;
+
+ *ret = (mode_t) l;
+ return 0;
+}