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) {
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;
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);