1 /* new.c -- console allocation plugin for vlock,
2 * the VT locking program for linux
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
19 #include <sys/ioctl.h>
21 #include <sys/types.h>
24 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
25 #include <sys/consio.h>
30 #include "vlock_plugin.h"
32 const char *preceeds
[] = { "all", NULL
};
33 const char *requires
[] = { "all", NULL
};
35 /* name of the virtual console device */
36 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
37 #define CONSOLE "/dev/ttyv0"
39 #define CONSOLE "/dev/tty0"
41 /* template for the device of a given virtual console */
42 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
43 #define VTNAME "/dev/ttyv%x"
45 #define VTNAME "/dev/tty%d"
48 /* Get the currently active console from the given
49 * console file descriptor. Returns console number
50 * (starting from 1) on success, -1 on error. */
51 #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
52 static int get_active_console(int consfd
)
56 if (ioctl(consfd
, VT_GETACTIVE
, &n
) == 0)
62 static int get_active_console(int consfd
)
64 struct vt_stat vtstate
;
66 /* get the virtual console status */
67 if (ioctl(consfd
, VT_GETSTATE
, &vtstate
) == 0)
68 return vtstate
.v_active
;
74 /* Get the device name for the given console number.
75 * Returns the device name or NULL on error. */
76 static char *get_console_name(int n
)
78 static char name
[sizeof VTNAME
+ 2];
84 /* format the virtual terminal filename from the number */
85 #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
86 namelen
= snprintf(name
, sizeof name
, VTNAME
, n
- 1);
88 namelen
= snprintf(name
, sizeof name
, VTNAME
, n
);
91 if (namelen
> (ssize_t
) sizeof name
) {
92 fprintf(stderr
, "vlock-new: virtual terminal number too large\n");
94 } else if (namelen
< 0) {
95 fprintf(stderr
, "vlock-new: error calculating terminal device name: %s\n", strerror(errno
));
102 /* Change to the given console number using the given console
103 * file descriptor. */
104 static int activate_console(int consfd
, int vtno
)
106 int c
= ioctl(consfd
, VT_ACTIVATE
, vtno
);
108 return c
< 0 ? c
: ioctl(consfd
, VT_WAITACTIVE
, vtno
);
111 struct new_console_context
{
120 /* Run switch to a new console and redirect stdio there. */
121 bool vlock_start(void **ctx_ptr
)
123 struct new_console_context
*ctx
;
127 /* Allocate the context. */
128 if ((ctx
= malloc(sizeof *ctx
)) == NULL
)
131 /* Try stdin first. */
132 ctx
->consfd
= dup(STDIN_FILENO
);
134 /* Get the number of the currently active console. */
135 ctx
->old_vtno
= get_active_console(ctx
->consfd
);
137 if (ctx
->old_vtno
< 0) {
138 /* stdin is does not a virtual console. */
139 (void) close(ctx
->consfd
);
141 /* XXX: add optional PAM check here */
143 /* Open the virtual console directly. */
144 if ((ctx
->consfd
= open(CONSOLE
, O_RDWR
)) < 0) {
145 perror("vlock-new: cannot open virtual console");
149 /* Get the number of the currently active console, again. */
150 ctx
->old_vtno
= get_active_console(ctx
->consfd
);
152 if (ctx
->old_vtno
< 0) {
153 perror("vlock-new: could not get the currently active console");
158 /* Get a free virtual terminal number. */
159 if (ioctl(ctx
->consfd
, VT_OPENQRY
, &ctx
->new_vtno
) < 0) {
160 perror("vlock-new: could not find a free virtual terminal");
164 /* Get the device name for the new virtual console. */
165 vtname
= get_console_name(ctx
->new_vtno
);
167 /* Open the free virtual terminal. */
168 if ((vtfd
= open(vtname
, O_RDWR
)) < 0) {
169 perror("vlock-new: cannot open new console");
173 /* Work around stupid X11 bug: When switching immediately after the command
174 * is entered, the enter button may get stuck. */
175 if (getenv("DISPLAY") != NULL
)
178 /* Switch to the new virtual terminal. */
179 if (activate_console(ctx
->consfd
, ctx
->new_vtno
) < 0) {
180 perror("vlock-new: could not activate new terminal");
184 /* Save the stdio file descriptors. */
185 ctx
->saved_stdin
= dup(STDIN_FILENO
);
186 ctx
->saved_stdout
= dup(STDOUT_FILENO
);
187 ctx
->saved_stderr
= dup(STDERR_FILENO
);
189 /* Redirect stdio to virtual terminal. */
190 (void) dup2(vtfd
, STDIN_FILENO
);
191 (void) dup2(vtfd
, STDOUT_FILENO
);
192 (void) dup2(vtfd
, STDERR_FILENO
);
194 /* Close virtual terminal file descriptor. */
206 /* Redirect stdio back und switch to the previous console. */
207 bool vlock_end(void **ctx_ptr
)
209 struct new_console_context
*ctx
= *ctx_ptr
;
214 /* Restore saved stdio file descriptors. */
215 (void) dup2(ctx
->saved_stdin
, STDIN_FILENO
);
216 (void) dup2(ctx
->saved_stdout
, STDOUT_FILENO
);
217 (void) dup2(ctx
->saved_stderr
, STDERR_FILENO
);
219 /* Switch back to previous virtual terminal. */
220 if (activate_console(ctx
->consfd
, ctx
->old_vtno
) < 0)
221 perror("vlock-new: could not activate previous console");
223 #if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
224 /* Deallocate virtual terminal. */
225 if (ioctl(ctx
->consfd
, VT_DISALLOCATE
, ctx
->new_vtno
) < 0)
226 perror("vlock-new: could not disallocate console");
229 (void) close(ctx
->consfd
);