#include <sys/poll.h>
#include <libgen.h>
#include <ctype.h>
+#include <sys/prctl.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <netinet/ip.h>
#include "macro.h"
#include "util.h"
return timespec_load(&ts);
}
+dual_timestamp* dual_timestamp_get(dual_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);
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);
return -EINVAL;
}
-int safe_atou(const char *s, unsigned *ret_u) {
- char *x = NULL;
- unsigned long l;
+int parse_pid(const char *s, pid_t* ret_pid) {
+ unsigned long ul;
+ pid_t pid;
+ int r;
assert(s);
- assert(ret_u);
+ assert(ret_pid);
- errno = 0;
- l = strtoul(s, &x, 0);
+ if ((r = safe_atolu(s, &ul)) < 0)
+ return r;
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
+ pid = (pid_t) ul;
- if ((unsigned long) (unsigned) l != l)
+ if ((unsigned long) pid != ul)
return -ERANGE;
- *ret_u = (unsigned) l;
- return 0;
-}
-
-int safe_atoi(const char *s, int *ret_i) {
- char *x = NULL;
- long l;
-
- assert(s);
- assert(ret_i);
-
- errno = 0;
- l = strtol(s, &x, 0);
-
- if (!x || *x || errno)
- return errno ? -errno : -EINVAL;
-
- if ((long) (int) l != l)
+ if (pid <= 0)
return -ERANGE;
- *ret_i = (int) l;
+ *ret_pid = pid;
return 0;
}
-int safe_atolu(const char *s, long unsigned *ret_lu) {
+int safe_atou(const char *s, unsigned *ret_u) {
char *x = NULL;
unsigned long l;
assert(s);
- assert(ret_lu);
+ assert(ret_u);
errno = 0;
l = strtoul(s, &x, 0);
if (!x || *x || errno)
return errno ? -errno : -EINVAL;
- *ret_lu = l;
+ if ((unsigned long) (unsigned) l != l)
+ return -ERANGE;
+
+ *ret_u = (unsigned) l;
return 0;
}
-int safe_atoli(const char *s, long int *ret_li) {
+int safe_atoi(const char *s, int *ret_i) {
char *x = NULL;
long l;
assert(s);
- assert(ret_li);
+ assert(ret_i);
errno = 0;
l = strtol(s, &x, 0);
if (!x || *x || errno)
return errno ? -errno : -EINVAL;
- *ret_li = l;
+ if ((long) (int) l != l)
+ return -ERANGE;
+
+ *ret_i = (int) l;
return 0;
}
/* Split a string into words, but consider strings enclosed in '' and
* "" as words even if they include spaces. */
char *split_quoted(const char *c, size_t *l, char **state) {
- char *current;
+ char *current, *e;
+ bool escaped = false;
current = *state ? *state : (char*) c;
if (*current == '\'') {
current ++;
- *l = strcspn(current, "'");
- *state = current+*l;
- if (**state == '\'')
- (*state)++;
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\'')
+ break;
+ }
+
+ *l = e-current;
+ *state = *e == 0 ? e : e+1;
} else if (*current == '\"') {
current ++;
- *l = strcspn(current, "\"");
- *state = current+*l;
- if (**state == '\"')
- (*state)++;
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (*e == '\"')
+ break;
+ }
+
+ *l = e-current;
+ *state = *e == 0 ? e : e+1;
} else {
- *l = strcspn(current, WHITESPACE);
- *state = current+*l;
+ for (e = current; *e; e++) {
+ if (escaped)
+ escaped = false;
+ else if (*e == '\\')
+ escaped = true;
+ else if (strchr(WHITESPACE, *e))
+ break;
+ }
+ *l = e-current;
+ *state = e;
}
- /* FIXME: Cannot deal with strings that have spaces AND ticks
- * in them */
-
return (char*) current;
}
int r;
FILE *f;
char fn[132], line[256], *p;
- long long unsigned ppid;
+ long unsigned ppid;
assert(pid >= 0);
assert(_ppid);
- assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%llu/stat", (unsigned long long) pid) < (int) (sizeof(fn)-1));
+ assert_se(snprintf(fn, sizeof(fn)-1, "/proc/%lu/stat", (unsigned long) pid) < (int) (sizeof(fn)-1));
fn[sizeof(fn)-1] = 0;
if (!(f = fopen(fn, "r")))
if (sscanf(p, " "
"%*c " /* state */
- "%llu ", /* ppid */
+ "%lu ", /* ppid */
&ppid) != 1)
return -EIO;
- if ((long long unsigned) (pid_t) ppid != ppid)
+ if ((long unsigned) (pid_t) ppid != ppid)
return -ERANGE;
*_ppid = (pid_t) ppid;
assert(pid >= 1);
assert(name);
- if (asprintf(&p, "/proc/%llu/comm", (unsigned long long) pid) < 0)
+ if (asprintf(&p, "/proc/%lu/comm", (unsigned long) pid) < 0)
return -ENOMEM;
r = read_one_line_file(p, name);
return 0;
}
+int get_process_cmdline(pid_t pid, size_t max_length, char **line) {
+ char *p, *r, *k;
+ int c;
+ bool space = false;
+ size_t left;
+ FILE *f;
+
+ assert(pid >= 1);
+ assert(max_length > 0);
+ assert(line);
+
+ if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "r");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ if (!(r = new(char, max_length))) {
+ fclose(f);
+ return -ENOMEM;
+ }
+
+ k = r;
+ left = max_length;
+ while ((c = getc(f)) != EOF) {
+
+ if (isprint(c)) {
+ if (space) {
+ if (left <= 4)
+ break;
+
+ *(k++) = ' ';
+ left--;
+ space = false;
+ }
+
+ if (left <= 4)
+ break;
+
+ *(k++) = (char) c;
+ left--;
+ } else
+ space = true;
+ }
+
+ if (left <= 4) {
+ size_t n = MIN(left-1, 3U);
+ memcpy(k, "...", n);
+ k[n] = 0;
+ } else
+ *k = 0;
+
+ fclose(f);
+
+ *line = r;
+ return 0;
+}
+
char *strappend(const char *s, const char *suffix) {
size_t a, b;
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;
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;
return r;
}
+int safe_mkdir(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ struct stat st;
+
+ if (mkdir(path, mode) >= 0)
+ if (chmod_and_chown(path, mode, uid, gid) < 0)
+ return -errno;
+
+ if (lstat(path, &st) < 0)
+ return -errno;
+
+ if ((st.st_mode & 0777) != mode ||
+ st.st_uid != uid ||
+ st.st_gid != gid ||
+ !S_ISDIR(st.st_mode)) {
+ errno = EEXIST;
+ return -errno;
+ }
+
+ return 0;
+}
+
+
int mkdir_parents(const char *path, mode_t mode) {
const char *p, *e;
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";
return r;
}
-char *cunescape(const char *s) {
+char *cunescape_length(const char *s, size_t length) {
char *r, *t;
const char *f;
/* Undoes C style string escaping */
- if (!(r = new(char, strlen(s)+1)))
+ if (!(r = new(char, length+1)))
return r;
- for (f = s, t = r; *f; f++) {
+ for (f = s, t = r; f < s + length; f++) {
if (*f != '\\') {
*(t++) = *f;
default:
/* Invalid escape code, let's take it literal then */
*(t++) = '\\';
- *(t++) = 'f';
+ *(t++) = *f;
break;
}
}
return r;
}
+char *cunescape(const char *s) {
+ return cunescape_length(s, strlen(s));
+}
char *xescape(const char *s, const char *bad) {
char *r, *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;
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",
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;
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);
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;
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);
+}
+
+int getmachineid_malloc(char **b) {
+ int r;
+
+ assert(b);
+
+ if ((r = read_one_line_file("/var/lib/dbus/machine-id", b)) < 0)
+ return r;
+
+ strstrip(*b);
+ return 0;
+}
+
+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;
+}
+
+int getttyname_malloc(char **r) {
+ char path[PATH_MAX], *p, *c;
+
+ assert(r);
+
+ if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0)
+ return -errno;
+
+ char_array_0(path);
+
+ p = path;
+ if (startswith(path, "/dev/"))
+ p += 5;
+
+ if (!(c = strdup(p)))
+ return -ENOMEM;
+
+ *r = c;
+ return 0;
+}
+
+static int rm_rf_children(int fd, bool only_dirs) {
+ DIR *d;
+ int ret = 0;
+
+ assert(fd >= 0);
+
+ /* This returns the first error we run into, but nevertheless
+ * tries to go on */
+
+ if (!(d = fdopendir(fd))) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ for (;;) {
+ struct dirent buf, *de;
+ bool is_dir;
+ int r;
+
+ if ((r = readdir_r(d, &buf, &de)) != 0) {
+ if (ret == 0)
+ ret = -r;
+ break;
+ }
+
+ if (!de)
+ break;
+
+ if (streq(de->d_name, ".") || streq(de->d_name, ".."))
+ continue;
+
+ if (de->d_type == DT_UNKNOWN) {
+ struct stat st;
+
+ if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ continue;
+ }
+
+ is_dir = S_ISDIR(st.st_mode);
+ } else
+ is_dir = de->d_type == DT_DIR;
+
+ if (is_dir) {
+ int subdir_fd;
+
+ if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ continue;
+ }
+
+ if ((r = rm_rf_children(subdir_fd, only_dirs)) < 0) {
+ if (ret == 0)
+ ret = r;
+ }
+
+ if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ }
+ } else if (!only_dirs) {
+
+ if (unlinkat(fd, de->d_name, 0) < 0) {
+ if (ret == 0)
+ ret = -errno;
+ }
+ }
+ }
+
+ closedir(d);
+
+ return ret;
+}
+
+int rm_rf(const char *path, bool only_dirs, bool delete_root) {
+ int fd;
+ int r;
+
+ assert(path);
+
+ if ((fd = open(path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
+
+ if (errno != ENOTDIR)
+ return -errno;
+
+ if (delete_root && !only_dirs)
+ if (unlink(path) < 0)
+ return -errno;
+
+ return 0;
+ }
+
+ r = rm_rf_children(fd, only_dirs);
+
+ if (delete_root)
+ if (rmdir(path) < 0) {
+ if (r == 0)
+ r = -errno;
+ }
+
+ return r;
+}
+
+int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+ assert(path);
+
+ /* Under the assumption that we are running privileged we
+ * first change the access mode and only then hand out
+ * ownership to avoid a window where access is too open. */
+
+ if (chmod(path, mode) < 0)
+ return -errno;
+
+ if (chown(path, uid, gid) < 0)
+ return -errno;
+
+ return 0;
+}
+
+cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
+ cpu_set_t *r;
+ unsigned n = 1024;
+
+ /* Allocates the cpuset in the right size */
+
+ for (;;) {
+ if (!(r = CPU_ALLOC(n)))
+ return NULL;
+
+ if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), r) >= 0) {
+ CPU_ZERO_S(CPU_ALLOC_SIZE(n), r);
+
+ if (ncpus)
+ *ncpus = n;
+
+ return r;
+ }
+
+ CPU_FREE(r);
+
+ if (errno != EINVAL)
+ return NULL;
+
+ n *= 2;
+ }
+}
+
+void status_vprintf(const char *format, va_list ap) {
+ char *s = NULL;
+ int fd = -1;
+
+ assert(format);
+
+ /* This independent of logging, as status messages are
+ * optional and go exclusively to the console. */
+
+ if (vasprintf(&s, format, ap) < 0)
+ goto finish;
+
+ if ((fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0)
+ goto finish;
+
+ write(fd, s, strlen(s));
+
+finish:
+ free(s);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+}
+
+void status_printf(const char *format, ...) {
+ va_list ap;
+
+ assert(format);
+
+ va_start(ap, format);
+ status_vprintf(format, ap);
+ va_end(ap);
+}
+
+void status_welcome(void) {
+
+#if defined(TARGET_FEDORA)
+ char *r;
+
+ if (read_one_line_file("/etc/system-release", &r) < 0)
+ return;
+
+ truncate_nl(r);
+
+ /* This tries to mimic the color magic the old Red Hat sysinit
+ * script did. */
+
+ if (startswith(r, "Red Hat"))
+ status_printf("Welcome to \x1B[0;31m%s\x1B[0m!\n", r); /* Red for RHEL */
+ else if (startswith(r, "Fedora"))
+ status_printf("Welcome to \x1B[0;34m%s\x1B[0m!\n", r); /* Blue for Fedora */
+ else
+ status_printf("Welcome to %s!\n", r);
+
+ free(r);
+
+#elif defined(TARGET_SUSE)
+ char *r;
+
+ if (read_one_line_file("/etc/SuSE-release", &r) < 0)
+ return;
+
+ truncate_nl(r);
+
+ status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */
+ free(r);
+#else
+#warning "You probably should add a welcome text logic here."
+#endif
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
};
DEFINE_STRING_TABLE_LOOKUP(rlimit, int);
+
+static const char* const ip_tos_table[] = {
+ [IPTOS_LOWDELAY] = "low-delay",
+ [IPTOS_THROUGHPUT] = "throughput",
+ [IPTOS_RELIABILITY] = "reliability",
+ [IPTOS_LOWCOST] = "low-cost",
+};
+
+DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);