1 /* console_switch.c -- console grabbing routines for vlock,
2  *                     the VT locking program for linux
3  *
4  * This program is copyright (C) 2007 Frank Benkstein, and is free
5  * software which is freely distributable under the terms of the
6  * GNU General Public License version 2, included as the file COPYING in this
7  * distribution.  It is NOT public domain software, and any
8  * redistribution not permitted by the GNU General Public License is
9  * expressly forbidden without prior written permission from
10  * the author.
11  *
12  */
13 
14 #if !defined(__FreeBSD__) && !defined(_GNU_SOURCE)
15 #define _GNU_SOURCE
16 #endif
17 
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <sys/ioctl.h>
24 #include <errno.h>
25 #include <signal.h>
26 
27 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
28 #include <sys/consio.h>
29 #else
30 #include <sys/vt.h>
31 #endif
32 
33 #include "console_switch.h"
34 
35 /* Is console switching currently disabled? */
36 bool console_switch_locked = false;
37 
38 /* This handler is called whenever a user tries to
39  * switch away from this virtual console. */
release_vt(int signum)40 static void release_vt(int __attribute__ ((__unused__)) signum)
41 {
42   /* Deny console switch. */
43   (void) ioctl(STDIN_FILENO, VT_RELDISP, 0);
44 }
45 
46 /* This handler is called whenever a user switches to this
47  * virtual console. */
acquire_vt(int signum)48 static void acquire_vt(int __attribute__ ((__unused__)) signum)
49 {
50   /* Acknowledge console switch. */
51   (void) ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
52 }
53 
54 /* Console mode before switching was disabled. */
55 static struct vt_mode vtm;
56 /* Signal actions before console handling was disabled. */
57 static struct sigaction sa_usr1;
58 static struct sigaction sa_usr2;
59 
60 /* Disable virtual console switching in the kernel.  If disabling fails false
61  * is returned and errno is set. */
lock_console_switch(void)62 bool lock_console_switch(void)
63 {
64   /* Console mode when switching is disabled. */
65   struct vt_mode lock_vtm;
66   struct sigaction sa;
67 
68   /* Get the virtual console mode. */
69   if (ioctl(STDIN_FILENO, VT_GETMODE, &vtm) < 0) {
70     if (errno == ENOTTY || errno == EINVAL)
71       fprintf(stderr, "vlock: this terminal is not a virtual console\n");
72     else
73       perror("vlock: could not get virtual console mode");
74 
75     errno = 0;
76     return false;
77   }
78 
79   /* Copy the current virtual console mode. */
80   lock_vtm = vtm;
81 
82   (void) sigemptyset(&(sa.sa_mask));
83   sa.sa_flags = SA_RESTART;
84   sa.sa_handler = release_vt;
85   (void) sigaction(SIGUSR1, &sa, &sa_usr1);
86   sa.sa_handler = acquire_vt;
87   (void) sigaction(SIGUSR2, &sa, &sa_usr2);
88 
89   /* Set terminal switching to be process governed. */
90   lock_vtm.mode = VT_PROCESS;
91   /* Set terminal release signal, i.e. sent when switching away. */
92   lock_vtm.relsig = SIGUSR1;
93   /* Set terminal acquire signal, i.e. sent when switching here. */
94   lock_vtm.acqsig = SIGUSR2;
95   /* Set terminal free signal, not implemented on either FreeBSD or Linux. */
96   /* Linux ignores this but FreeBSD wants a valid signal number here. */
97   lock_vtm.frsig = SIGHUP;
98 
99   /* Set virtual console mode to be process governed thus disabling console
100    * switching through the signal handlers above. */
101   if (ioctl(STDIN_FILENO, VT_SETMODE, &lock_vtm) < 0) {
102     perror("vlock: disabling console switching failed");
103 
104     /* Reset signal handlers. */
105     (void) sigaction(SIGUSR1, &sa_usr1, NULL);
106     (void) sigaction(SIGUSR2, &sa_usr2, NULL);
107     errno = 0;
108     return false;
109   }
110 
111   console_switch_locked = true;
112   return true;
113 }
114 
115 /* Reenable console switching if it was previously disabled. */
unlock_console_switch(void)116 bool unlock_console_switch(void)
117 {
118   if (ioctl(STDIN_FILENO, VT_SETMODE, &vtm) == 0) {
119     /* Reset signal handlers. */
120     (void) sigaction(SIGUSR1, &sa_usr1, NULL);
121     (void) sigaction(SIGUSR2, &sa_usr2, NULL);
122 
123     return true;
124   } else {
125     perror("vlock: reenabling console switch failed");
126     errno = 0;
127     return false;
128   }
129 }
130