#include <sys/capability.h>
#include <sys/time.h>
#include <linux/rtc.h>
+#include <glob.h>
+#include <grp.h>
#include "macro.h"
#include "util.h"
#include "exit-status.h"
#include "hashmap.h"
+int saved_argc = 0;
+char **saved_argv = NULL;
+
size_t page_size(void) {
static __thread size_t pgsz = 0;
long r;
return ts;
}
+dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
+ int64_t delta;
+ assert(ts);
+
+ ts->realtime = u;
+
+ if (u == 0)
+ ts->monotonic = 0;
+ else {
+ delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
+
+ ts->monotonic = now(CLOCK_MONOTONIC);
+
+ if ((int64_t) ts->monotonic > delta)
+ ts->monotonic -= delta;
+ else
+ ts->monotonic = 0;
+ }
+
+ return ts;
+}
+
usec_t timespec_load(const struct timespec *ts) {
assert(ts);
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;
assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
char_array_0(fn);
- if (!(f = fopen(fn, "r")))
+ if (!(f = fopen(fn, "re")))
return -errno;
if (!(fgets(line, sizeof(line), f))) {
assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
char_array_0(fn);
- if (!(f = fopen(fn, "r")))
+ if (!(f = fopen(fn, "re")))
return -errno;
if (!(fgets(line, sizeof(line), f))) {
if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
return -ENOMEM;
- f = fopen(p, "r");
+ f = fopen(p, "re");
free(p);
if (!f)
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;
int r = 0, fd;
struct sigaction sa_old, sa_new;
- if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY)) < 0)
+ if ((fd = open("/dev/tty", O_RDWR|O_NOCTTY|O_NDELAY|O_CLOEXEC)) < 0)
return -errno;
/* Temporarily ignore SIGHUP, so that we don't get SIGHUP'ed
if (r < 0 || s < 0 || t < 0)
return -errno;
+ fd_cloexec(STDIN_FILENO, false);
+ fd_cloexec(STDOUT_FILENO, false);
+ fd_cloexec(STDERR_FILENO, false);
+
return 0;
}
if (program_invocation_name)
strncpy(program_invocation_name, name, strlen(program_invocation_name));
+
+ if (saved_argc > 0) {
+ int i;
+
+ if (saved_argv[0])
+ strncpy(saved_argv[0], name, strlen(saved_argv[0]));
+
+ for (i = 1; i < saved_argc; i++) {
+ if (!saved_argv[i])
+ break;
+
+ memset(saved_argv[i], 0, strlen(saved_argv[i]));
+ }
+ }
}
void sigset_add_many(sigset_t *ss, ...) {
if (streq(s, "tty")) {
free(s);
- return get_ctty(r, NULL);
+ return get_ctty(0, NULL, r);
}
*r = s;
return 0;
}
-int get_ctty_devnr(dev_t *d) {
+int get_ctty_devnr(pid_t pid, dev_t *d) {
int k;
- char line[LINE_MAX], *p;
+ char line[LINE_MAX], *p, *fn;
unsigned long ttynr;
FILE *f;
- if (!(f = fopen("/proc/self/stat", "r")))
+ if (asprintf(&fn, "/proc/%lu/stat", (unsigned long) (pid <= 0 ? getpid() : pid)) < 0)
+ return -ENOMEM;
+
+ f = fopen(fn, "re");
+ free(fn);
+ if (!f)
return -errno;
- if (!(fgets(line, sizeof(line), f))) {
+ if (!fgets(line, sizeof(line), f)) {
k = -errno;
fclose(f);
return k;
fclose(f);
- if (!(p = strrchr(line, ')')))
+ p = strrchr(line, ')');
+ if (!p)
return -EIO;
p++;
return 0;
}
-int get_ctty(char **r, dev_t *_devnr) {
+int get_ctty(pid_t pid, dev_t *_devnr, char **r) {
int k;
char fn[PATH_MAX], *s, *b, *p;
dev_t devnr;
assert(r);
- if ((k = get_ctty_devnr(&devnr)) < 0)
+ k = get_ctty_devnr(pid, &devnr);
+ if (k < 0)
return k;
snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr));
}
int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ siginfo_t dummy;
+
assert(pid >= 1);
- assert(status);
+
+ if (!status)
+ status = &dummy;
for (;;) {
zero(*status);
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 (startswith(tty, "/dev/"))
tty += 5;
- return startswith(tty, "tty") &&
- tty[3] >= '0' && tty[3] <= '9';
+ return vtnr_from_tty(tty) >= 0;
+}
+
+int vtnr_from_tty(const char *tty) {
+ int i, r;
+
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (!startswith(tty, "tty") )
+ return -EINVAL;
+
+ if (tty[3] < '0' || tty[3] > '9')
+ return -EINVAL;
+
+ r = safe_atoi(tty+3, &i);
+ if (r < 0)
+ return r;
+
+ if (i < 0 || i > 63)
+ return -EINVAL;
+
+ return i;
}
const char *default_term_for_tty(const char *tty) {
return 1;
}
- if ((f = fopen("/proc/self/cgroup", "r"))) {
+ if ((f = fopen("/proc/self/cgroup", "re"))) {
for (;;) {
char line[LINE_MAX], *p;
if (fd < 0)
return fd;
- loop_write(fd, "\033[H\033[2J", 7, false); /* clear screen */
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[2J", /* clear screen */
+ 10, false);
close_nointr_nofail(fd);
return 0;
if (fd < 0)
return fd;
- /* Requires Linux 2.6.40 */
- loop_write(fd, "\033[H\033[3J", 7, false); /* clear screen including scrollback */
+ loop_write(fd,
+ "\033[r" /* clear scrolling region */
+ "\033[H" /* move home */
+ "\033[3J", /* clear screen including scrollback, requires Linux 2.6.40 */
+ 10, false);
close_nointr_nofail(fd);
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);
}
return local;
}
-int hwclock_apply_localtime_delta(void) {
+int hwclock_apply_localtime_delta(int *min) {
const struct timeval *tv_null = NULL;
struct timespec ts;
struct tm *tm;
*/
if (settimeofday(tv_null, &tz) < 0)
return -errno;
-
- return minuteswest;
+ if (min)
+ *min = minuteswest;
+ return 0;
}
int hwclock_reset_localtime_delta(void) {
return r;
}
+int audit_session_from_pid(pid_t pid, uint32_t *id) {
+ char *p, *s;
+ uint32_t u;
+ int r;
+
+ assert(pid >= 1);
+ assert(id);
+
+ if (have_effective_cap(CAP_AUDIT_CONTROL) <= 0)
+ return -ENOENT;
+
+ if (asprintf(&p, "/proc/%lu/sessionid", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ r = read_one_line_file(p, &s);
+ free(p);
+ if (r < 0)
+ return r;
+
+ r = safe_atou32(s, &u);
+ free(s);
+
+ if (r < 0)
+ return r;
+
+ if (u == (uint32_t) -1 || u <= 0)
+ return -ENOENT;
+
+ *id = u;
+ return 0;
+}
+
+bool display_is_local(const char *display) {
+ assert(display);
+
+ return
+ display[0] == ':' &&
+ display[1] >= '0' &&
+ display[1] <= '9';
+}
+
+int socket_from_display(const char *display, char **path) {
+ size_t k;
+ char *f, *c;
+
+ assert(display);
+ assert(path);
+
+ if (!display_is_local(display))
+ return -EINVAL;
+
+ k = strspn(display+1, "0123456789");
+
+ f = new(char, sizeof("/tmp/.X11-unix/X") + k);
+ if (!f)
+ return -ENOMEM;
+
+ c = stpcpy(f, "/tmp/.X11-unix/X");
+ memcpy(c, display+1, k);
+ c[k] = 0;
+
+ *path = f;
+
+ return 0;
+}
+
+int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const char **home) {
+ struct passwd *p;
+ uid_t u;
+
+ assert(username);
+ assert(*username);
+
+ /* 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";
+
+ if (uid)
+ *uid = 0;
+
+ if (gid)
+ *gid = 0;
+
+ if (home)
+ *home = "/root";
+ return 0;
+ }
+
+ if (parse_uid(*username, &u) >= 0) {
+ errno = 0;
+ p = getpwuid(u);
+
+ /* If there are multiple users with the same id, make
+ * sure to leave $USER to the configured value instead
+ * of the first occurrence in the database. However if
+ * the uid was configured by a numeric uid, then let's
+ * pick the real username from /etc/passwd. */
+ if (p)
+ *username = p->pw_name;
+ } else {
+ errno = 0;
+ p = getpwnam(*username);
+ }
+
+ if (!p)
+ return errno != 0 ? -errno : -ESRCH;
+
+ 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;
+}
+
+int glob_exists(const char *path) {
+ glob_t g;
+ int r, k;
+
+ assert(path);
+
+ zero(g);
+ errno = 0;
+ k = glob(path, GLOB_NOSORT|GLOB_BRACE, NULL, &g);
+
+ if (k == GLOB_NOMATCH)
+ r = 0;
+ else if (k == GLOB_NOSPACE)
+ r = -ENOMEM;
+ else if (k == 0)
+ r = !strv_isempty(g.gl_pathv);
+ else
+ r = errno ? -errno : -EIO;
+
+ globfree(&g);
+
+ 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);
+ assert(list);
+
+ d = opendir(path);
+ 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 ((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;
+ }
+
+finish:
+ if (d)
+ closedir(d);
+
+ if (r >= 0)
+ *list = l;
+ else
+ strv_free(l);
+
+ return r;
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",