assert(fn);
assert(line);
- if (!(f = fopen(fn, "re")))
+ f = fopen(fn, "re");
+ if (!f)
return -errno;
- if (!(fgets(t, sizeof(t), f))) {
- r = feof(f) ? -EIO : -errno;
- goto finish;
+ if (!fgets(t, sizeof(t), f)) {
+
+ if (ferror(f)) {
+ r = -errno;
+ goto finish;
+ }
+
+ t[0] = 0;
}
- if (!(c = strdup(t))) {
+ c = strdup(t);
+ if (!c) {
r = -ENOMEM;
goto finish;
}
char ***rl) {
FILE *f;
- char **m = 0;
+ char **m = NULL;
int r;
assert(fname);
return 0;
}
+int is_kernel_thread(pid_t pid) {
+ char *p;
+ size_t count;
+ char c;
+ bool eof;
+ FILE *f;
+
+ if (pid == 0)
+ return 0;
+
+ if (asprintf(&p, "/proc/%lu/cmdline", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ count = fread(&c, 1, 1, f);
+ eof = feof(f);
+ fclose(f);
+
+ /* Kernel threads have an empty cmdline */
+
+ if (count <= 0)
+ return eof ? 1 : -errno;
+
+ return 0;
+}
+
int get_process_exe(pid_t pid, char **name) {
int r;
return r;
}
+int get_process_uid(pid_t pid, uid_t *uid) {
+ char *p;
+ FILE *f;
+ int r;
+
+ assert(uid);
+
+ if (pid == 0)
+ return getuid();
+
+ if (asprintf(&p, "/proc/%lu/status", (unsigned long) pid) < 0)
+ return -ENOMEM;
+
+ f = fopen(p, "re");
+ free(p);
+
+ if (!f)
+ return -errno;
+
+ while (!feof(f)) {
+ char line[LINE_MAX], *l;
+
+ if (!fgets(line, sizeof(line), f)) {
+ if (feof(f))
+ break;
+
+ r = -errno;
+ goto finish;
+ }
+
+ l = strstrip(line);
+
+ if (startswith(l, "Uid:")) {
+ l += 4;
+ l += strspn(l, WHITESPACE);
+
+ l[strcspn(l, WHITESPACE)] = 0;
+
+ r = parse_uid(l, uid);
+ goto finish;
+ }
+ }
+
+ r = -EIO;
+
+finish:
+ fclose(f);
+
+ return r;
+}
+
char *strnappend(const char *s, const char *suffix, size_t b) {
size_t a;
char *r;
return r;
}
-int read_one_char(FILE *f, char *ret, bool *need_nl) {
+int read_one_char(FILE *f, char *ret, usec_t t, bool *need_nl) {
struct termios old_termios, new_termios;
char c;
char line[LINE_MAX];
if (tcsetattr(fileno(f), TCSADRAIN, &new_termios) >= 0) {
size_t k;
+ if (t != (usec_t) -1) {
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0) {
+ tcsetattr(fileno(f), TCSADRAIN, &old_termios);
+ return -ETIMEDOUT;
+ }
+ }
+
k = fread(&c, 1, 1, f);
tcsetattr(fileno(f), TCSADRAIN, &old_termios);
}
}
- if (!(fgets(line, sizeof(line), f)))
+ if (t != (usec_t) -1)
+ if (fd_wait_for_event(fileno(f), POLLIN, t) <= 0)
+ return -ETIMEDOUT;
+
+ if (!fgets(line, sizeof(line), f))
return -EIO;
truncate_nl(line);
bool need_nl = true;
if (on_tty)
- fputs("\x1B[1m", stdout);
+ fputs(ANSI_HIGHLIGHT_ON, stdout);
va_start(ap, text);
vprintf(text, ap);
va_end(ap);
if (on_tty)
- fputs("\x1B[0m", stdout);
+ fputs(ANSI_HIGHLIGHT_OFF, stdout);
fflush(stdout);
- if ((r = read_one_char(stdin, &c, &need_nl)) < 0) {
+ r = read_one_char(stdin, &c, (usec_t) -1, &need_nl);
+ if (r < 0) {
if (r == -EBADMSG) {
puts("Bad input, please try again.");
}
}
-int reset_terminal_fd(int fd) {
+int reset_terminal_fd(int fd, bool switch_to_text) {
struct termios termios;
int r = 0;
- long arg;
/* Set terminal to some sane defaults */
/* Disable exclusive mode, just in case */
ioctl(fd, TIOCNXCL);
+ /* Switch to text mode */
+ if (switch_to_text)
+ ioctl(fd, KDSETMODE, KD_TEXT);
+
/* Enable console unicode mode */
- arg = K_UNICODE;
- ioctl(fd, KDSKBMODE, &arg);
+ ioctl(fd, KDSKBMODE, K_UNICODE);
if (tcgetattr(fd, &termios) < 0) {
r = -errno;
if (fd < 0)
return fd;
- r = reset_terminal_fd(fd);
+ r = reset_terminal_fd(fd, true);
close_nointr_nofail(fd);
return r;
if (notify >= 0)
close_nointr_nofail(notify);
- if ((r = reset_terminal_fd(fd)) < 0)
+ r = reset_terminal_fd(fd, true);
+ if (r < 0)
log_warning("Failed to reset terminal: %s", strerror(-r));
return fd;
{ "M", 1024ULL*1024ULL },
{ "G", 1024ULL*1024ULL*1024ULL },
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "", 1 },
};
void rename_process(const char name[8]) {
assert(name);
- prctl(PR_SET_NAME, name);
+ /* This is a like a poor man's setproctitle(). It changes the
+ * comm field, argv[0], and also the glibc's internally used
+ * name of the process. For the first one a limit of 16 chars
+ * applies, to the second one usually one of 10 (i.e. length
+ * of "/sbin/init"), to the third one one of 7 (i.e. length of
+ * "systemd"). If you pass a longer string it will be
+ * truncated */
- /* This is a like a poor man's setproctitle(). The string
- * passed should fit in 7 chars (i.e. the length of
- * "systemd") */
+ prctl(PR_SET_NAME, name);
if (program_invocation_name)
strncpy(program_invocation_name, name, strlen(program_invocation_name));
}
if (honour_sticky)
- keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
is_dir = S_ISDIR(st.st_mode);
continue;
}
- keep_around = st.st_uid == 0 && (st.st_mode & S_ISVTX);
+ keep_around =
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
+ (st.st_mode & S_ISVTX);
}
is_dir = de->d_type == DT_DIR;
if (delete_root) {
- if (honour_sticky && file_is_sticky(path) > 0)
+ if (honour_sticky && file_is_priv_sticky(path) > 0)
return r;
if (rmdir(path) < 0 && errno != ENOENT) {
* 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 (mode != (mode_t) -1)
+ if (chmod(path, mode) < 0)
+ return -errno;
- if (chown(path, uid, gid) < 0)
- return -errno;
+ if (uid != (uid_t) -1 || gid != (gid_t) -1)
+ if (chown(path, uid, gid) < 0)
+ return -errno;
return 0;
}
}
}
-void status_vprintf(const char *format, va_list ap) {
- char *s = NULL;
- int fd = -1;
+void status_vprintf(const char *status, bool ellipse, const char *format, va_list ap) {
+ char *s = NULL, *spaces = NULL, *e;
+ int fd = -1, c;
+ size_t emax, sl, left;
+ struct iovec iovec[5];
+ int n = 0;
assert(format);
if (vasprintf(&s, format, ap) < 0)
goto finish;
- if ((fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC)) < 0)
+ fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
+ if (fd < 0)
goto finish;
- write(fd, s, strlen(s));
+ if (ellipse) {
+ c = fd_columns(fd);
+ if (c <= 0)
+ c = 80;
+
+ if (status) {
+ sl = 2 + 6 + 1; /* " [" status "]" */
+ emax = (size_t) c > sl ? c - sl - 1 : 0;
+ } else
+ emax = c - 1;
+
+ e = ellipsize(s, emax, 75);
+ if (e) {
+ free(s);
+ s = e;
+ }
+ }
+
+ zero(iovec);
+ IOVEC_SET_STRING(iovec[n++], s);
+
+ if (ellipse) {
+ sl = strlen(s);
+ left = emax > sl ? emax - sl : 0;
+ if (left > 0) {
+ spaces = malloc(left);
+ if (spaces) {
+ memset(spaces, ' ', left);
+ iovec[n].iov_base = spaces;
+ iovec[n].iov_len = left;
+ n++;
+ }
+ }
+ }
+
+ if (status) {
+ IOVEC_SET_STRING(iovec[n++], " [");
+ IOVEC_SET_STRING(iovec[n++], status);
+ IOVEC_SET_STRING(iovec[n++], "]\n");
+ } else
+ IOVEC_SET_STRING(iovec[n++], "\n");
+
+ writev(fd, iovec, n);
finish:
free(s);
+ free(spaces);
if (fd >= 0)
close_nointr_nofail(fd);
}
-void status_printf(const char *format, ...) {
+void status_printf(const char *status, bool ellipse, const char *format, ...) {
va_list ap;
assert(format);
va_start(ap, format);
- status_vprintf(format, ap);
+ status_vprintf(status, ellipse, format, ap);
va_end(ap);
}
log_warning("Failed to read /etc/os-release: %s", strerror(-r));
}
-#if defined(TARGET_FEDORA)
- if (!pretty_name) {
- if ((r = read_one_line_file("/etc/system-release", &pretty_name)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/system-release: %s", strerror(-r));
- }
- }
-
- if (!ansi_color && pretty_name) {
-
- /* 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)
-
- 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));
- }
- }
-
- 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));
- }
- }
-
- if (!ansi_color)
- const_color = "1;34"; /* Light Blue for Gentoo */
-
-#elif defined(TARGET_ALTLINUX)
-
- if (!pretty_name) {
- if ((r = read_one_line_file("/etc/altlinux-release", &pretty_name)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/altlinux-release: %s", strerror(-r));
- }
- }
-
- if (!ansi_color)
- const_color = "0;36"; /* Cyan for ALTLinux */
-
-
-#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 {
- 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 */
-
-#elif defined(TARGET_MANDRIVA)
-
- if (!pretty_name) {
- char *s, *p;
-
- if ((r = read_one_line_file("/etc/mandriva-release", &s) < 0)) {
- if (r != -ENOENT)
- log_warning("Failed to read /etc/mandriva-release: %s", strerror(-r));
- } else {
- p = strstr(s, " release ");
- if (p) {
- *p = '\0';
- p += 9;
- p[strcspn(p, " ")] = '\0';
-
- /* This corresponds to standard rc.sysinit */
- if (asprintf(&pretty_name, "%s\x1B[0;39m %s", s, p) > 0)
- const_color = "1;36";
- else
- log_warning("Failed to allocate Mandriva version string.");
- } else
- log_warning("Failed to parse /etc/mandriva-release");
- free(s);
- }
- }
-#elif defined(TARGET_MEEGO)
-
- if (!pretty_name) {
- if ((r = read_one_line_file("/etc/meego-release", &pretty_name)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/meego-release: %s", strerror(-r));
- }
- }
-
- if (!ansi_color)
- const_color = "1;35"; /* Bright Magenta for MeeGo */
-#endif
-
if (!pretty_name && !const_pretty)
const_pretty = "Linux";
if (!ansi_color && !const_color)
const_color = "1";
- status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n",
+ status_printf(NULL,
+ false,
+ "\nWelcome to \x1B[%sm%s\x1B[0m!\n",
const_color ? const_color : ansi_color,
const_pretty ? const_pretty : pretty_name);
return r;
}
+int fd_columns(int fd) {
+ struct winsize ws;
+ zero(ws);
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_col <= 0)
+ return -EIO;
+
+ return ws.ws_col;
+}
+
unsigned columns(void) {
static __thread int parsed_columns = 0;
const char *e;
if (_likely_(parsed_columns > 0))
return parsed_columns;
- if ((e = getenv("COLUMNS")))
+ e = getenv("COLUMNS");
+ if (e)
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 = fd_columns(STDOUT_FILENO);
if (parsed_columns <= 0)
parsed_columns = 80;
return parsed_columns;
}
+int fd_lines(int fd) {
+ struct winsize ws;
+ zero(ws);
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) < 0)
+ return -errno;
+
+ if (ws.ws_row <= 0)
+ return -EIO;
+
+ return ws.ws_row;
+}
+
+unsigned lines(void) {
+ static __thread int parsed_lines = 0;
+ const char *e;
+
+ if (_likely_(parsed_lines > 0))
+ return parsed_lines;
+
+ e = getenv("LINES");
+ if (e)
+ parsed_lines = atoi(e);
+
+ if (parsed_lines <= 0)
+ parsed_lines = fd_lines(STDOUT_FILENO);
+
+ if (parsed_lines <= 0)
+ parsed_lines = 25;
+
+ return parsed_lines;
+}
+
int running_in_chroot(void) {
struct stat a, b;
size_t l;
assert(s);
- if ((l = strlen(s)) < 2)
+ l = strlen(s);
+ if (l < 2)
return strdup(s);
if (strchr(quotes, s[0]) && s[l-1] == s[0])
}
-void freeze(void) {
+_noreturn_ void freeze(void) {
/* Make sure nobody waits for us on a socket anymore */
close_all_fds(NULL, 0);
return i;
}
-const char *default_term_for_tty(const char *tty) {
+bool tty_is_vc_resolve(const char *tty) {
char *active = NULL;
- const char *term;
+ bool b;
assert(tty);
if (startswith(tty, "/dev/"))
tty += 5;
- /* Resolve where /dev/console is pointing when determining
- * TERM */
+ /* Resolve where /dev/console is pointing to */
if (streq(tty, "console"))
if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
/* If multiple log outputs are configured the
* last one is what /dev/console points to */
- if ((tty = strrchr(active, ' ')))
+ tty = strrchr(active, ' ');
+ if (tty)
tty++;
else
tty = active;
}
- term = tty_is_vc(tty) ? "TERM=linux" : "TERM=vt100";
+ b = tty_is_vc(tty);
free(active);
- return term;
+ return b;
+}
+
+const char *default_term_for_tty(const char *tty) {
+ assert(tty);
+
+ return tty_is_vc_resolve(tty) ? "TERM=linux" : "TERM=vt100";
}
bool dirent_is_file(const struct dirent *de) {
}
while (!hashmap_isempty(pids)) {
+ pid_t pid = PTR_TO_UINT(hashmap_first_key(pids));
siginfo_t si;
char *path;
zero(si);
- if (waitid(P_ALL, 0, &si, WEXITED) < 0) {
+ if (waitid(P_PID, pid, &si, WEXITED) < 0) {
if (errno == EINTR)
continue;
return pollfd.revents & POLLHUP;
}
-int fd_wait_for_event(int fd, int event) {
+int fd_wait_for_event(int fd, int event, usec_t t) {
struct pollfd pollfd;
int r;
pollfd.fd = fd;
pollfd.events = event;
- r = poll(&pollfd, 1, -1);
+ r = poll(&pollfd, 1, t == (usec_t) -1 ? -1 : (int) (t / USEC_PER_MSEC));
if (r < 0)
return -errno;
return 0;
}
+int rtc_open(int flags) {
+ int fd;
+ DIR *d;
+
+ /* First, we try to make use of the /dev/rtc symlink. If that
+ * doesn't exist, we open the first RTC which has hctosys=1
+ * set. If we don't find any we just take the first RTC that
+ * exists at all. */
+
+ fd = open("/dev/rtc", flags);
+ if (fd >= 0)
+ return fd;
+
+ d = opendir("/sys/class/rtc");
+ if (!d)
+ goto fallback;
+
+ for (;;) {
+ char *p, *v;
+ struct dirent buf, *de;
+ int r;
+
+ r = readdir_r(d, &buf, &de);
+ if (r != 0)
+ goto fallback;
+
+ if (!de)
+ goto fallback;
+
+ if (ignore_file(de->d_name))
+ continue;
+
+ p = join("/sys/class/rtc/", de->d_name, "/hctosys", NULL);
+ if (!p) {
+ closedir(d);
+ return -ENOMEM;
+ }
+
+ r = read_one_line_file(p, &v);
+ free(p);
+
+ if (r < 0)
+ continue;
+
+ r = parse_boolean(v);
+ free(v);
+
+ if (r <= 0)
+ continue;
+
+ p = strappend("/dev/", de->d_name);
+ fd = open(p, flags);
+ free(p);
+
+ if (fd >= 0) {
+ closedir(d);
+ return fd;
+ }
+ }
+
+fallback:
+ if (d)
+ closedir(d);
+
+ fd = open("/dev/rtc0", flags);
+ if (fd < 0)
+ return -errno;
+
+ return fd;
+}
+
int hwclock_get_time(struct tm *tm) {
int fd;
int err = 0;
assert(tm);
- fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ fd = rtc_open(O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
assert(tm);
- fd = open("/dev/rtc0", O_RDONLY|O_CLOEXEC);
+ fd = rtc_open(O_RDONLY|O_CLOEXEC);
if (fd < 0)
return -errno;
return -ENOENT;
}
-int file_is_sticky(const char *p) {
+int file_is_priv_sticky(const char *p) {
struct stat st;
assert(p);
return -errno;
return
- st.st_uid == 0 &&
+ (st.st_uid == 0 || st.st_uid == getuid()) &&
(st.st_mode & S_ISVTX);
}
const char *suffix;
off_t factor;
} table[] = {
+ { "E", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
+ { "P", 1024ULL*1024ULL*1024ULL*1024ULL*1024ULL },
{ "T", 1024ULL*1024ULL*1024ULL*1024ULL },
{ "G", 1024ULL*1024ULL*1024ULL },
{ "M", 1024ULL*1024ULL },
return buf;
}
+
+void* memdup(const void *p, size_t l) {
+ void *r;
+
+ assert(p);
+
+ r = malloc(l);
+ if (!r)
+ return NULL;
+
+ memcpy(r, p, l);
+ return r;
+}
+
+int fd_inc_sndbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, &l);
+ if (r >= 0 &&
+ l == sizeof(value) &&
+ (size_t) value >= n*2)
+ return 0;
+
+ value = (int) n;
+ r = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value));
+ if (r < 0)
+ return -errno;
+
+ return 1;
+}
+
+int fd_inc_rcvbuf(int fd, size_t n) {
+ int r, value;
+ socklen_t l = sizeof(value);
+
+ r = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, &l);
+ if (r >= 0 &&
+ l == sizeof(value) &&
+ (size_t) value >= n*2)
+ return 0;
+
+ value = (int) n;
+ r = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value));
+ if (r < 0)
+ return -errno;
+
+ return 1;
+}