aboutsummaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorKenny Levinsen <kl@kl.wtf>2020-08-28 15:25:04 +0000
committerKenny Levinsen <kl@kl.wtf>2020-08-28 17:46:13 +0200
commitdbdce8a54beec1385149a77d1005ef85dbfc2ac5 (patch)
treed22c9c8586be999c2fbe36f1b99516a6322e8a11 /common
parent60a8e809b23fab0d77fb21c6336ef7f37c45640c (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.
Diffstat (limited to 'common')
-rw-r--r--common/terminal.c83
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;
}