vt: fix race-condition in signal handling

Currently, we set up our VT with SIGUSR1/2 signals and after that the user may
connect the signal handlers to the eloop. However, if we receive a signal in
between, the signal gets lost.

Therefore, this simply merges the kmscon_vt_connect_eloop function into
kmscon_vt_open as there is no obvious reason to split them.

Signed-off-by: David Herrmann <dh.herrmann@googlemail.com>
This commit is contained in:
David Herrmann 2011-12-30 13:47:36 +01:00
parent 068591e395
commit 84248b0ac7
4 changed files with 59 additions and 70 deletions

109
src/vt.c
View File

@ -144,6 +144,7 @@ static void vt_enter(struct kmscon_signal *sig, int signum, void *data)
log_debug("vt: entering VT\n");
ioctl(vt->fd, VT_RELDISP, VT_ACKACQ);
if (ioctl(vt->fd, KDSETMODE, KD_GRAPHICS))
log_warning("vt: cannot set graphics mode on vt\n");
@ -158,7 +159,6 @@ static void vt_leave(struct kmscon_signal *sig, int signum, void *data)
if (!vt || vt->fd < 0)
return;
if (vt->cb && !vt->cb(vt, KMSCON_VT_LEAVE, vt->data)) {
log_debug("vt: leaving VT denied\n");
ioctl(vt->fd, VT_RELDISP, 0);
@ -181,6 +181,50 @@ static void vt_input(struct kmscon_fd *fd, int mask, void *data)
tcflush(vt->fd, TCIFLUSH);
}
static int connect_eloop(struct kmscon_vt *vt, struct kmscon_eloop *eloop)
{
int ret;
if (!vt || !eloop || vt->fd < 0)
return -EINVAL;
ret = kmscon_eloop_new_signal(eloop, &vt->sig1, SIGUSR1, vt_leave, vt);
if (ret)
return ret;
ret = kmscon_eloop_new_signal(eloop, &vt->sig2, SIGUSR2, vt_enter, vt);
if (ret)
goto err_sig1;
ret = kmscon_eloop_new_fd(eloop, &vt->efd, vt->fd, KMSCON_READABLE,
vt_input, vt);
if (ret)
goto err_sig2;
return 0;
err_sig2:
kmscon_eloop_rm_signal(vt->sig2);
vt->sig2 = NULL;
err_sig1:
kmscon_eloop_rm_signal(vt->sig1);
vt->sig1 = NULL;
return ret;
}
static void disconnect_eloop(struct kmscon_vt *vt)
{
if (!vt)
return;
kmscon_eloop_rm_signal(vt->sig1);
kmscon_eloop_rm_signal(vt->sig2);
kmscon_eloop_rm_fd(vt->efd);
vt->sig1 = NULL;
vt->sig2 = NULL;
vt->efd = NULL;
}
static int open_tty(int id, int *tty_fd, int *tty_num)
{
int fd;
@ -222,7 +266,7 @@ static int open_tty(int id, int *tty_fd, int *tty_num)
return 0;
}
int kmscon_vt_open(struct kmscon_vt *vt, int id)
int kmscon_vt_open(struct kmscon_vt *vt, int id, struct kmscon_eloop *eloop)
{
struct termios raw_attribs;
struct vt_mode mode;
@ -237,6 +281,10 @@ int kmscon_vt_open(struct kmscon_vt *vt, int id)
if (ret)
return ret;
ret = connect_eloop(vt, eloop);
if (ret)
goto err_fd;
/*
* Get the number of the VT which is active now, so we have something
* to switch back to in kmscon_vt_switch_leave.
@ -252,7 +300,7 @@ int kmscon_vt_open(struct kmscon_vt *vt, int id)
if (tcgetattr(vt->fd, &vt->saved_attribs) < 0) {
log_err("vt: cannot get terminal attributes\n");
ret = -EFAULT;
goto err_fd;
goto err_eloop;
}
/* Ignore control characters and disable echo */
@ -293,6 +341,8 @@ err_text:
ioctl(vt->fd, KDSETMODE, KD_TEXT);
err_reset:
tcsetattr(vt->fd, TCSANOW, &vt->saved_attribs);
err_eloop:
disconnect_eloop(vt);
err_fd:
close(vt->fd);
vt->fd = -1;
@ -306,63 +356,14 @@ void kmscon_vt_close(struct kmscon_vt *vt)
ioctl(vt->fd, KDSETMODE, KD_TEXT);
tcsetattr(vt->fd, TCSANOW, &vt->saved_attribs);
kmscon_vt_disconnect_eloop(vt);
disconnect_eloop(vt);
close(vt->fd);
vt->fd = -1;
vt->num = -1;
vt->saved_num = -1;
}
int kmscon_vt_connect_eloop(struct kmscon_vt *vt, struct kmscon_eloop *loop)
{
int ret;
if (!vt || !loop || vt->fd < 0)
return -EINVAL;
if (vt->sig1 || vt->sig2)
return -EALREADY;
ret = kmscon_eloop_new_signal(loop, &vt->sig1, SIGUSR1, vt_leave,
vt);
if (ret)
return ret;
ret = kmscon_eloop_new_signal(loop, &vt->sig2, SIGUSR2, vt_enter,
vt);
if (ret)
goto err_sig1;
ret = kmscon_eloop_new_fd(loop, &vt->efd, vt->fd, KMSCON_READABLE,
vt_input, vt);
if (ret)
goto err_sig2;
return 0;
err_sig2:
kmscon_eloop_rm_signal(vt->sig2);
vt->sig2 = NULL;
err_sig1:
kmscon_eloop_rm_signal(vt->sig1);
vt->sig1 = NULL;
return ret;
}
void kmscon_vt_disconnect_eloop(struct kmscon_vt *vt)
{
if (!vt)
return;
kmscon_eloop_rm_signal(vt->sig1);
kmscon_eloop_rm_signal(vt->sig2);
kmscon_eloop_rm_fd(vt->efd);
vt->sig1 = NULL;
vt->sig2 = NULL;
vt->efd = NULL;
}
/* Switch to this VT and make it the active VT. */
int kmscon_vt_enter(struct kmscon_vt *vt)
{

View File

@ -58,12 +58,10 @@ int kmscon_vt_new(struct kmscon_vt **out, kmscon_vt_cb cb, void *data);
void kmscon_vt_ref(struct kmscon_vt *vt);
void kmscon_vt_unref(struct kmscon_vt *vt);
int kmscon_vt_open(struct kmscon_vt *vt, int id);
int kmscon_vt_open(struct kmscon_vt *vt, int id, struct kmscon_eloop *eloop);
void kmscon_vt_close(struct kmscon_vt *vt);
int kmscon_vt_connect_eloop(struct kmscon_vt *vt, struct kmscon_eloop *loop);
void kmscon_vt_disconnect_eloop(struct kmscon_vt *vt);
int kmscon_vt_enter(struct kmscon_vt *vt);
int kmscon_vt_leave(struct kmscon_vt *vt);
#endif /* KMSCON_VT_H */

View File

@ -285,11 +285,7 @@ static int setup_eloop(struct console *con)
if (ret)
goto err_loop;
ret = kmscon_vt_open(con->vt, KMSCON_VT_NEW);
if (ret)
goto err_loop;
ret = kmscon_vt_connect_eloop(con->vt, con->loop);
ret = kmscon_vt_open(con->vt, KMSCON_VT_NEW, con->loop);
if (ret)
goto err_loop;

View File

@ -76,18 +76,12 @@ int main(int argc, char **argv)
goto err_sig;
}
ret = kmscon_vt_open(vt, KMSCON_VT_NEW);
ret = kmscon_vt_open(vt, KMSCON_VT_NEW, loop);
if (ret) {
log_err("Cannot open VT\n");
goto err_vt;
}
ret = kmscon_vt_connect_eloop(vt, loop);
if (ret) {
log_err("Cannot connect VT\n");
goto err_vt;
}
ret = kmscon_vt_enter(vt);
if (ret)
log_warning("Cannot switch to VT\n");