diff --git a/kitty/data-types.c b/kitty/data-types.c index 41662d3f2..303b63201 100644 --- a/kitty/data-types.c +++ b/kitty/data-types.c @@ -226,9 +226,7 @@ extern bool init_mouse(PyObject *module); extern bool init_kittens(PyObject *module); extern bool init_logging(PyObject *module); extern bool init_png_reader(PyObject *module); -#ifdef __unix__ extern bool init_utmp(PyObject *module); -#endif #ifdef __APPLE__ extern int init_CoreText(PyObject *); extern bool init_cocoa(PyObject *module); @@ -296,9 +294,7 @@ PyInit_fast_data_types(void) { if (!init_freetype_render_ui_text(m)) return NULL; #endif if (!init_fonts(m)) return NULL; -#if defined(__unix__) if (!init_utmp(m)) return NULL; -#endif CellAttrs a; #define s(name, attr) { a.val = 0; a.attr = 1; PyModule_AddIntConstant(m, #name, shift_to_first_set_bit(a)); } diff --git a/kitty/debug_config.py b/kitty/debug_config.py index 0a14d1ef3..cb5e73ba4 100644 --- a/kitty/debug_config.py +++ b/kitty/debug_config.py @@ -3,10 +3,16 @@ # License: GPLv3 Copyright: 2021, Kovid Goyal import os +import re +import socket import sys +import termios +import time from functools import partial from pprint import pformat -from typing import Callable, Dict, Generator, Iterable, Set, Tuple +from typing import ( + IO, Callable, Dict, Generator, Iterable, Optional, Set, Tuple +) from kittens.tui.operations import colored, styled @@ -15,6 +21,7 @@ from .conf.utils import KeyAction from .constants import ( extensions_dir, is_macos, is_wayland, kitty_base_dir, kitty_exe, shell_path ) +from .fast_data_types import num_users from .options.types import Options as KittyOpts, defaults from .options.utils import MouseMap from .rgb import Color, color_as_sharp @@ -152,84 +159,58 @@ def compare_opts(opts: KittyOpts, print: Callable) -> None: print('\n\t'.join(sorted(colors))) -is_linux = sys.platform in ('linux', 'linux2') +class IssueData: - -if is_linux: - import socket - import time - import re - import termios - from .fast_data_types import num_users - from typing import IO - - - class IssueData: - uname: os.uname_result - hostname: str - formatted_date: str - formatted_time: str - tty_name: str - baud_rate: int - num_users: int - def __init__(self): - self.uname = os.uname() - self.hostname = socket.gethostname() - _time = time.localtime() - self.formatted_time = time.strftime('%a %b %d %Y', _time) - self.formatted_date = time.strftime('%H:%M:%S', _time) - try: - self.tty_name = format_tty_name(os.ttyname(sys.stdin.fileno())) - except OSError: - self.tty_name = '(none)' - self.baud_rate = termios.tcgetattr(sys.stdin.fileno())[5] - self.num_users = num_users() - - - # https://kernel.googlesource.com/pub/scm/utils/util-linux/util-linux/+/v2.7.1/login-utils/agetty.c#790 - issue_mappings = { - # ctx = IssueData - 's': lambda ctx: ctx.uname.sysname, - 'n': lambda ctx: ctx.uname.nodename, - 'r': lambda ctx: ctx.uname.release, - 'v': lambda ctx: ctx.uname.version, - 'm': lambda ctx: ctx.uname.machine, - 'o': lambda ctx: ctx.hostname, - 'd': lambda ctx: ctx.formatted_date, - 't': lambda ctx: ctx.formatted_time, - 'l': lambda ctx: ctx.tty_name, - 'b': lambda ctx: ctx.baud_rate, - 'u': lambda ctx: ctx.num_users, - 'U': lambda ctx: str(ctx.num_users) + ' user' + ('' if ctx.num_users == 1 else 's'), - } - - - def translate_issue_char(ctx: IssueData, char: str) -> str: - assert len(char) == 1 + def __init__(self) -> None: + self.uname = os.uname() + self.s, self.n, self.r, self.v, self.m = self.uname + self.hostname = self.o = socket.gethostname() + _time = time.localtime() + self.formatted_time = self.d = time.strftime('%a %b %d %Y', _time) + self.formatted_date = self.t = time.strftime('%H:%M:%S', _time) try: - return issue_mappings[char](ctx) - except KeyError: - return char + self.tty_name = format_tty_name(os.ttyname(sys.stdin.fileno())) + except OSError: + self.tty_name = '(none)' + self.l = self.tty_name # noqa + self.baud_rate = termios.tcgetattr(sys.stdin.fileno())[5] + self.b = str(self.baud_rate) + try: + self.num_users = num_users() + except RuntimeError: + self.num_users = -1 + self.u = str(self.num_users) + self.U = self.u + ' user' + ('' if self.num_users == 1 else 's') - def format_tty_name(raw: str) -> str: - return re.sub(r'^/dev/([^/]+)/([^/]+)$', r'\1\2', raw) +def translate_issue_char(ctx: IssueData, char: str) -> str: + try: + return str(getattr(ctx, char)) if len(char) == 1 else char + except AttributeError: + return char - def print_issue(issue_file: IO[str], print_fn) -> None: - last_char = None - issue_data = IssueData() - while this_char := issue_file.read(1): - if last_char == '\\': - print_fn(translate_issue_char(issue_data, this_char), end='') - elif last_char is not None: - print_fn(last_char, end='') - # `\\\a` should not match the last two slashes, - # so make it look like it was `\?\a` where `?` - # is some character other than `\`. - last_char = None if last_char == '\\' else this_char - if last_char is not None: +def format_tty_name(raw: str) -> str: + return re.sub(r'^/dev/([^/]+)/([^/]+)$', r'\1\2', raw) + + +def print_issue(issue_file: IO[str], print_fn: Callable) -> None: + last_char: Optional[str] = None + issue_data = IssueData() + while True: + this_char = issue_file.read(1) + if not this_char: + break + if last_char == '\\': + print_fn(translate_issue_char(issue_data, this_char), end='') + elif last_char is not None: print_fn(last_char, end='') + # `\\\a` should not match the last two slashes, + # so make it look like it was `\?\a` where `?` + # is some character other than `\`. + last_char = None if last_char == '\\' else this_char + if last_char is not None: + print_fn(last_char, end='') def debug_config(opts: KittyOpts) -> str: @@ -241,7 +222,7 @@ def debug_config(opts: KittyOpts) -> str: if is_macos: import subprocess p(' '.join(subprocess.check_output(['sw_vers']).decode('utf-8').splitlines()).strip()) - if is_linux and os.path.exists('/etc/issue'): + if os.path.exists('/etc/issue'): with open('/etc/issue', encoding='utf-8', errors='replace') as f: print_issue(f, p) if os.path.exists('/etc/lsb-release'): diff --git a/kitty/utmp.c b/kitty/utmp.c index e084c9b94..a08f7ef60 100644 --- a/kitty/utmp.c +++ b/kitty/utmp.c @@ -1,50 +1,35 @@ -#if defined(__unix__) -#define PY_SSIZE_T_CLEAN -#include -#include - -#include +#include "data-types.h" +#ifdef __unix__ +#include static PyObject* -num_users(PyObject *const self, PyObject *const args) { - (void)self; (void)args; +num_users(PyObject *const self UNUSED, PyObject *const args UNUSED) { size_t users = 0; + struct utmpx *ut; Py_BEGIN_ALLOW_THREADS -#ifdef UTENT_REENTRANT - struct utmp *result = NULL; - struct utmp buffer = { 0, }; - while (true) { - if (getutent_r(&buffer, &result) == -1) { - Py_BLOCK_THREADS - return PyErr_SetFromErrno(PyExc_OSError); - } - if (result == NULL) { break; } - if (result->ut_type == USER_PROCESS) { users++; } + setutxent(); + while ((ut = getutxent())) { + if (ut->ut_type == USER_PROCESS) users++; } -#else - struct utmp *ut; - setutent(); - while ((ut = getutent())) { - if (ut->ut_type == USER_PROCESS) { - users++; - } - } - endutent(); -#endif + endutxent(); Py_END_ALLOW_THREADS return PyLong_FromSize_t(users); } +#else +static PyObject* +num_users(PyObject *const self UNUSED, PyObject *const args UNUSED) { + PyErr_SetString(PyExc_RuntimeError, "Counting the number of users is not supported"); + return NULL; +} +#endif -static PyMethodDef UtmpMethods[] = { +static PyMethodDef methods[] = { {"num_users", num_users, METH_NOARGS, "Get the number of users using UTMP data" }, { NULL, NULL, 0, NULL }, }; bool init_utmp(PyObject *module) { - // 0 = success - return PyModule_AddFunctions(module, UtmpMethods) == 0; + return PyModule_AddFunctions(module, methods) == 0; } - -#endif