src/script.c: fix bug that prevented script dependencies from being read
[vlock.git] / modules / new.c
blob392d3e08a7fe9fa88cdb0ddd6b330cbf1e36b2be
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 <unistd.h>
17 #include <fcntl.h>
18 #include <sys/ioctl.h>
19 #include <sys/wait.h>
20 #include <sys/types.h>
22 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
23 #include <sys/consio.h>
24 #else
25 #include <sys/vt.h>
26 #endif
28 #include "vlock_plugin.h"
30 const char *preceeds[] = { "all", NULL };
31 const char *requires[] = { "all", NULL };
33 /* name of the virtual console device */
34 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
35 #define CONSOLE "/dev/ttyv0"
36 #else
37 #define CONSOLE "/dev/tty0"
38 #endif
39 /* template for the device of a given virtual console */
40 #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
41 #define VTNAME "/dev/ttyv%x"
42 #else
43 #define VTNAME "/dev/tty%d"
44 #endif
46 /* Get the currently active console from the given
47 * console file descriptor. Returns console number
48 * (starting from 1) on success, -1 on error. */
49 #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
50 static int get_active_console(int consfd)
52 int n;
54 if (ioctl(consfd, VT_GETACTIVE, &n) == 0)
55 return n;
56 else
57 return -1;
59 #else
60 static int get_active_console(int consfd)
62 struct vt_stat vtstate;
64 /* get the virtual console status */
65 if (ioctl(consfd, VT_GETSTATE, &vtstate) == 0)
66 return vtstate.v_active;
67 else
68 return -1;
70 #endif
72 /* Get the device name for the given console number.
73 * Returns the device name or NULL on error. */
74 static char *get_console_name(int n)
76 static char name[sizeof VTNAME + 2];
77 size_t namelen;
79 if (n <= 0)
80 return NULL;
82 /* format the virtual terminal filename from the number */
83 #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__)
84 namelen = snprintf(name, sizeof name, VTNAME, n - 1);
85 #else
86 namelen = snprintf(name, sizeof name, VTNAME, n);
87 #endif
89 if (namelen > sizeof name) {
90 fprintf(stderr, "vlock-new: virtual terminal number too large\n");
91 return NULL;
92 } else {
93 return name;
97 /* Change to the given console number using the given console
98 * file descriptor. */
99 static int activate_console(int consfd, int vtno)
101 int c = ioctl(consfd, VT_ACTIVATE, vtno);
103 return c < 0 ? c : ioctl(consfd, VT_WAITACTIVE, vtno);
106 struct new_console_context {
107 int consfd;
108 int old_vtno;
109 int new_vtno;
110 int saved_stdin;
111 int saved_stdout;
112 int saved_stderr;
115 /* Run switch to a new console and redirect stdio there. */
116 bool vlock_start(void **ctx_ptr)
118 struct new_console_context *ctx;
119 int vtfd;
120 char *vtname;
122 /* Allocate the context. */
123 if ((ctx = malloc(sizeof *ctx)) == NULL)
124 return false;
126 /* Try stdin first. */
127 ctx->consfd = dup(STDIN_FILENO);
129 /* Get the number of the currently active console. */
130 ctx->old_vtno = get_active_console(ctx->consfd);
132 if (ctx->old_vtno < 0) {
133 /* stdin is does not a virtual console. */
134 (void) close(ctx->consfd);
136 /* XXX: add optional PAM check here */
138 /* Open the virtual console directly. */
139 if ((ctx->consfd = open(CONSOLE, O_RDWR)) < 0) {
140 perror("vlock-new: cannot open virtual console");
141 goto err;
144 /* Get the number of the currently active console, again. */
145 ctx->old_vtno = get_active_console(ctx->consfd);
147 if (ctx->old_vtno < 0) {
148 perror("vlock-new: could not get the currently active console");
149 goto err;
153 /* Get a free virtual terminal number. */
154 if (ioctl(ctx->consfd, VT_OPENQRY, &ctx->new_vtno) < 0) {
155 perror("vlock-new: could not find a free virtual terminal");
156 goto err;
159 /* Get the device name for the new virtual console. */
160 vtname = get_console_name(ctx->new_vtno);
162 /* Open the free virtual terminal. */
163 if ((vtfd = open(vtname, O_RDWR)) < 0) {
164 perror("vlock-new: cannot open new console");
165 goto err;
168 /* Work around stupid X11 bug: When switching immediately after the command
169 * is entered, the enter button may get stuck. */
170 if (getenv("DISPLAY") != NULL)
171 sleep(1);
173 /* Switch to the new virtual terminal. */
174 if (activate_console(ctx->consfd, ctx->new_vtno) < 0) {
175 perror("vlock-new: could not activate new terminal");
176 goto err;
179 /* Save the stdio file descriptors. */
180 ctx->saved_stdin = dup(STDIN_FILENO);
181 ctx->saved_stdout = dup(STDOUT_FILENO);
182 ctx->saved_stderr = dup(STDERR_FILENO);
184 /* Redirect stdio to virtual terminal. */
185 (void) dup2(vtfd, STDIN_FILENO);
186 (void) dup2(vtfd, STDOUT_FILENO);
187 (void) dup2(vtfd, STDERR_FILENO);
189 /* Close virtual terminal file descriptor. */
190 (void) close(vtfd);
192 *ctx_ptr = ctx;
193 return true;
195 err:
196 free(ctx);
197 return false;
200 /* Redirect stdio back und switch to the previous console. */
201 bool vlock_end(void **ctx_ptr)
203 struct new_console_context *ctx = *ctx_ptr;
205 if (ctx == NULL)
206 return true;
208 /* Restore saved stdio file descriptors. */
209 (void) dup2(ctx->saved_stdin, STDIN_FILENO);
210 (void) dup2(ctx->saved_stdout, STDOUT_FILENO);
211 (void) dup2(ctx->saved_stderr, STDERR_FILENO);
213 /* Switch back to previous virtual terminal. */
214 if (activate_console(ctx->consfd, ctx->old_vtno) < 0)
215 perror("vlock-new: could not activate previous console");
217 #if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
218 /* Deallocate virtual terminal. */
219 if (ioctl(ctx->consfd, VT_DISALLOCATE, ctx->new_vtno) < 0)
220 perror("vlock-new: could not disallocate console");
221 #endif
223 (void) close(ctx->consfd);
224 free(ctx);
226 return true;