cdev: implement VT_WAITACTIVE
The VT API provides a VT_WAITACTIVE ioctl which waits until the VT got activated. We use the same technique as with blocking reads() to implement this without threads. Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
parent
00297c43cc
commit
294312185f
@ -98,6 +98,8 @@ struct cdev_client {
|
||||
struct vt_mode vtmode;
|
||||
struct fuse_ctx user;
|
||||
bool pending_switch;
|
||||
|
||||
struct shl_dlist waiters;
|
||||
};
|
||||
|
||||
struct cdev_reader {
|
||||
@ -107,6 +109,12 @@ struct cdev_reader {
|
||||
size_t len;
|
||||
};
|
||||
|
||||
struct cdev_waiter {
|
||||
struct shl_dlist list;
|
||||
bool killed;
|
||||
fuse_req_t req;
|
||||
};
|
||||
|
||||
static pthread_mutex_t cdev_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static struct shl_array *cdev_ids = NULL;
|
||||
|
||||
@ -161,8 +169,6 @@ err_unlock:
|
||||
* devices, we actually try to kill the fake-VT so we can get access again.
|
||||
*/
|
||||
|
||||
static void reader_free(struct cdev_reader *reader, int error);
|
||||
|
||||
static void reader_interrupt(fuse_req_t req, void *data)
|
||||
{
|
||||
struct cdev_reader *reader = data;
|
||||
@ -220,6 +226,62 @@ static int reader_release(struct cdev_reader *reader, const char *buf,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void waiter_interrupt(fuse_req_t req, void *data)
|
||||
{
|
||||
struct cdev_waiter *waiter = data;
|
||||
|
||||
if (!waiter)
|
||||
return;
|
||||
|
||||
waiter->killed = true;
|
||||
}
|
||||
|
||||
static int waiter_new(struct cdev_waiter **out, struct cdev_client *client,
|
||||
fuse_req_t req)
|
||||
{
|
||||
struct cdev_waiter *waiter;
|
||||
|
||||
if (fuse_req_interrupted(req))
|
||||
return -ENOENT;
|
||||
|
||||
waiter = malloc(sizeof(*waiter));
|
||||
if (!waiter)
|
||||
return -ENOMEM;
|
||||
memset(waiter, 0, sizeof(*waiter));
|
||||
waiter->req = req;
|
||||
fuse_req_interrupt_func(req, waiter_interrupt, waiter);
|
||||
if (waiter->killed) {
|
||||
fuse_req_interrupt_func(req, NULL, NULL);
|
||||
free(waiter);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
shl_dlist_link_tail(&client->waiters, &waiter->list);
|
||||
*out = waiter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void waiter_free(struct cdev_waiter *waiter, int error)
|
||||
{
|
||||
shl_dlist_unlink(&waiter->list);
|
||||
if (waiter->req) {
|
||||
fuse_req_interrupt_func(waiter->req, NULL, NULL);
|
||||
fuse_reply_err(waiter->req, -error);
|
||||
}
|
||||
free(waiter);
|
||||
}
|
||||
|
||||
static int waiter_release(struct cdev_waiter *waiter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
fuse_req_interrupt_func(waiter->req, NULL, NULL);
|
||||
ret = fuse_reply_ioctl(waiter->req, 0, NULL, 0);
|
||||
waiter->req = NULL;
|
||||
waiter_free(waiter, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void client_vte_event(struct tsm_vte *vte, const char *u8, size_t len,
|
||||
void *data)
|
||||
{
|
||||
@ -294,6 +356,7 @@ static void client_input_event(struct uterm_input *input,
|
||||
static void client_free(struct cdev_client *client)
|
||||
{
|
||||
struct cdev_reader *reader;
|
||||
struct cdev_waiter *waiter;
|
||||
|
||||
log_debug("free fake TTY client %p", client);
|
||||
|
||||
@ -307,6 +370,12 @@ static void client_free(struct cdev_client *client)
|
||||
reader_free(reader, -EPIPE);
|
||||
}
|
||||
|
||||
while (!shl_dlist_empty(&client->waiters)) {
|
||||
waiter = shl_dlist_entry(client->waiters.next,
|
||||
struct cdev_waiter, list);
|
||||
waiter_free(waiter, -EPIPE);
|
||||
}
|
||||
|
||||
uterm_input_unregister_cb(client->cdev->input, client_input_event,
|
||||
client);
|
||||
tsm_vte_unref(client->vte);
|
||||
@ -318,6 +387,7 @@ static void client_free(struct cdev_client *client)
|
||||
static int client_activate(struct cdev_client *client)
|
||||
{
|
||||
int ret;
|
||||
struct cdev_waiter *waiter;
|
||||
|
||||
/* TODO: Document that CAP_KILL is needed for this to work properly */
|
||||
if (client->vtmode.mode == VT_PROCESS && client->vtmode.acqsig) {
|
||||
@ -327,6 +397,15 @@ static int client_activate(struct cdev_client *client)
|
||||
client->user.pid, client, errno);
|
||||
}
|
||||
|
||||
while (!shl_dlist_empty(&client->waiters)) {
|
||||
waiter = shl_dlist_entry(client->waiters.next,
|
||||
struct cdev_waiter, list);
|
||||
if (waiter->killed)
|
||||
waiter_free(waiter, 0);
|
||||
else
|
||||
waiter_release(waiter);
|
||||
}
|
||||
|
||||
client->active = true;
|
||||
return 0;
|
||||
}
|
||||
@ -382,6 +461,7 @@ static int client_new(struct cdev_client **out, struct kmscon_cdev *cdev)
|
||||
client->kbmode = K_UNICODE;
|
||||
client->vtmode.mode = VT_AUTO;
|
||||
shl_dlist_init(&client->readers);
|
||||
shl_dlist_init(&client->waiters);
|
||||
|
||||
log_debug("new fake TTY client %p", client);
|
||||
|
||||
@ -459,12 +539,19 @@ static void client_cleanup(struct cdev_client *client)
|
||||
{
|
||||
struct shl_dlist *i, *tmp;
|
||||
struct cdev_reader *reader;
|
||||
struct cdev_waiter *waiter;
|
||||
|
||||
shl_dlist_for_each_safe(i, tmp, &client->readers) {
|
||||
reader = shl_dlist_entry(i, struct cdev_reader, list);
|
||||
if (reader->killed)
|
||||
reader_free(reader, -ENOENT);
|
||||
}
|
||||
|
||||
shl_dlist_for_each_safe(i, tmp, &client->waiters) {
|
||||
waiter = shl_dlist_entry(i, struct cdev_waiter, list);
|
||||
if (waiter->killed)
|
||||
waiter_free(waiter, -ENOENT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -643,7 +730,19 @@ static void ioctl_VT_ACTIVATE(struct cdev_client *client, fuse_req_t req,
|
||||
static void ioctl_VT_WAITACTIVE(struct cdev_client *client, fuse_req_t req,
|
||||
int val)
|
||||
{
|
||||
fuse_reply_err(req, EOPNOTSUPP);
|
||||
int ret;
|
||||
struct cdev_waiter *waiter;
|
||||
|
||||
if (client->active) {
|
||||
fuse_reply_ioctl(req, 0, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ret = waiter_new(&waiter, client, req);
|
||||
if (ret) {
|
||||
fuse_reply_err(req, -ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ioctl_VT_GETSTATE(struct cdev_client *client, fuse_req_t req)
|
||||
|
Loading…
x
Reference in New Issue
Block a user