Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/vt/vt_screen_reader.c
- This file was added.
/*- | ||||||||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | ||||||||||
* | ||||||||||
* Copyright (c) 2022 Hans Petter Selasky | ||||||||||
* | ||||||||||
* Redistribution and use in source and binary forms, with or without | ||||||||||
* modification, are permitted provided that the following conditions | ||||||||||
* are met: | ||||||||||
* 1. Redistributions of source code must retain the above copyright | ||||||||||
* notice, this list of conditions and the following disclaimer. | ||||||||||
* 2. Redistributions in binary form must reproduce the above copyright | ||||||||||
* notice, this list of conditions and the following disclaimer in the | ||||||||||
* documentation and/or other materials provided with the distribution. | ||||||||||
* | ||||||||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | ||||||||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||||||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||||||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | ||||||||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||||||||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||||||||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||||||||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||||||||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||||||||||
* SUCH DAMAGE. | ||||||||||
*/ | ||||||||||
#include <sys/param.h> | ||||||||||
#include <sys/systm.h> | ||||||||||
#include <sys/kernel.h> | ||||||||||
#include <sys/lock.h> | ||||||||||
#include <sys/proc.h> | ||||||||||
#include <sys/sx.h> | ||||||||||
#include <sys/malloc.h> | ||||||||||
#include <sys/mutex.h> | ||||||||||
#include <sys/reboot.h> | ||||||||||
#include <dev/vt/vt.h> | ||||||||||
#define VT_ACCESS_UTF8_DATA_MAX 1000 /* bytes */ | ||||||||||
static MALLOC_DEFINE(M_VTACCESS, "vt_screen_reader", "VT screen reader"); | ||||||||||
static SYSCTL_NODE(_kern_vt, OID_AUTO, screen_reader, | ||||||||||
CTLFLAG_RW | CTLFLAG_MPSAFE, 0, "Screen reader parameters"); | ||||||||||
static int vt_screen_reader_text_utf8(SYSCTL_HANDLER_ARGS); | ||||||||||
static int vt_screen_reader_feed(SYSCTL_HANDLER_ARGS); | ||||||||||
static term_pos_t vt_screen_reader_cursor; | ||||||||||
static char *vt_screen_reader_utf8_data; | ||||||||||
static size_t vt_screen_reader_utf8_length; | ||||||||||
static struct sx vt_screen_reader_sx; | ||||||||||
static void vt_screen_reader_hup_callback(void *, int); | ||||||||||
static struct task vt_screen_reader_hup_task = | ||||||||||
TASK_INITIALIZER(0, &vt_screen_reader_hup_callback, NULL); | ||||||||||
SX_SYSINIT(vt_screen_reader_sx, &vt_screen_reader_sx, "VT screen reader"); | ||||||||||
#define VT_ACCESS_LOCK() sx_xlock(&vt_screen_reader_sx) | ||||||||||
#define VT_ACCESS_UNLOCK() sx_xunlock(&vt_screen_reader_sx) | ||||||||||
static unsigned vt_screen_reader_lines = 1; /* default value */ | ||||||||||
static unsigned vt_screen_reader_hup_pid; /* send HUP signal to this PID, if non-zero */ | ||||||||||
SYSCTL_UINT(_kern_vt_screen_reader, OID_AUTO, lines, CTLFLAG_RWTUN | CTLFLAG_MPSAFE, | ||||||||||
markj: There is no need to set CTLFLAG_MPSAFE on UINT/U16 sysctls. | ||||||||||
&vt_screen_reader_lines, 0, "Number of lines the VT screen reader module will feed, between 1 and 1000"); | ||||||||||
SYSCTL_UINT(_kern_vt_screen_reader, OID_AUTO, hangup_pid, CTLFLAG_RW | CTLFLAG_MPSAFE, | ||||||||||
&vt_screen_reader_hup_pid, 0, "Send hangup signal to this PID, if non-zero"); | ||||||||||
SYSCTL_U16(_kern_vt_screen_reader, OID_AUTO, row, CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||||||||
&vt_screen_reader_cursor.tp_row, 0, "Current VT screen reader row number, between 0 and 65535 inclusivly"); | ||||||||||
markjUnsubmitted Not Done Inline Actions
markj: | ||||||||||
SYSCTL_U16(_kern_vt_screen_reader, OID_AUTO, col, CTLFLAG_RD | CTLFLAG_MPSAFE, | ||||||||||
&vt_screen_reader_cursor.tp_col, 0, "Current VT screen reader column number, between 0 and 65535 inclusivly"); | ||||||||||
markjUnsubmitted Not Done Inline Actions
markj: | ||||||||||
SYSCTL_PROC(_kern_vt_screen_reader, OID_AUTO, text_utf8, CTLTYPE_STRING | CTLFLAG_RD | | ||||||||||
CTLFLAG_MPSAFE, NULL, 0, &vt_screen_reader_text_utf8, "A", "Current VT screen reader buffer in UTF-8 format"); | ||||||||||
SYSCTL_PROC(_kern_vt_screen_reader, OID_AUTO, feed, | ||||||||||
CTLTYPE_UINT | CTLFLAG_RWTUN | CTLFLAG_MPSAFE, NULL, | ||||||||||
0, &vt_screen_reader_feed, "IU", "Set to non-zero to feed a new VT screen reader buffer"); | ||||||||||
markjUnsubmitted Not Done Inline ActionsLines here are too long. markj: Lines here are too long. | ||||||||||
static int | ||||||||||
vt_screen_reader_text_utf8(SYSCTL_HANDLER_ARGS) | ||||||||||
{ | ||||||||||
int error; | ||||||||||
VT_ACCESS_LOCK(); | ||||||||||
if (vt_screen_reader_utf8_data != NULL) { | ||||||||||
error = SYSCTL_OUT(req, vt_screen_reader_utf8_data, | ||||||||||
vt_screen_reader_utf8_length); | ||||||||||
} else { | ||||||||||
error = 0; | ||||||||||
} | ||||||||||
VT_ACCESS_UNLOCK(); | ||||||||||
return (error); | ||||||||||
} | ||||||||||
static int | ||||||||||
vt_screen_reader_feed(SYSCTL_HANDLER_ARGS) | ||||||||||
{ | ||||||||||
unsigned max_ln; | ||||||||||
unsigned val; | ||||||||||
int error; | ||||||||||
VT_ACCESS_LOCK(); | ||||||||||
val = 0; | ||||||||||
error = SYSCTL_OUT(req, &val, sizeof(val)); | ||||||||||
if (error || !req->newptr) | ||||||||||
goto done; | ||||||||||
error = SYSCTL_IN(req, &val, sizeof(val)); | ||||||||||
if (error || val == 0) | ||||||||||
goto done; | ||||||||||
if (vt_screen_reader_utf8_data == NULL) { | ||||||||||
vt_screen_reader_utf8_data = | ||||||||||
malloc(VT_ACCESS_UTF8_DATA_MAX, M_VTACCESS, | ||||||||||
M_WAITOK | M_ZERO); | ||||||||||
} else { | ||||||||||
/* Re-use the UTF-8 buffer */ | ||||||||||
} | ||||||||||
max_ln = vt_screen_reader_lines; | ||||||||||
if (max_ln < 1) | ||||||||||
max_ln = 1; | ||||||||||
else if (max_ln > 1000) | ||||||||||
max_ln = 1000; | ||||||||||
vt_screen_reader_utf8_length = 0; | ||||||||||
/* Update the screen reader buffer: */ | ||||||||||
if (vtbuf_extract_screen_reader(vt_screen_reader_utf8_data, | ||||||||||
&vt_screen_reader_utf8_length, &vt_screen_reader_cursor, max_ln, | ||||||||||
VT_ACCESS_UTF8_DATA_MAX) != 0) | ||||||||||
vt_screen_reader_utf8_length = 0; | ||||||||||
done: | ||||||||||
VT_ACCESS_UNLOCK(); | ||||||||||
return (error); | ||||||||||
} | ||||||||||
static void | ||||||||||
vt_screen_reader_hup_callback(void *arg, int pending) | ||||||||||
{ | ||||||||||
struct proc *p; | ||||||||||
pid_t pid; | ||||||||||
pid = atomic_swap_int(&vt_screen_reader_hup_pid, 0); | ||||||||||
markjUnsubmitted Not Done Inline ActionsIsn't this dangerous? What if the screen reader crashes and its PID is reused for something else? Or does something protect against that? markj: Isn't this dangerous? What if the screen reader crashes and its PID is reused for something… | ||||||||||
markjUnsubmitted Not Done Inline ActionsLooking at vtspeakd, I think a better mechanism would be to use a device file interface rather than sysctls, and have kernel state attached to a device file descriptor. Then that state can be released reliably when vtspeakd exits, even if it crashes. markj: Looking at vtspeakd, I think a better mechanism would be to use a device file interface rather… | ||||||||||
hselaskyAuthorUnsubmitted Done Inline ActionsYes, I can do that. Then it will be a header file with some IOCTLs inside. Do you have any thoughts about the name or location of such a header file? What about the name of the /dev/xxx node? --HPS hselasky: Yes, I can do that.
Then it will be a header file with some IOCTLs inside.
Do you have any… | ||||||||||
markjUnsubmitted Not Done Inline ActionsCan it be an ioctl on ttyvN, handled by vtterm_ioctl()? There are already some vt(4)-specific ioctls. markj: Can it be an ioctl on ttyvN, handled by vtterm_ioctl()? There are already some vt(4)-specific… | ||||||||||
if (pid <= 0) | ||||||||||
return; | ||||||||||
p = pfind(pid); | ||||||||||
if (p == NULL) | ||||||||||
return; | ||||||||||
kern_psignal(p, SIGHUP); | ||||||||||
PROC_UNLOCK(p); | ||||||||||
} | ||||||||||
void | ||||||||||
vt_screen_reader_hup(void) | ||||||||||
{ | ||||||||||
taskqueue_enqueue(taskqueue_thread, &vt_screen_reader_hup_task); | ||||||||||
} |
There is no need to set CTLFLAG_MPSAFE on UINT/U16 sysctls.