X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=766aa02965825651481d6bdf4146372a72f50def;hp=f7d538aaacf87496367661ba3822bc2f44cb6070;hb=f872ec33973b2f508d36939e917ffafd47a87b07;hpb=e99e38bbdcca3fe5956823bdb3d38544ccf93221 diff --git a/src/util.c b/src/util.c index f7d538aaa..766aa0296 100644 --- a/src/util.c +++ b/src/util.c @@ -43,6 +43,9 @@ #include #include #include +#include +#include +#include #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; @@ -786,6 +893,53 @@ int mkdir_p(const char *path, mode_t mode) { return 0; } +int rmdir_parents(const char *path, const char *stop) { + size_t l; + int r = 0; + + assert(path); + assert(stop); + + l = strlen(path); + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + while (l > 0) { + char *t; + + /* Skip last component */ + while (l > 0 && path[l-1] != '/') + l--; + + /* Skip trailing slashes */ + while (l > 0 && path[l-1] == '/') + l--; + + if (l <= 0) + break; + + if (!(t = strndup(path, l))) + return -ENOMEM; + + if (path_startswith(stop, t)) { + free(t); + return 0; + } + + r = rmdir(t); + free(t); + + if (r < 0) + if (errno != ENOENT) + return -errno; + } + + return 0; +} + + char hexchar(int x) { static const char table[16] = "0123456789abcdef"; @@ -1345,7 +1499,7 @@ char *format_timestamp(char *buf, size_t l, usec_t t) { if (t <= 0) return NULL; - sec = (time_t) t / USEC_PER_SEC; + sec = (time_t) (t / USEC_PER_SEC); if (strftime(buf, l, "%a, %d %b %Y %H:%M:%S %z", localtime_r(&sec, &tm)) <= 0) return NULL; @@ -1353,6 +1507,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 +1801,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 +1843,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 +1938,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[]) { @@ -1756,7 +2011,7 @@ int close_pipe(int p[]) { return a < 0 ? a : b; } -ssize_t loop_read(int fd, void *buf, size_t nbytes) { +ssize_t loop_read(int fd, void *buf, size_t nbytes, bool do_poll) { uint8_t *p; ssize_t n = 0; @@ -1770,10 +2025,10 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes) { if ((k = read(fd, p, nbytes)) <= 0) { - if (errno == EINTR) + if (k < 0 && errno == EINTR) continue; - if (errno == EAGAIN) { + if (k < 0 && errno == EAGAIN && do_poll) { struct pollfd pollfd; zero(pollfd); @@ -1804,6 +2059,54 @@ ssize_t loop_read(int fd, void *buf, size_t nbytes) { return n; } +ssize_t loop_write(int fd, const void *buf, size_t nbytes, bool do_poll) { + const uint8_t *p; + ssize_t n = 0; + + assert(fd >= 0); + assert(buf); + + p = buf; + + while (nbytes > 0) { + ssize_t k; + + if ((k = write(fd, p, nbytes)) <= 0) { + + if (k < 0 && errno == EINTR) + continue; + + if (k < 0 && errno == EAGAIN && do_poll) { + struct pollfd pollfd; + + zero(pollfd); + pollfd.fd = fd; + pollfd.events = POLLOUT; + + if (poll(&pollfd, 1, -1) < 0) { + if (errno == EINTR) + continue; + + return n > 0 ? n : -errno; + } + + if (pollfd.revents != POLLOUT) + return n > 0 ? n : -EIO; + + continue; + } + + return n > 0 ? n : (k < 0 ? -errno : 0); + } + + p += k; + nbytes -= k; + n += k; + } + + return n; +} + int path_is_mount_point(const char *t) { struct stat a, b; char *copy; @@ -1937,6 +2240,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), true); + 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",