#include <netinet/ip.h>
#include <linux/kd.h>
#include <dlfcn.h>
+#include <sys/wait.h>
#include "macro.h"
#include "util.h"
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);
return
filename[0] == '.' ||
streq(filename, "lost+found") ||
+ streq(filename, "aquota.user") ||
+ streq(filename, "aquota.group") ||
endswith(filename, "~") ||
endswith(filename, ".rpmnew") ||
endswith(filename, ".rpmsave") ||
}
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);
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;
- r = -errno;
- } else
+ while (l > 0) {
+ size_t step;
+
+ 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 = -EIO;
- goto fail;
+ e = (struct inotify_event*) ((uint8_t*) e + step);
+ l -= step;
}
break;
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)
}
void status_welcome(void) {
+ char *pretty_name = NULL, *ansi_color = NULL;
+ const char *const_pretty = NULL, *const_color = NULL;
+ int r;
-#if defined(TARGET_FEDORA)
- char *r;
+ if ((r = parse_env_file("/etc/os-release", NEWLINE,
+ "PRETTY_NAME", &pretty_name,
+ "ANSI_COLOR", &ansi_color,
+ NULL)) < 0) {
- if (read_one_line_file("/etc/system-release", &r) < 0)
- return;
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/os-release: %s", strerror(-r));
+ }
- truncate_nl(r);
+#if defined(TARGET_FEDORA)
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) {
- /* This tries to mimic the color magic the old Red Hat sysinit
- * script did. */
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/system-release: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
- 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 (!ansi_color && pretty_name) {
- free(r);
+ /* This tries to mimic the color magic the old Red Hat sysinit
+ * script did. */
+
+ 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)
- char *r;
- if (read_one_line_file("/etc/SuSE-release", &r) < 0)
- return;
+ 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);
+ }
- truncate_nl(r);
+ if (!ansi_color)
+ const_color = "0;32"; /* Green for openSUSE */
- status_printf("Welcome to \x1B[0;32m%s\x1B[0m!\n", r); /* Green for SUSE */
- free(r);
#elif defined(TARGET_GENTOO)
- char *r;
- if (read_one_line_file("/etc/gentoo-release", &r) < 0)
- return;
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/gentoo-release", &pretty_name)) < 0) {
- truncate_nl(r);
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/gentoo-release: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
- status_printf("Welcome to \x1B[1;34m%s\x1B[0m!\n", r); /* Light Blue for Gentoo */
+ if (!ansi_color)
+ const_color = "1;34"; /* Light Blue for Gentoo */
- free(r);
-#else
-#warning "You probably should add a welcome text logic here."
+#elif defined(TARGET_DEBIAN)
+
+ if (!pretty_name) {
+ if ((r = read_one_line_file("/etc/debian_version", &pretty_name)) < 0) {
+
+ if (r != -ENOENT)
+ log_warning("Failed to read /etc/debian_version: %s", strerror(-r));
+ } else
+ truncate_nl(pretty_name);
+ }
+
+ 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 */
+
+#elif defined(TARGET_ARCH)
+
+ if (!pretty_name)
+ const_pretty = "Arch Linux";
+
+ if (!ansi_color)
+ const_color = "1;36"; /* Cyan for Arch */
#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);
}
char *replace_env(const char *format, char **env) {
return 0;
}
-char *unquote(const char *s, const char quote) {
+char *unquote(const char *s, const char* quotes) {
size_t l;
assert(s);
if ((l = strlen(s)) < 2)
return strdup(s);
- if (s[0] == quote && s[l-1] == quote)
+ if (strchr(quotes, s[0]) && s[l-1] == s[0])
return strndup(s+1, l-2);
return strdup(s);
}
+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) {
+ 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) {
+ return fdopendir(openat(fd, name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC));
+}
+
+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;
+
+ fprintf(f, "%s=%llu %llu\n",
+ name,
+ (unsigned long long) t->realtime,
+ (unsigned long long) t->monotonic);
+}
+
+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", ascii_strlower(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-nav";
+}
+
static const char *const ioprio_class_table[] = {
[IOPRIO_CLASS_NONE] = "none",
[IOPRIO_CLASS_RT] = "realtime",
[SIGPIPE] = "PIPE",
[SIGALRM] = "ALRM",
[SIGTERM] = "TERM",
- [SIGSTKFLT] = "STKFLT",
+#ifdef SIGSTKFLT
+ [SIGSTKFLT] = "STKFLT", /* Linux on SPARC doesn't know SIGSTKFLT */
+#endif
[SIGCHLD] = "CHLD",
[SIGCONT] = "CONT",
[SIGSTOP] = "STOP",