src/signals.c: reformat termination blurb
[vlock.git] / modules / new.c
blobe9b15fb1192409bf949f1879f8b7939cebaa2764
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
10 * the author.
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <fcntl.h>
19 #include <sys/ioctl.h>
20 #include <sys/wait.h>
21 #include <sys/types.h>
22 #include <errno.h>
24 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
25 #include <sys/consio.h>
26 #else
27 #include <sys/vt.h>
28 #endif
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"
38 #else
39 #define CONSOLE "/dev/tty0"
40 #endif
41 /* template for the device of a given virtual console */
42 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
43 #define VTNAME "/dev/ttyv%x"
44 #else
45 #define VTNAME "/dev/tty%d"
46 #endif
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)
54 int n;
56 if (ioctl(consfd, VT_GETACTIVE, &n) == 0)
57 return n;
58 else
59 return -1;
61 #else
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;
69 else
70 return -1;
72 #endif
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];
79 ssize_t namelen;
81 if (n <= 0)
82 return NULL;
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);
87 #else
88 namelen = snprintf(name, sizeof name, VTNAME, n);
89 #endif
91 if (namelen > (ssize_t) sizeof name) {
92 fprintf(stderr, "vlock-new: virtual terminal number too large\n");
93 return NULL;
94 } else if (namelen < 0) {
95 fprintf(stderr, "vlock-new: error calculating terminal device name: %s\n", strerror(errno));
96 return NULL;
97 } else {
98 return name;
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 {
112 int consfd;
113 int old_vtno;
114 int new_vtno;
115 int saved_stdin;
116 int saved_stdout;
117 int saved_stderr;
120 /* Run switch to a new console and redirect stdio there. */
121 bool vlock_start(void **ctx_ptr)
123 struct new_console_context *ctx;
124 int vtfd;
125 char *vtname;
127 /* Allocate the context. */
128 if ((ctx = malloc(sizeof *ctx)) == NULL)
129 return false;
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");
146 goto err;
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");
154 goto err;
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");
161 goto err;
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");
170 goto err;
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)
176 sleep(1);
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");
181 goto err;
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. */
195 (void) close(vtfd);
197 *ctx_ptr = ctx;
198 return true;
200 err:
201 errno = 0;
202 free(ctx);
203 return false;
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;
211 if (ctx == NULL)
212 return true;
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");
227 #endif
229 (void) close(ctx->consfd);
230 free(ctx);
232 return true;