#include <sys/time.h>
#include <linux/rtc.h>
#include <glob.h>
+#include <grp.h>
#include "macro.h"
#include "util.h"
static __thread size_t pgsz = 0;
long r;
- if (pgsz)
+ if (_likely_(pgsz))
return pgsz;
assert_se((r = sysconf(_SC_PAGESIZE)) > 0);
return 0;
}
+int parse_uid(const char *s, uid_t* ret_uid) {
+ unsigned long ul = 0;
+ uid_t uid;
+ int r;
+
+ assert(s);
+ assert(ret_uid);
+
+ if ((r = safe_atolu(s, &ul)) < 0)
+ return r;
+
+ uid = (uid_t) ul;
+
+ if ((unsigned long) uid != ul)
+ return -ERANGE;
+
+ *ret_uid = uid;
+ return 0;
+}
+
int safe_atou(const char *s, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
}
}
- if (buf)
- buf[l] = 0;
- else if (!(buf = calloc(1, 1))) {
- r = -errno;
- goto finish;
- }
-
+ buf[l] = 0;
*contents = buf;
buf = NULL;
const char *separator, ...) {
int r = 0;
- char *contents, *p;
+ char *contents = NULL, *p;
assert(fname);
assert(separator);
return 0;
}
+int readlink_and_canonicalize(const char *p, char **r) {
+ char *t, *s;
+ int j;
+
+ assert(p);
+ assert(r);
+
+ j = readlink_and_make_absolute(p, &t);
+ if (j < 0)
+ return j;
+
+ s = canonicalize_file_name(t);
+ if (s) {
+ free(t);
+ *r = s;
+ } else
+ *r = t;
+
+ path_kill_slashes(*r);
+
+ return 0;
+}
+
int parent_of_path(const char *path, char **_r) {
const char *e, *a = NULL, *b = NULL, *p;
char *r;
}
char *path_make_absolute(const char *p, const char *prefix) {
- char *r;
-
assert(p);
/* Makes every item in the list an absolute path by prepending
if (path_is_absolute(p) || !prefix)
return strdup(p);
- if (asprintf(&r, "%s/%s", prefix, p) < 0)
- return NULL;
-
- return r;
+ return join(prefix, "/", p, NULL);
}
char *path_make_absolute_cwd(const char *p) {
}
char *strstrip(char *s) {
- char *e, *l = NULL;
+ char *e;
/* Drops trailing whitespace. Modifies the string in
* place. Returns pointer to first non-space character */
s += strspn(s, WHITESPACE);
- for (e = s; *e; e++)
- if (!strchr(WHITESPACE, *e))
- l = e;
+ for (e = strchr(s, 0); e > s; e --)
+ if (!strchr(WHITESPACE, e[-1]))
+ break;
- if (l)
- *(l+1) = 0;
- else
- *s = 0;
+ *e = 0;
return s;
}
return s;
}
+bool in_charset(const char *s, const char* charset) {
+ const char *i;
+
+ assert(s);
+ assert(charset);
+
+ for (i = s; *i; i++)
+ if (!strchr(charset, *i))
+ return false;
+
+ return true;
+}
+
char *file_in_same_dir(const char *path, const char *filename) {
char *e, *r;
size_t k;
return n;
}
-int path_is_mount_point(const char *t) {
+int path_is_mount_point(const char *t, bool allow_symlink) {
struct stat a, b;
char *parent;
int r;
- if (lstat(t, &a) < 0) {
+ if (allow_symlink)
+ r = stat(t, &a);
+ else
+ r = lstat(t, &a);
+
+ if (r < 0) {
if (errno == ENOENT)
return 0;
return -errno;
}
- if ((r = parent_of_path(t, &parent)) < 0)
+ r = parent_of_path(t, &parent);
+ if (r < 0)
return r;
r = lstat(parent, &b);
return 0;
}
+int parse_bytes(const char *t, off_t *bytes) {
+ static const struct {
+ const char *suffix;
+ off_t factor;
+ } table[] = {
+ { "B", 1 },
+ { "K", 1024ULL },
+ { "M", 1024ULL*1024ULL },
+ { "G", 1024ULL*1024ULL*1024ULL },
+ { "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "", 1 },
+ };
+
+ const char *p;
+ off_t r = 0;
+
+ assert(t);
+ assert(bytes);
+
+ p = t;
+ do {
+ long long l;
+ char *e;
+ unsigned i;
+
+ errno = 0;
+ l = strtoll(p, &e, 10);
+
+ if (errno != 0)
+ return -errno;
+
+ if (l < 0)
+ return -ERANGE;
+
+ if (e == p)
+ return -EINVAL;
+
+ e += strspn(e, WHITESPACE);
+
+ for (i = 0; i < ELEMENTSOF(table); i++)
+ if (startswith(e, table[i].suffix)) {
+ r += (off_t) l * table[i].factor;
+ p = e + strlen(table[i].suffix);
+ break;
+ }
+
+ if (i >= ELEMENTSOF(table))
+ return -EINVAL;
+
+ } while (*p != 0);
+
+ *bytes = r;
+
+ return 0;
+}
+
int make_stdio(int fd) {
int r, s, t;
return 0;
}
-static int rm_rf_children(int fd, bool only_dirs) {
+static int rm_rf_children(int fd, bool only_dirs, bool honour_sticky) {
DIR *d;
int ret = 0;
for (;;) {
struct dirent buf, *de;
- bool is_dir;
+ bool is_dir, keep_around = false;
int r;
if ((r = readdir_r(d, &buf, &de)) != 0) {
continue;
}
+ if (honour_sticky)
+ keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+
is_dir = S_ISDIR(st.st_mode);
- } else
+
+ } else {
+ if (honour_sticky) {
+ struct stat st;
+
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0 && errno != ENOENT)
+ ret = -errno;
+ continue;
+ }
+
+ keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+ }
+
is_dir = de->d_type == DT_DIR;
+ }
if (is_dir) {
int subdir_fd;
continue;
}
- if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) {
+ if ((r = rm_rf_children(subdir_fd, only_dirs, honour_sticky)) < 0) {
if (ret == 0)
ret = r;
}
- if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0 && errno != ENOENT)
- ret = -errno;
- }
- } else if (!only_dirs) {
+ 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)
return ret;
}
-int rm_rf(const char *path, bool only_dirs, bool delete_root) {
+int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky) {
int fd;
int r;
return 0;
}
- r = rm_rf_children(fd, only_dirs);
+ r = rm_rf_children(fd, only_dirs, honour_sticky);
+
+ if (delete_root) {
+
+ if (honour_sticky && file_is_sticky(path) > 0)
+ return r;
- if (delete_root)
- if (rmdir(path) < 0) {
+ if (rmdir(path) < 0 && errno != ENOENT) {
if (r == 0)
r = -errno;
}
+ }
return r;
}
static __thread int parsed_columns = 0;
const char *e;
- if (parsed_columns > 0)
+ if (_likely_(parsed_columns > 0))
return parsed_columns;
if ((e = getenv("COLUMNS")))
return false;
}
+int null_or_empty_path(const char *fn) {
+ struct stat st;
+
+ assert(fn);
+
+ if (stat(fn, &st) < 0)
+ return -errno;
+
+ return null_or_empty(&st);
+}
+
DIR *xopendirat(int fd, const char *name, int flags) {
int nfd;
DIR *d;
if (hypervisor) {
if (id)
- *id = "other";
+ *id = "other-vm";
return 1;
}
return 1;
}
- if ((f = fopen("/proc/self/cgroup", "re"))) {
+ f = fopen("/proc/1/environ", "re");
+ if (f) {
+ bool done = false;
+
+ 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 (streq(line, "container=lxc")) {
+ fclose(f);
+
+ if (id)
+ *id = "lxc";
+ return 1;
+
+ } else if (streq(line, "container=systemd-nspawn")) {
+ fclose(f);
+
+ if (id)
+ *id = "systemd-nspawn";
+ return 1;
+
+ } else if (startswith(line, "container=")) {
+ fclose(f);
+
+ if (id)
+ *id = "other-container";
+ return 1;
+ }
+
+ } while (!done);
+
+ fclose(f);
+ }
+
+ f = fopen("/proc/self/cgroup", "re");
+ if (f) {
for (;;) {
char line[LINE_MAX], *p;
if (!fgets(line, sizeof(line), f))
break;
- if (!(p = strchr(strstrip(line), ':')))
+ p = strchr(strstrip(line), ':');
+ if (!p)
continue;
if (strncmp(p, ":ns:", 4))
const char *_id;
int r;
- if (cached_id) {
+ if (_likely_(cached_id)) {
if (cached_id == (const char*) -1)
return 0;
static int files_add(Hashmap *h, const char *path, const char *suffix) {
DIR *dir;
- struct dirent *de;
+ struct dirent buffer, *de;
int r = 0;
dir = opendir(path);
return -errno;
}
- for (de = readdir(dir); de; de = readdir(dir)) {
+ for (;;) {
+ int k;
char *p, *f;
- const char *base;
+
+ k = readdir_r(dir, &buffer, &de);
+ if (k != 0) {
+ r = -k;
+ goto finish;
+ }
+
+ if (!de)
+ break;
if (!file_is_conf(de, suffix))
continue;
free(p);
log_debug("found: %s\n", f);
- base = f + strlen(path) + 1;
- if (hashmap_put(h, base, f) <= 0)
+ if (hashmap_put(h, file_name_from_path(f), f) <= 0)
free(f);
}
int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
struct passwd *p;
- unsigned long lu;
+ uid_t u;
assert(username);
assert(*username);
- assert(uid);
- assert(gid);
- assert(home);
/* We enforce some special rules for uid=0: in order to avoid
* NSS lookups for root we hardcode its data. */
if (streq(*username, "root") || streq(*username, "0")) {
*username = "root";
- *uid = 0;
- *gid = 0;
- *home = "/root";
+
+ if (uid)
+ *uid = 0;
+
+ if (gid)
+ *gid = 0;
+
+ if (home)
+ *home = "/root";
return 0;
}
- if (safe_atolu(*username, &lu) >= 0) {
+ if (parse_uid(*username, &u) >= 0) {
errno = 0;
- p = getpwuid((uid_t) lu);
+ p = getpwuid(u);
/* If there are multiple users with the same id, make
* sure to leave $USER to the configured value instead
if (!p)
return errno != 0 ? -errno : -ESRCH;
- *uid = p->pw_uid;
- *gid = p->pw_gid;
- *home = p->pw_dir;
+ if (uid)
+ *uid = p->pw_uid;
+
+ if (gid)
+ *gid = p->pw_gid;
+
+ if (home)
+ *home = p->pw_dir;
+
+ return 0;
+}
+
+int get_group_creds(const char **groupname, gid_t *gid) {
+ struct group *g;
+ gid_t id;
+
+ assert(groupname);
+
+ /* We enforce some special rules for gid=0: in order to avoid
+ * NSS lookups for root we hardcode its data. */
+
+ if (streq(*groupname, "root") || streq(*groupname, "0")) {
+ *groupname = "root";
+
+ if (gid)
+ *gid = 0;
+
+ return 0;
+ }
+
+ if (parse_gid(*groupname, &id) >= 0) {
+ errno = 0;
+ g = getgrgid(id);
+
+ if (g)
+ *groupname = g->gr_name;
+ } else {
+ errno = 0;
+ g = getgrnam(*groupname);
+ }
+
+ if (!g)
+ return errno != 0 ? -errno : -ESRCH;
+
+ if (gid)
+ *gid = g->gr_gid;
+
return 0;
}
return r;
}
+int dirent_ensure_type(DIR *d, struct dirent *de) {
+ struct stat st;
+
+ assert(d);
+ assert(de);
+
+ if (de->d_type != DT_UNKNOWN)
+ return 0;
+
+ if (fstatat(dirfd(d), de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0)
+ return -errno;
+
+ de->d_type =
+ S_ISREG(st.st_mode) ? DT_REG :
+ S_ISDIR(st.st_mode) ? DT_DIR :
+ S_ISLNK(st.st_mode) ? DT_LNK :
+ S_ISFIFO(st.st_mode) ? DT_FIFO :
+ S_ISSOCK(st.st_mode) ? DT_SOCK :
+ S_ISCHR(st.st_mode) ? DT_CHR :
+ S_ISBLK(st.st_mode) ? DT_BLK :
+ DT_UNKNOWN;
+
+ return 0;
+}
+
+int in_search_path(const char *path, char **search) {
+ char **i, *parent;
+ int r;
+
+ r = parent_of_path(path, &parent);
+ if (r < 0)
+ return r;
+
+ r = 0;
+
+ STRV_FOREACH(i, search) {
+ if (path_equal(parent, *i)) {
+ r = 1;
+ break;
+ }
+ }
+
+ free(parent);
+
+ return r;
+}
+
+int get_files_in_directory(const char *path, char ***list) {
+ DIR *d;
+ int r = 0;
+ unsigned n = 0;
+ char **l = NULL;
+
+ assert(path);
+
+ /* Returns all files in a directory in *list, and the number
+ * of files as return value. If list is NULL returns only the
+ * number */
+
+ d = opendir(path);
+ if (!d)
+ return -errno;
+
+ for (;;) {
+ struct dirent buffer, *de;
+ int k;
+
+ k = readdir_r(d, &buffer, &de);
+ if (k != 0) {
+ r = -k;
+ goto finish;
+ }
+
+ if (!de)
+ break;
+
+ dirent_ensure_type(d, de);
+
+ if (!dirent_is_file(de))
+ continue;
+
+ if (list) {
+ if ((unsigned) r >= n) {
+ char **t;
+
+ n = MAX(16, 2*r);
+ t = realloc(l, sizeof(char*) * n);
+ if (!t) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ l = t;
+ }
+
+ assert((unsigned) r < n);
+
+ l[r] = strdup(de->d_name);
+ if (!l[r]) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ l[++r] = NULL;
+ } else
+ r++;
+ }
+
+finish:
+ if (d)
+ closedir(d);
+
+ if (r >= 0) {
+ if (list)
+ *list = l;
+ } else
+ strv_free(l);
+
+ return r;
+}
+
+char *join(const char *x, ...) {
+ va_list ap;
+ size_t l;
+ char *r, *p;
+
+ va_start(ap, x);
+
+ if (x) {
+ l = strlen(x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ l += strlen(t);
+ }
+ } else
+ l = 0;
+
+ va_end(ap);
+
+ r = new(char, l+1);
+ if (!r)
+ return NULL;
+
+ if (x) {
+ p = stpcpy(r, x);
+
+ va_start(ap, x);
+
+ for (;;) {
+ const char *t;
+
+ t = va_arg(ap, const char *);
+ if (!t)
+ break;
+
+ p = stpcpy(p, t);
+ }
+
+ va_end(ap);
+ } else
+ r[0] = 0;
+
+ return r;
+}
+
+bool is_main_thread(void) {
+ static __thread int cached = 0;
+
+ if (_unlikely_(cached == 0))
+ cached = getpid() == gettid() ? 1 : -1;
+
+ return cached > 0;
+}
+
+int block_get_whole_disk(dev_t d, dev_t *ret) {
+ char *p, *s;
+ int r;
+ unsigned n, m;
+
+ assert(ret);
+
+ /* If it has a queue this is good enough for us */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = d;
+ return 0;
+ }
+
+ /* If it is a partition find the originating device */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/partition", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r < 0)
+ return -ENOENT;
+
+ /* Get parent dev_t */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/../dev", major(d), minor(d)) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+
+ if (r < 0)
+ return r;
+
+ r = sscanf(s, "%u:%u", &m, &n);
+ free(s);
+
+ if (r != 2)
+ return -EINVAL;
+
+ /* Only return this if it is really good enough for us. */
+ if (asprintf(&p, "/sys/dev/block/%u:%u/queue", m, n) < 0)
+ return -ENOMEM;
+
+ r = access(p, F_OK);
+ free(p);
+
+ if (r >= 0) {
+ *ret = makedev(m, n);
+ return 0;
+ }
+
+ return -ENOENT;
+}
+
+int file_is_sticky(const char *p) {
+ struct stat st;
+
+ assert(p);
+
+ if (lstat(p, &st) < 0)
+ return -errno;
+
+ return
+ st.st_uid == 0 &&
+ (st.st_mode & S_ISVTX);
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
-static const char *const signal_table[] = {
+static const char *const __signal_table[] = {
[SIGHUP] = "HUP",
[SIGINT] = "INT",
[SIGQUIT] = "QUIT",
[SIGSYS] = "SYS"
};
-DEFINE_STRING_TABLE_LOOKUP(signal, int);
+DEFINE_PRIVATE_STRING_TABLE_LOOKUP(__signal, int);
+
+const char *signal_to_string(int signo) {
+ static __thread char buf[12];
+ const char *name;
+
+ name = __signal_to_string(signo);
+ if (name)
+ return name;
+
+ if (signo >= SIGRTMIN && signo <= SIGRTMAX)
+ snprintf(buf, sizeof(buf) - 1, "RTMIN+%d", signo - SIGRTMIN);
+ else
+ snprintf(buf, sizeof(buf) - 1, "%d", signo);
+ char_array_0(buf);
+ 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 -1;
+}
+
+bool kexec_loaded(void) {
+ bool loaded = false;
+ char *s;
+
+ if (read_one_line_file("/sys/kernel/kexec_loaded", &s) >= 0) {
+ if (s[0] == '1')
+ loaded = true;
+ free(s);
+ }
+ return loaded;
+}