chiark / gitweb /
util: user parse_uid() wherever applicable
[elogind.git] / src / util.c
index 2047ebd7bd841bd8b045c86258327dab81f4fcf4..3a82ef7600f53453416eb487b076c4415c1172a4 100644 (file)
@@ -53,6 +53,7 @@
 #include <sys/capability.h>
 #include <sys/time.h>
 #include <linux/rtc.h>
+#include <glob.h>
 
 #include "macro.h"
 #include "util.h"
@@ -64,6 +65,9 @@
 #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;
@@ -108,6 +112,28 @@ dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
         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);
 
@@ -291,6 +317,26 @@ int parse_pid(const char *s, pid_t* ret_pid) {
         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;
@@ -465,7 +511,7 @@ int get_parent_of_pid(pid_t pid, pid_t *_ppid) {
         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))) {
@@ -510,7 +556,7 @@ int get_starttime_of_pid(pid_t pid, unsigned long long *st) {
         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))) {
@@ -985,7 +1031,7 @@ int get_process_cmdline(pid_t pid, size_t max_length, char **line) {
         if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
                 return -ENOMEM;
 
-        f = fopen(p, "r");
+        f = fopen(p, "re");
         free(p);
 
         if (!f)
@@ -1131,6 +1177,29 @@ int readlink_and_make_absolute(const char *p, char **r) {
         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;
@@ -2631,7 +2700,7 @@ int release_terminal(void) {
         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
@@ -2922,6 +2991,10 @@ int make_stdio(int fd) {
         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;
 }
 
@@ -3004,6 +3077,20 @@ void rename_process(const char name[8]) {
 
         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, ...) {
@@ -3092,23 +3179,28 @@ int getttyname_harder(int fd, char **r) {
 
         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;
@@ -3116,7 +3208,8 @@ int get_ctty_devnr(dev_t *d) {
 
         fclose(f);
 
-        if (!(p = strrchr(line, ')')))
+        p = strrchr(line, ')');
+        if (!p)
                 return -EIO;
 
         p++;
@@ -3134,14 +3227,15 @@ int get_ctty_devnr(dev_t *d) {
         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));
@@ -3815,8 +3909,12 @@ char *normalize_env_assignment(const char *s) {
 }
 
 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);
@@ -3889,6 +3987,17 @@ bool null_or_empty(struct stat *st) {
         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;
@@ -4018,8 +4127,31 @@ bool tty_is_vc(const char *tty) {
         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) {
@@ -4203,7 +4335,7 @@ int detect_container(const char **id) {
                 return 1;
         }
 
-        if ((f = fopen("/proc/self/cgroup", "r"))) {
+        if ((f = fopen("/proc/self/cgroup", "re"))) {
 
                 for (;;) {
                         char line[LINE_MAX], *p;
@@ -4622,7 +4754,11 @@ int vt_disallocate(const char *name) {
                 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;
@@ -4658,8 +4794,11 @@ int vt_disallocate(const char *name) {
         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;
@@ -4682,7 +4821,7 @@ static int file_is_conf(const struct dirent *d, const char *suffix) {
 
 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);
@@ -4692,9 +4831,18 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
                 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;
@@ -4713,8 +4861,7 @@ static int files_add(Hashmap *h, const char *path, const char *suffix) {
                 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);
         }
 
@@ -4820,7 +4967,7 @@ int hwclock_is_localtime(void) {
         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;
@@ -4841,8 +4988,9 @@ int hwclock_apply_localtime_delta(void) {
          */
         if (settimeofday(tv_null, &tz) < 0)
                 return -errno;
-
-        return minuteswest;
+        if (min)
+                *min = minuteswest;
+        return 0;
 }
 
 int hwclock_reset_localtime_delta(void) {
@@ -5046,6 +5194,253 @@ int symlink_or_copy_atomic(const char *from, const char *to) {
         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);
+        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";
+                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;
+
+        *uid = p->pw_uid;
+        *gid = p->pw_gid;
+        *home = p->pw_dir;
+        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",