X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Futil.c;h=b8e73ef4bf44a4380129796bcce5c1302c3e46ba;hp=b0a01fde2f00e551b98cb5eaaff289b3cc955601;hb=50483512b901aa216bc12bbe12dc99c1963d79fb;hpb=83cc030fadf71d63d488cf9015275f9e5a02e2cc diff --git a/src/util.c b/src/util.c index b0a01fde2..b8e73ef4b 100644 --- a/src/util.c +++ b/src/util.c @@ -2134,8 +2134,32 @@ finish: int open_terminal(const char *name, int mode) { int fd, r; + unsigned c = 0; - if ((fd = open(name, mode)) < 0) + /* + * If a TTY is in the process of being closed opening it might + * cause EIO. This is horribly awful, but unlikely to be + * changed in the kernel. Hence we work around this problem by + * retrying a couple of times. + * + * https://bugs.launchpad.net/ubuntu/+source/linux/+bug/554172/comments/245 + */ + + for (;;) { + if ((fd = open(name, mode)) >= 0) + break; + + if (errno != EIO) + return -errno; + + if (c >= 20) + return -errno; + + usleep(50 * USEC_PER_MSEC); + c++; + } + + if (fd < 0) return -errno; if ((r = isatty(fd)) < 0) { @@ -2749,28 +2773,121 @@ char* getlogname_malloc(void) { return name; } -int getttyname_malloc(char **r) { - char path[PATH_MAX], *p, *c; +int getttyname_malloc(int fd, char **r) { + char path[PATH_MAX], *c; int k; assert(r); - if ((k = ttyname_r(STDIN_FILENO, path, sizeof(path))) != 0) + if ((k = ttyname_r(fd, path, sizeof(path))) != 0) return -k; char_array_0(path); - p = path; - if (startswith(path, "/dev/")) - p += 5; - - if (!(c = strdup(p))) + if (!(c = strdup(startswith(path, "/dev/") ? path + 5 : path))) return -ENOMEM; *r = c; return 0; } +int getttyname_harder(int fd, char **r) { + int k; + char *s; + + if ((k = getttyname_malloc(fd, &s)) < 0) + return k; + + if (streq(s, "tty")) { + free(s); + return get_ctty(r); + } + + *r = s; + return 0; +} + +int get_ctty_devnr(dev_t *d) { + int k; + char line[256], *p; + unsigned long ttynr; + FILE *f; + + if (!(f = fopen("/proc/self/stat", "r"))) + return -errno; + + if (!(fgets(line, sizeof(line), f))) { + k = -errno; + fclose(f); + return k; + } + + fclose(f); + + if (!(p = strrchr(line, ')'))) + return -EIO; + + p++; + + if (sscanf(p, " " + "%*c " /* state */ + "%*d " /* ppid */ + "%*d " /* pgrp */ + "%*d " /* session */ + "%lu ", /* ttynr */ + &ttynr) != 1) + return -EIO; + + *d = (dev_t) ttynr; + return 0; +} + +int get_ctty(char **r) { + int k; + char fn[128], *s, *b, *p; + dev_t devnr; + + assert(r); + + if ((k = get_ctty_devnr(&devnr)) < 0) + return k; + + snprintf(fn, sizeof(fn), "/dev/char/%u:%u", major(devnr), minor(devnr)); + char_array_0(fn); + + if ((k = readlink_malloc(fn, &s)) < 0) { + + if (k != -ENOENT) + return k; + + /* Probably something like the ptys which have no + * symlink in /dev/char. Let's return something + * vaguely useful. */ + + if (!(b = strdup(fn + 5))) + return -ENOMEM; + + *r = b; + return 0; + } + + if (startswith(s, "/dev/")) + p = s + 5; + else if (startswith(s, "../")) + p = s + 3; + else + p = s; + + b = strdup(p); + free(s); + + if (!b) + return -ENOMEM; + + *r = b; + return 0; +} + static int rm_rf_children(int fd, bool only_dirs) { DIR *d; int ret = 0; @@ -3076,7 +3193,7 @@ void status_welcome(void) { if (!ansi_color && !const_color) const_color = "1"; - status_printf("Welcome to \x1B[%sm%s\x1B[0m!\n", + status_printf("\nWelcome to \x1B[%sm%s\x1B[0m!\n\n", const_color ? const_color : ansi_color, const_pretty ? const_pretty : pretty_name);