-/*-*- Mode: C; c-basic-offset: 8 -*-*/
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
#include <sys/utsname.h>
#include <pwd.h>
#include <netinet/ip.h>
+#include <linux/kd.h>
+#include <dlfcn.h>
+#include <sys/wait.h>
#include "macro.h"
#include "util.h"
#include "missing.h"
#include "log.h"
#include "strv.h"
+#include "label.h"
+#include "exit-status.h"
bool streq_ptr(const char *a, const char *b) {
goto finish;
}
+ if (!endswith(line, "\n"))
+ fputc('\n', f);
+
r = 0;
finish:
fclose(f);
int read_one_line_file(const char *fn, char **line) {
FILE *f;
int r;
- char t[2048], *c;
+ char t[LINE_MAX], *c;
assert(fn);
assert(line);
return r;
}
+int read_full_file(const char *fn, char **contents) {
+ FILE *f;
+ int r;
+ size_t n, l;
+ char *buf = NULL;
+ struct stat st;
+
+ if (!(f = fopen(fn, "re")))
+ return -errno;
+
+ if (fstat(fileno(f), &st) < 0) {
+ r = -errno;
+ goto finish;
+ }
+
+ n = st.st_size > 0 ? st.st_size : LINE_MAX;
+ l = 0;
+
+ for (;;) {
+ char *t;
+ size_t k;
+
+ if (!(t = realloc(buf, n+1))) {
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ buf = t;
+ k = fread(buf + l, 1, n - l, f);
+
+ if (k <= 0) {
+ if (ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ break;
+ }
+
+ l += k;
+ n *= 2;
+
+ /* Safety check */
+ if (n > 4*1024*1024) {
+ r = -E2BIG;
+ goto finish;
+ }
+ }
+
+ if (buf)
+ buf[l] = 0;
+ else if (!(buf = calloc(1, 1))) {
+ r = -errno;
+ goto finish;
+ }
+
+ *contents = buf;
+ buf = NULL;
+
+ r = 0;
+
+finish:
+ fclose(f);
+ free(buf);
+
+ return r;
+}
+
+int parse_env_file(
+ const char *fname,
+ const char *separator, ...) {
+
+ int r = 0;
+ char *contents, *p;
+
+ assert(fname);
+ assert(separator);
+
+ if ((r = read_full_file(fname, &contents)) < 0)
+ return r;
+
+ p = contents;
+ for (;;) {
+ const char *key = NULL;
+
+ p += strspn(p, separator);
+ p += strspn(p, WHITESPACE);
+
+ if (!*p)
+ break;
+
+ if (!strchr(COMMENTS, *p)) {
+ va_list ap;
+ char **value;
+
+ va_start(ap, separator);
+ while ((key = va_arg(ap, char *))) {
+ size_t n;
+ char *v;
+
+ value = va_arg(ap, char **);
+
+ n = strlen(key);
+ if (strncmp(p, key, n) != 0 ||
+ p[n] != '=')
+ continue;
+
+ p += n + 1;
+ n = strcspn(p, separator);
+
+ if (n >= 2 &&
+ strchr(QUOTES, p[0]) &&
+ p[n-1] == p[0])
+ v = strndup(p+1, n-2);
+ else
+ v = strndup(p, n);
+
+ if (!v) {
+ r = -ENOMEM;
+ va_end(ap);
+ goto fail;
+ }
+
+ if (v[0] == '\0') {
+ /* return empty value strings as NULL */
+ free(v);
+ v = NULL;
+ }
+
+ free(*value);
+ *value = v;
+
+ p += n;
+
+ r ++;
+ break;
+ }
+ va_end(ap);
+ }
+
+ if (!key)
+ p += strcspn(p, separator);
+ }
+
+fail:
+ free(contents);
+ return r;
+}
+
char *truncate_nl(char *s) {
assert(s);
fclose(f);
+ /* Kernel threads have no argv[] */
+ if (r[0] == 0) {
+ char *t;
+ int h;
+
+ free(r);
+
+ if ((h = get_process_name(pid, &t)) < 0)
+ return h;
+
+ h = asprintf(&r, "[%s]", t);
+ free(t);
+
+ if (h < 0)
+ return -ENOMEM;
+ }
+
*line = r;
return 0;
}
-char *strappend(const char *s, const char *suffix) {
- size_t a, b;
+char *strnappend(const char *s, const char *suffix, size_t b) {
+ size_t a;
char *r;
+ if (!s && !suffix)
+ return strdup("");
+
+ if (!s)
+ return strndup(suffix, b);
+
+ if (!suffix)
+ return strdup(s);
+
assert(s);
assert(suffix);
a = strlen(s);
- b = strlen(suffix);
if (!(r = new(char, a+b+1)))
return NULL;
return r;
}
+char *strappend(const char *s, const char *suffix) {
+ return strnappend(s, suffix, suffix ? strlen(suffix) : 0);
+}
+
int readlink_malloc(const char *p, char **r) {
size_t l = 100;
return 0;
}
+int parent_of_path(const char *path, char **_r) {
+ const char *e, *a = NULL, *b = NULL, *p;
+ char *r;
+ bool slash = false;
+
+ assert(path);
+ assert(_r);
+
+ if (!*path)
+ return -EINVAL;
+
+ for (e = path; *e; e++) {
+
+ if (!slash && *e == '/') {
+ a = b;
+ b = e;
+ slash = true;
+ } else if (slash && *e != '/')
+ slash = false;
+ }
+
+ if (*(e-1) == '/')
+ p = a;
+ else
+ p = b;
+
+ if (!p)
+ return -EINVAL;
+
+ if (p == path)
+ r = strdup("/");
+ else
+ r = strndup(path, p-path);
+
+ if (!r)
+ return -ENOMEM;
+
+ *_r = r;
+ return 0;
+}
+
+
char *file_name_from_path(const char *p) {
char *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 (label_mkdir(path, mode) >= 0)
if (chmod_and_chown(path, mode, uid, gid) < 0)
return -errno;
if (!(t = strndup(path, e - path)))
return -ENOMEM;
- r = mkdir(t, mode);
-
+ r = label_mkdir(t, mode);
free(t);
if (r < 0 && errno != EEXIST)
if ((r = mkdir_parents(path, mode)) < 0)
return r;
- if (mkdir(path, mode) < 0)
+ if (label_mkdir(path, mode) < 0 && errno != EEXIST)
return -errno;
return 0;
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;
*(t++) = '\'';
break;
+ case 's':
+ /* This is an extension of the XDG syntax files */
+ *(t++) = ' ';
+ break;
+
case 'x': {
/* hexadecimal encoding */
int a, b;
return r;
}
+char *cunescape(const char *s) {
+ return cunescape_length(s, strlen(s));
+}
char *xescape(const char *s, const char *bad) {
char *r, *t;
return
filename[0] == '.' ||
streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
endswith(filename, "~") ||
endswith(filename, ".rpmnew") ||
endswith(filename, ".rpmsave") ||
return buf;
}
+char *format_timestamp_pretty(char *buf, size_t l, usec_t t) {
+ usec_t n, d;
+
+ n = now(CLOCK_REALTIME);
+
+ if (t <= 0 || t > n || t + USEC_PER_DAY*7 <= t)
+ return NULL;
+
+ d = n - t;
+
+ if (d >= USEC_PER_YEAR)
+ snprintf(buf, l, "%llu years and %llu months ago",
+ (unsigned long long) (d / USEC_PER_YEAR),
+ (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH));
+ else if (d >= USEC_PER_MONTH)
+ snprintf(buf, l, "%llu months and %llu days ago",
+ (unsigned long long) (d / USEC_PER_MONTH),
+ (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY));
+ else if (d >= USEC_PER_WEEK)
+ snprintf(buf, l, "%llu weeks and %llu days ago",
+ (unsigned long long) (d / USEC_PER_WEEK),
+ (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY));
+ else if (d >= 2*USEC_PER_DAY)
+ snprintf(buf, l, "%llu days ago", (unsigned long long) (d / USEC_PER_DAY));
+ else if (d >= 25*USEC_PER_HOUR)
+ snprintf(buf, l, "1 day and %lluh ago",
+ (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR));
+ else if (d >= 6*USEC_PER_HOUR)
+ snprintf(buf, l, "%lluh ago",
+ (unsigned long long) (d / USEC_PER_HOUR));
+ else if (d >= USEC_PER_HOUR)
+ snprintf(buf, l, "%lluh %llumin ago",
+ (unsigned long long) (d / USEC_PER_HOUR),
+ (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE));
+ else if (d >= 5*USEC_PER_MINUTE)
+ snprintf(buf, l, "%llumin ago",
+ (unsigned long long) (d / USEC_PER_MINUTE));
+ else if (d >= USEC_PER_MINUTE)
+ snprintf(buf, l, "%llumin %llus ago",
+ (unsigned long long) (d / USEC_PER_MINUTE),
+ (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC));
+ else if (d >= USEC_PER_SEC)
+ snprintf(buf, l, "%llus ago",
+ (unsigned long long) (d / USEC_PER_SEC));
+ else if (d >= USEC_PER_MSEC)
+ snprintf(buf, l, "%llums ago",
+ (unsigned long long) (d / USEC_PER_MSEC));
+ else if (d > 0)
+ snprintf(buf, l, "%lluus ago",
+ (unsigned long long) d);
+ else
+ snprintf(buf, l, "now");
+
+ buf[l-1] = 0;
+ return buf;
+}
+
char *format_timespan(char *buf, size_t l, usec_t t) {
static const struct {
const char *suffix;
if (t == (usec_t) -1)
return NULL;
+ if (t == 0) {
+ snprintf(p, l, "0");
+ p[l-1] = 0;
+ return p;
+ }
+
/* The result of this function can be parsed with parse_usec */
for (i = 0; i < ELEMENTSOF(table); i++) {
}
int ask(char *ret, const char *replies, const char *text, ...) {
+ bool on_tty;
+
assert(ret);
assert(replies);
assert(text);
+ on_tty = isatty(STDOUT_FILENO);
+
for (;;) {
va_list ap;
char c;
int r;
bool need_nl = true;
- fputs("\x1B[1m", stdout);
+ if (on_tty)
+ fputs("\x1B[1m", stdout);
va_start(ap, text);
vprintf(text, ap);
va_end(ap);
- fputs("\x1B[0m", stdout);
+ if (on_tty)
+ fputs("\x1B[0m", stdout);
fflush(stdout);
int reset_terminal(int fd) {
struct termios termios;
int r = 0;
+ long arg;
+
+ /* Set terminal to some sane defaults */
assert(fd >= 0);
- /* Set terminal to some sane defaults */
+ /* We leave locked terminal attributes untouched, so that
+ * Plymouth may set whatever it wants to set, and we don't
+ * interfere with that. */
+
+ /* Disable exclusive mode, just in case */
+ ioctl(fd, TIOCNXCL);
+
+ /* Enable console unicode mode */
+ arg = K_UNICODE;
+ ioctl(fd, KDSKBMODE, &arg);
if (tcgetattr(fd, &termios) < 0) {
r = -errno;
assert(notify >= 0);
for (;;) {
- struct inotify_event e;
+ uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
ssize_t l;
+ struct inotify_event *e;
- if ((l = read(notify, &e, sizeof(e))) != sizeof(e)) {
+ if ((l = read(notify, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
- if (l < 0) {
+ if (errno == EINTR)
+ continue;
- if (errno == EINTR)
- continue;
+ r = -errno;
+ goto fail;
+ }
+
+ e = (struct inotify_event*) inotify_buffer;
+
+ while (l > 0) {
+ size_t step;
- r = -errno;
- } else
+ if (e->wd != wd || !(e->mask & IN_CLOSE)) {
r = -EIO;
+ goto fail;
+ }
- goto fail;
- }
+ step = sizeof(struct inotify_event) + e->len;
+ assert(step <= (size_t) l);
- if (e.wd != wd || !(e.mask & IN_CLOSE)) {
- r = -errno;
- goto fail;
+ e = (struct inotify_event*) ((uint8_t*) e + step);
+ l -= step;
}
break;
int path_is_mount_point(const char *t) {
struct stat a, b;
- char *copy;
+ char *parent;
+ int r;
if (lstat(t, &a) < 0) {
-
if (errno == ENOENT)
return 0;
return -errno;
}
- if (!(copy = strdup(t)))
- return -ENOMEM;
+ if ((r = parent_of_path(t, &parent)) < 0)
+ return r;
- if (lstat(dirname(copy), &b) < 0) {
- free(copy);
- return -errno;
- }
+ r = lstat(parent, &b);
+ free(parent);
- free(copy);
+ if (r < 0)
+ return -errno;
return a.st_dev != b.st_dev;
}
return 0;
}
+int make_null_stdio(void) {
+ int null_fd;
+
+ if ((null_fd = open("/dev/null", O_RDWR|O_NOCTTY)) < 0)
+ return -errno;
+
+ return make_stdio(null_fd);
+}
+
bool is_clean_exit(int code, int status) {
if (code == CLD_EXITED)
return false;
}
+bool is_clean_exit_lsb(int code, int status) {
+
+ if (is_clean_exit(code, status))
+ return true;
+
+ return
+ code == CLD_EXITED &&
+ (status == EXIT_NOTINSTALLED || status == EXIT_NOTCONFIGURED);
+}
+
bool is_device_path(const char *path) {
/* Returns true on paths that refer to a device, either in
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;
int getttyname_malloc(char **r) {
char path[PATH_MAX], *p, *c;
+ int k;
assert(r);
- if (ttyname_r(STDIN_FILENO, path, sizeof(path)) < 0)
- return -errno;
+ if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0)
+ return -k;
char_array_0(path);
if (!(d = fdopendir(fd))) {
close_nointr_nofail(fd);
- return -errno;
+
+ return errno == ENOENT ? 0 : -errno;
}
for (;;) {
struct stat st;
if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
int subdir_fd;
if ((subdir_fd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
continue;
}
}
if (unlinkat(fd, de->d_name, AT_REMOVEDIR) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
}
} else if (!only_dirs) {
if (unlinkat(fd, de->d_name, 0) < 0) {
- if (ret == 0)
+ if (ret == 0 && errno != ENOENT)
ret = -errno;
}
}
}
void status_welcome(void) {
+ char *pretty_name = NULL, *ansi_color = NULL;
+ const char *const_pretty = NULL, *const_color = NULL;
+ int r;
+
+ if ((r = parse_env_file("/etc/os-release", NEWLINE,
+ "PRETTY_NAME", &pretty_name,
+ "ANSI_COLOR", &ansi_color,
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+ }
#if defined(TARGET_FEDORA)
- char *r;
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) {
- if (read_one_line_file("/etc/system-release", &r) < 0)
- return;
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/system-release: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
- truncate_nl(r);
+ if (!ansi_color && pretty_name) {
- /* This tries to mimic the color magic the old Red Hat sysinit
- * script did. */
+ /* 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);
+ if (startswith(pretty_name, "Red Hat"))
+ const_color = "0;31"; /* Red for RHEL */
+ else if (startswith(pretty_name, "Fedora"))
+ const_color = "0;34"; /* Blue for Fedora */
+ }
+
+#elif defined(TARGET_SUSE)
+
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/SuSE-release", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/SuSE-release: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
+
+ if (!ansi_color)
+ const_color = "0;32"; /* Green for openSUSE */
+
+#elif defined(TARGET_GENTOO)
+
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
+
+ if (!ansi_color)
+ const_color = "1;34"; /* Light Blue for Gentoo */
+
+#elif defined(TARGET_DEBIAN)
+
+ if (!pretty_name) {
+ char *version;
+
+ if ((r = read_one_line_file("/etc/debian_version", &version)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/debian_version: %s", strerror(-r));
+ } else {
+ truncate_nl(version);
+ pretty_name = strappend("Debian ", version);
+ free(version);
+
+ if (!pretty_name)
+ log_warning("Failed to allocate Debian version string.");
+ }
+ }
+
+ if (!ansi_color)
+ const_color = "1;31"; /* Light Red for Debian */
+
+#elif defined(TARGET_UBUNTU)
+
+ if ((r = parse_env_file("/etc/lsb-release", NEWLINE,
+ "DISTRIB_DESCRIPTION", &pretty_name,
+ NULL)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/lsb-release: %s", strerror(-r));
+ }
+
+ if (!ansi_color)
+ const_color = "0;33"; /* Orange/Brown for Ubuntu */
+
+#endif
+
+ if (!pretty_name && !const_pretty)
+ const_pretty = "Linux";
+
+ if (!ansi_color && !const_color)
+ const_color = "1";
+
+ status_printf("Welcome to \x1B[%sm%s\x1B[0m!\n",
+ const_color ? const_color : ansi_color,
+ const_pretty ? const_pretty : pretty_name);
+
+ free(ansi_color);
+ free(pretty_name);
+}
+
+char *replace_env(const char *format, char **env) {
+ enum {
+ WORD,
+ CURLY,
+ VARIABLE
+ } state = WORD;
+
+ const char *e, *word = format;
+ char *r = NULL, *k;
+
+ assert(format);
+
+ for (e = format; *e; e ++) {
+
+ switch (state) {
+
+ case WORD:
+ if (*e == '$')
+ state = CURLY;
+ break;
+
+ case CURLY:
+ if (*e == '{') {
+ if (!(k = strnappend(r, word, e-word-1)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e-1;
+ state = VARIABLE;
+
+ } else if (*e == '$') {
+ if (!(k = strnappend(r, word, e-word)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ } else
+ state = WORD;
+ break;
+
+ case VARIABLE:
+ if (*e == '}') {
+ const char *t;
+
+ if (!(t = strv_env_get_with_length(env, word+2, e-word-2)))
+ t = "";
+
+ if (!(k = strappend(r, t)))
+ goto fail;
+
+ free(r);
+ r = k;
+
+ word = e+1;
+ state = WORD;
+ }
+ break;
+ }
+ }
+
+ if (!(k = strnappend(r, word, e-word)))
+ goto fail;
free(r);
+ return k;
-#elif defined(TARGET_SUSE)
+fail:
+ free(r);
+ return NULL;
+}
+
+char **replace_env_argv(char **argv, char **env) {
+ char **r, **i;
+ unsigned k = 0, l = 0;
+
+ l = strv_length(argv);
+
+ if (!(r = new(char*, l+1)))
+ return NULL;
+
+ STRV_FOREACH(i, argv) {
+
+ /* If $FOO appears as single word, replace it by the split up variable */
+ if ((*i)[0] == '$' && (*i)[1] != '{') {
+ char *e;
+ char **w, **m;
+ unsigned q;
+
+ if ((e = strv_env_get(env, *i+1))) {
+
+ if (!(m = strv_split_quoted(e))) {
+ r[k] = NULL;
+ strv_free(r);
+ return NULL;
+ }
+ } else
+ m = NULL;
+
+ q = strv_length(m);
+ l = l + q - 1;
+
+ if (!(w = realloc(r, sizeof(char*) * (l+1)))) {
+ r[k] = NULL;
+ strv_free(r);
+ strv_free(m);
+ return NULL;
+ }
+
+ r = w;
+ if (m) {
+ memcpy(r + k, m, q * sizeof(char*));
+ free(m);
+ }
+
+ k += q;
+ continue;
+ }
+
+ /* If ${FOO} appears as part of a word, replace it by the variable as-is */
+ if (!(r[k++] = replace_env(*i, env))) {
+ strv_free(r);
+ return NULL;
+ }
+ }
+
+ r[k] = NULL;
+ return r;
+}
+
+int columns(void) {
+ static __thread int parsed_columns = 0;
+ const char *e;
+
+ if (parsed_columns > 0)
+ return parsed_columns;
+
+ if ((e = getenv("COLUMNS")))
+ parsed_columns = atoi(e);
+
+ if (parsed_columns <= 0) {
+ struct winsize ws;
+ zero(ws);
+
+ if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) >= 0)
+ parsed_columns = ws.ws_col;
+ }
+
+ if (parsed_columns <= 0)
+ parsed_columns = 80;
+
+ return parsed_columns;
+}
+
+int running_in_chroot(void) {
+ struct stat a, b;
+
+ zero(a);
+ zero(b);
+
+ /* Only works as root */
+
+ if (stat("/proc/1/root", &a) < 0)
+ return -errno;
+
+ if (stat("/", &b) < 0)
+ return -errno;
+
+ return
+ a.st_dev != b.st_dev ||
+ a.st_ino != b.st_ino;
+}
+
+char *ellipsize(const char *s, unsigned length, unsigned percent) {
+ size_t l, x;
char *r;
- if (read_one_line_file("/etc/SuSE-release", &r) < 0)
+ assert(s);
+ assert(percent <= 100);
+ assert(length >= 3);
+
+ l = strlen(s);
+
+ if (l <= 3 || l <= length)
+ return strdup(s);
+
+ if (!(r = new0(char, length+1)))
+ return r;
+
+ x = (length * percent) / 100;
+
+ if (x > length - 3)
+ x = length - 3;
+
+ memcpy(r, s, x);
+ r[x] = '.';
+ r[x+1] = '.';
+ r[x+2] = '.';
+ memcpy(r + x + 3,
+ s + l - (length - x - 3),
+ length - x - 3);
+
+ return r;
+}
+
+int touch(const char *path) {
+ int fd;
+
+ assert(path);
+
+ if ((fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY, 0666)) < 0)
+ return -errno;
+
+ close_nointr_nofail(fd);
+ return 0;
+}
+
+char *unquote(const char *s, const char* quotes) {
+ size_t l;
+ assert(s);
+
+ if ((l = strlen(s)) < 2)
+ return strdup(s);
+
+ if (strchr(quotes, s[0]) && s[l-1] == s[0])
+ return strndup(s+1, l-2);
+
+ return strdup(s);
+}
+
+char *normalize_env_assignment(const char *s) {
+ char *name, *value, *p, *r;
+
+ p = strchr(s, '=');
+
+ if (!p) {
+ if (!(r = strdup(s)))
+ return NULL;
+
+ return strstrip(r);
+ }
+
+ if (!(name = strndup(s, p - s)))
+ return NULL;
+
+ if (!(p = strdup(p+1))) {
+ free(name);
+ return NULL;
+ }
+
+ value = unquote(strstrip(p), QUOTES);
+ free(p);
+
+ if (!value) {
+ free(p);
+ free(name);
+ return NULL;
+ }
+
+ if (asprintf(&r, "%s=%s", name, value) < 0)
+ r = NULL;
+
+ free(value);
+ free(name);
+
+ return r;
+}
+
+int wait_for_terminate(pid_t pid, siginfo_t *status) {
+ assert(pid >= 1);
+ assert(status);
+
+ for (;;) {
+ zero(*status);
+
+ if (waitid(P_PID, pid, status, WEXITED) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ return -errno;
+ }
+
+ return 0;
+ }
+}
+
+int wait_for_terminate_and_warn(const char *name, pid_t pid) {
+ int r;
+ siginfo_t status;
+
+ assert(name);
+ assert(pid > 1);
+
+ if ((r = wait_for_terminate(pid, &status)) < 0) {
+ log_warning("Failed to wait for %s: %s", name, strerror(-r));
+ return r;
+ }
+
+ if (status.si_code == CLD_EXITED) {
+ if (status.si_status != 0) {
+ log_warning("%s failed with error code %i.", name, status.si_status);
+ return -EPROTO;
+ }
+
+ log_debug("%s succeeded.", name);
+ return 0;
+
+ } else if (status.si_code == CLD_KILLED ||
+ status.si_code == CLD_DUMPED) {
+
+ log_warning("%s terminated by signal %s.", name, signal_to_string(status.si_status));
+ return -EPROTO;
+ }
+
+ log_warning("%s failed due to unknown reason.", name);
+ return -EPROTO;
+
+}
+
+void freeze(void) {
+ sync();
+
+ for (;;)
+ pause();
+}
+
+bool null_or_empty(struct stat *st) {
+ assert(st);
+
+ if (S_ISREG(st->st_mode) && st->st_size <= 0)
+ return true;
+
+ if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode))
+ return true;
+
+ return false;
+}
+
+DIR *xopendirat(int fd, const char *name, int flags) {
+ int nfd;
+ DIR *d;
+
+ if ((nfd = openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|flags)) < 0)
+ return NULL;
+
+ if (!(d = fdopendir(nfd))) {
+ close_nointr_nofail(nfd);
+ return NULL;
+ }
+
+ return d;
+}
+
+int signal_from_string_try_harder(const char *s) {
+ int signo;
+ assert(s);
+
+ if ((signo = signal_from_string(s)) <= 0)
+ if (startswith(s, "SIG"))
+ return signal_from_string(s+3);
+
+ return signo;
+}
+
+void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
+
+ assert(f);
+ assert(name);
+ assert(t);
+
+ if (!dual_timestamp_is_set(t))
return;
- truncate_nl(r);
+ fprintf(f, "%s=%llu %llu\n",
+ name,
+ (unsigned long long) t->realtime,
+ (unsigned long long) t->monotonic);
+}
- 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
+void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
+ unsigned long long a, b;
+
+ assert(value);
+ assert(t);
+
+ if (sscanf(value, "%lli %llu", &a, &b) != 2)
+ log_debug("Failed to parse finish timestamp value %s", value);
+ else {
+ t->realtime = a;
+ t->monotonic = b;
+ }
+}
+
+char *fstab_node_to_udev_node(const char *p) {
+ char *dn, *t, *u;
+ int r;
+
+ /* FIXME: to follow udev's logic 100% we need to leave valid
+ * UTF8 chars unescaped */
+
+ if (startswith(p, "LABEL=")) {
+
+ if (!(u = unquote(p+6, "\"\'")))
+ return NULL;
+
+ t = xescape(u, "/ ");
+ free(u);
+
+ if (!t)
+ return NULL;
+
+ r = asprintf(&dn, "/dev/disk/by-label/%s", t);
+ free(t);
+
+ if (r < 0)
+ return NULL;
+
+ return dn;
+ }
+
+ if (startswith(p, "UUID=")) {
+
+ if (!(u = unquote(p+5, "\"\'")))
+ return NULL;
+
+ t = xescape(u, "/ ");
+ free(u);
+
+ if (!t)
+ return NULL;
+
+ r = asprintf(&dn, "/dev/disk/by-uuid/%s", t);
+ free(t);
+
+ if (r < 0)
+ return NULL;
+
+ return dn;
+ }
+
+ return strdup(p);
+}
+
+void filter_environ(const char *prefix) {
+ int i, j;
+ assert(prefix);
+
+ if (!environ)
+ return;
+
+ for (i = 0, j = 0; environ[i]; i++) {
+
+ if (startswith(environ[i], prefix))
+ continue;
+
+ environ[j++] = environ[i];
+ }
+
+ environ[j] = NULL;
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ if (startswith(tty, "/dev/"))
+ tty += 5;
+
+ if (startswith(tty, "tty") &&
+ tty[3] >= '0' && tty[3] <= '9')
+ return "TERM=linux";
+
+ /* FIXME: Proper handling of /dev/console would be cool */
+
+ return "TERM=vt100";
}
static const char *const ioprio_class_table[] = {
};
DEFINE_STRING_TABLE_LOOKUP(ip_tos, int);
+
+static const char *const signal_table[] = {
+ [SIGHUP] = "HUP",
+ [SIGINT] = "INT",
+ [SIGQUIT] = "QUIT",
+ [SIGILL] = "ILL",
+ [SIGTRAP] = "TRAP",
+ [SIGABRT] = "ABRT",
+ [SIGBUS] = "BUS",
+ [SIGFPE] = "FPE",
+ [SIGKILL] = "KILL",
+ [SIGUSR1] = "USR1",
+ [SIGSEGV] = "SEGV",
+ [SIGUSR2] = "USR2",
+ [SIGPIPE] = "PIPE",
+ [SIGALRM] = "ALRM",
+ [SIGTERM] = "TERM",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
+ [SIGCHLD] = "CHLD",
+ [SIGCONT] = "CONT",
+ [SIGSTOP] = "STOP",
+ [SIGTSTP] = "TSTP",
+ [SIGTTIN] = "TTIN",
+ [SIGTTOU] = "TTOU",
+ [SIGURG] = "URG",
+ [SIGXCPU] = "XCPU",
+ [SIGXFSZ] = "XFSZ",
+ [SIGVTALRM] = "VTALRM",
+ [SIGPROF] = "PROF",
+ [SIGWINCH] = "WINCH",
+ [SIGIO] = "IO",
+ [SIGPWR] = "PWR",
+ [SIGSYS] = "SYS"
+};
+
+DEFINE_STRING_TABLE_LOOKUP(signal, int);