diff options
author | Kenny Levinsen <kl@kl.wtf> | 2020-08-28 15:25:04 +0000 |
---|---|---|
committer | Kenny Levinsen <kl@kl.wtf> | 2020-08-28 17:46:13 +0200 |
commit | dbdce8a54beec1385149a77d1005ef85dbfc2ac5 (patch) | |
tree | d22c9c8586be999c2fbe36f1b99516a6322e8a11 | |
parent | 60a8e809b23fab0d77fb21c6336ef7f37c45640c (diff) |
terminal: Construct TTY paths correctly on FreeBSD
FreeBSD TTY paths are contructed in the kernel using the %r formatter,
which in this case ends up being a base 32 encoding.
The base 32 implementation is taken more or less directly from wlroots
commit fc6c0ca12e94.
-rw-r--r-- | common/terminal.c | 83 |
1 files changed, 79 insertions, 4 deletions
diff --git a/common/terminal.c b/common/terminal.c index 77e9235..aa9797e 100644 --- a/common/terminal.c +++ b/common/terminal.c @@ -1,3 +1,4 @@ +#include <assert.h> #include <errno.h> #include <fcntl.h> #include <signal.h> @@ -10,7 +11,6 @@ #if defined(__linux__) #include <linux/kd.h> #include <linux/vt.h> -#define TTYF "/dev/tty%d" #define K_ENABLE K_UNICODE #define K_DISABLE K_OFF #define FRSIG 0 @@ -18,7 +18,6 @@ #include <sys/consio.h> #include <sys/kbio.h> #include <termios.h> -#define TTYF "/dev/ttyv%d" #define K_ENABLE K_XLATE #define K_DISABLE K_RAW #define FRSIG SIGIO @@ -29,11 +28,87 @@ #include "log.h" #include "terminal.h" -#define TTYPATHLEN 64 +#define TTYPATHLEN 16 + +#if defined(__FreeBSD__) +static int get_tty_path(int tty, char path[static TTYPATHLEN]) { + assert(tty >= 0); + + const char prefix[] = "/dev/ttyv"; + const size_t prefix_len = sizeof(prefix) - 1; + strcpy(path, prefix); + + // The FreeBSD tty name is constructed in the kernel as follows: + // + // static void + // vtterm_cnprobe(struct terminal *tm, struct consdev *cp) + // { + // ... + // struct vt_window *vw = tm->tm_softc; + // ... + // sprintf(cp->cn_name, "ttyv%r", VT_UNIT(vw)); + // ... + // } + // + // With %r being a FreeBSD-internal radix formatter (seemingly set to + // base 32), and VT_UNIT expanding to the following to extract the + // internal VT number (which is one less than the external VT number): + // + // ((vw)->vw_device->vd_unit * VT_MAXWINDOWS + (vw)->vw_number) + // + // As the %r formatter is kernel-internal, we implement the base 32 + // encoding ourselves below. + + size_t offset = prefix_len; + if (tty == 0) { + path[offset++] = '0'; + path[offset++] = '\0'; + return 0; + } + + const int base = 32; + for (int remaining = tty; remaining > 0; remaining /= base, offset++) { + // Return early if the buffer is too small. + if (offset + 1 >= TTYPATHLEN) { + errno = ENOMEM; + return -1; + } + + const int value = remaining % base; + if (value >= 10) { + path[offset] = 'a' + value - 10; + } else { + path[offset] = '0' + value; + } + } + + const size_t num_len = offset - prefix_len; + for (size_t i = 0; i < num_len / 2; i++) { + const size_t p1 = prefix_len + i; + const size_t p2 = offset - 1 - i; + const char tmp = path[p1]; + path[p1] = path[p2]; + path[p2] = tmp; + } + + path[offset++] = '\0'; + return 0; +} +#elif defined(__linux__) +static int get_tty_path(int tty, char path[static TTYPATHLEN]) { + assert(tty >= 0); + if (snprintf(path, TTYPATHLEN, "/dev/tty%d", tty) == -1) { + return -1; + } + return 0; +} +#else +#error Unsupported platform +#endif int terminal_open(int vt) { char path[TTYPATHLEN]; - if (snprintf(path, TTYPATHLEN, TTYF, vt) == -1) { + if (get_tty_path(vt, path) == -1) { log_errorf("could not generate tty path: %s", strerror(errno)); return -1; } |