chiark / gitweb /
systemctl: warn users via wall that the system goes down
[elogind.git] / src / util.c
index f7d538aaacf87496367661ba3822bc2f44cb6070..0988675f09be39f291cd565b446ffeb0dba4dd1f 100644 (file)
@@ -43,6 +43,9 @@
 #include <sys/poll.h>
 #include <libgen.h>
 #include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
 
 #include "macro.h"
 #include "util.h"
@@ -72,6 +75,15 @@ usec_t now(clockid_t clock_id) {
         return timespec_load(&ts);
 }
 
+timestamp* timestamp_get(timestamp *ts) {
+        assert(ts);
+
+        ts->realtime = now(CLOCK_REALTIME);
+        ts->monotonic = now(CLOCK_MONOTONIC);
+
+        return ts;
+}
+
 usec_t timespec_load(const struct timespec *ts) {
         assert(ts);
 
@@ -213,6 +225,13 @@ void close_nointr_nofail(int fd) {
         errno = saved_errno;
 }
 
+void close_many(const int fds[], unsigned n_fd) {
+        unsigned i;
+
+        for (i = 0; i < n_fd; i++)
+                close_nointr_nofail(fds[i]);
+}
+
 int parse_boolean(const char *v) {
         assert(v);
 
@@ -224,6 +243,29 @@ int parse_boolean(const char *v) {
         return -EINVAL;
 }
 
+int parse_pid(const char *s, pid_t* ret_pid) {
+        unsigned long ul;
+        pid_t pid;
+        int r;
+
+        assert(s);
+        assert(ret_pid);
+
+        if ((r = safe_atolu(s, &ul)) < 0)
+                return r;
+
+        pid = (pid_t) ul;
+
+        if ((unsigned long) pid != ul)
+                return -ERANGE;
+
+        if (pid <= 0)
+                return -ERANGE;
+
+        *ret_pid = pid;
+        return 0;
+}
+
 int safe_atou(const char *s, unsigned *ret_u) {
         char *x = NULL;
         unsigned long l;
@@ -573,6 +615,26 @@ int readlink_malloc(const char *p, char **r) {
         }
 }
 
+int readlink_and_make_absolute(const char *p, char **r) {
+        char *target, *k;
+        int j;
+
+        assert(p);
+        assert(r);
+
+        if ((j = readlink_malloc(p, &target)) < 0)
+                return j;
+
+        k = file_in_same_dir(p, target);
+        free(target);
+
+        if (!k)
+                return -ENOMEM;
+
+        *r = k;
+        return 0;
+}
+
 char *file_name_from_path(const char *p) {
         char *r;
 
@@ -652,6 +714,51 @@ char **strv_path_make_absolute_cwd(char **l) {
         return l;
 }
 
+char **strv_path_canonicalize(char **l) {
+        char **s;
+        unsigned k = 0;
+        bool enomem = false;
+
+        if (strv_isempty(l))
+                return l;
+
+        /* Goes through every item in the string list and canonicalize
+         * the path. This works in place and won't rollback any
+         * changes on failure. */
+
+        STRV_FOREACH(s, l) {
+                char *t, *u;
+
+                t = path_make_absolute_cwd(*s);
+                free(*s);
+
+                if (!t) {
+                        enomem = true;
+                        continue;
+                }
+
+                errno = 0;
+                u = canonicalize_file_name(t);
+                free(t);
+
+                if (!u) {
+                        if (errno == ENOMEM || !errno)
+                                enomem = true;
+
+                        continue;
+                }
+
+                l[k++] = u;
+        }
+
+        l[k] = NULL;
+
+        if (enomem)
+                return NULL;
+
+        return l;
+}
+
 int reset_all_signal_handlers(void) {
         int sig;
 
@@ -1353,6 +1460,55 @@ char *format_timestamp(char *buf, size_t l, usec_t t) {
         return buf;
 }
 
+char *format_timespan(char *buf, size_t l, usec_t t) {
+        static const struct {
+                const char *suffix;
+                usec_t usec;
+        } table[] = {
+                { "w", USEC_PER_WEEK },
+                { "d", USEC_PER_DAY },
+                { "h", USEC_PER_HOUR },
+                { "min", USEC_PER_MINUTE },
+                { "s", USEC_PER_SEC },
+                { "ms", USEC_PER_MSEC },
+                { "us", 1 },
+        };
+
+        unsigned i;
+        char *p = buf;
+
+        assert(buf);
+        assert(l > 0);
+
+        if (t == (usec_t) -1)
+                return NULL;
+
+        /* The result of this function can be parsed with parse_usec */
+
+        for (i = 0; i < ELEMENTSOF(table); i++) {
+                int k;
+                size_t n;
+
+                if (t < table[i].usec)
+                        continue;
+
+                if (l <= 1)
+                        break;
+
+                k = snprintf(p, l, "%s%llu%s", p > buf ? " " : "", (unsigned long long) (t / table[i].usec), table[i].suffix);
+                n = MIN((size_t) k, l);
+
+                l -= n;
+                p += n;
+
+                t %= table[i].usec;
+        }
+
+        *p = 0;
+
+        return buf;
+}
+
 bool fstype_is_network(const char *fstype) {
         static const char * const table[] = {
                 "cifs",
@@ -1598,7 +1754,7 @@ int flush_fd(int fd) {
         }
 }
 
-int acquire_terminal(const char *name, bool fail, bool force) {
+int acquire_terminal(const char *name, bool fail, bool force, bool ignore_tiocstty_eperm) {
         int fd = -1, notify = -1, r, wd = -1;
 
         assert(name);
@@ -1640,8 +1796,15 @@ int acquire_terminal(const char *name, bool fail, bool force) {
                         return -errno;
 
                 /* First, try to get the tty */
-                if ((r = ioctl(fd, TIOCSCTTY, force)) < 0 &&
-                    (force || fail || errno != EPERM)) {
+                r = ioctl(fd, TIOCSCTTY, force);
+
+                /* Sometimes it makes sense to ignore TIOCSCTTY
+                 * returning EPERM, i.e. when very likely we already
+                 * are have this controlling terminal. */
+                if (r < 0 && errno == EPERM && ignore_tiocstty_eperm)
+                        r = 0;
+
+                if (r < 0 && (force || fail || errno != EPERM)) {
                         r = -errno;
                         goto fail;
                 }
@@ -1728,14 +1891,59 @@ int release_terminal(void) {
         return r;
 }
 
-int ignore_signal(int sig) {
+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;
+        va_list ap;
+        int r = 0;
 
         zero(sa);
         sa.sa_handler = SIG_IGN;
         sa.sa_flags = SA_RESTART;
 
-        return sigaction(sig, &sa, NULL);
+        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;
+        va_list ap;
+        int r = 0;
+
+        zero(sa);
+        sa.sa_handler = SIG_DFL;
+        sa.sa_flags = SA_RESTART;
+
+        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 close_pipe(int p[]) {
@@ -1937,6 +2145,142 @@ bool is_device_path(const char *path) {
                 path_startswith(path, "/sys/");
 }
 
+int dir_is_empty(const char *path) {
+        DIR *d;
+        int r;
+        struct dirent buf, *de;
+
+        if (!(d = opendir(path)))
+                return -errno;
+
+        for (;;) {
+                if ((r = readdir_r(d, &buf, &de)) > 0) {
+                        r = -r;
+                        break;
+                }
+
+                if (!de) {
+                        r = 1;
+                        break;
+                }
+
+                if (!ignore_file(de->d_name)) {
+                        r = 0;
+                        break;
+                }
+        }
+
+        closedir(d);
+        return r;
+}
+
+unsigned long long random_ull(void) {
+        int fd;
+        uint64_t ull;
+        ssize_t r;
+
+        if ((fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC|O_NOCTTY)) < 0)
+                goto fallback;
+
+        r = loop_read(fd, &ull, sizeof(ull));
+        close_nointr_nofail(fd);
+
+        if (r != sizeof(ull))
+                goto fallback;
+
+        return ull;
+
+fallback:
+        return random() * RAND_MAX + random();
+}
+
+void rename_process(const char name[8]) {
+        assert(name);
+
+        prctl(PR_SET_NAME, name);
+
+        /* This is a like a poor man's setproctitle(). The string
+         * passed should fit in 7 chars (i.e. the length of
+         * "systemd") */
+
+        if (program_invocation_name)
+                strncpy(program_invocation_name, name, strlen(program_invocation_name));
+}
+
+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);
+}
+
+char* gethostname_malloc(void) {
+        struct utsname u;
+
+        assert_se(uname(&u) >= 0);
+
+        if (u.nodename[0])
+                return strdup(u.nodename);
+
+        return strdup(u.sysname);
+}
+
+char* getlogname_malloc(void) {
+        uid_t uid;
+        long bufsize;
+        char *buf, *name;
+        struct passwd pwbuf, *pw = NULL;
+        struct stat st;
+
+        if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
+                uid = st.st_uid;
+        else
+                uid = getuid();
+
+        /* Shortcut things to avoid NSS lookups */
+        if (uid == 0)
+                return strdup("root");
+
+        if ((bufsize = sysconf(_SC_GETPW_R_SIZE_MAX)) <= 0)
+                bufsize = 4096;
+
+        if (!(buf = malloc(bufsize)))
+                return NULL;
+
+        if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw) {
+                name = strdup(pw->pw_name);
+                free(buf);
+                return name;
+        }
+
+        free(buf);
+
+        if (asprintf(&name, "%lu", (unsigned long) uid) < 0)
+                return NULL;
+
+        return name;
+}
+
+char *getttyname_malloc(void) {
+        char path[PATH_MAX], *p;
+
+        if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0)
+                return strdup("unknown");
+
+        char_array_0(path);
+
+        p = path;
+        if (startswith(path, "/dev/"))
+                p += 5;
+
+        return strdup(p);
+}
+
 static const char *const ioprio_class_table[] = {
         [IOPRIO_CLASS_NONE] = "none",
         [IOPRIO_CLASS_RT] = "realtime",