src/vlock-new.c: don't rely on /dev/tty, use stdin instead
[vlock.git] / src / vlock-new.c
blob1ea4495d5651440a685b6d64e947400823214571
1 /* vlock-new.c -- console allocation routine 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 #ifdef __FreeBSD__
23 #include <sys/consio.h>
24 #else
25 #include <sys/vt.h>
26 #endif /* __FreeBSD__ */
28 #include "vlock.h"
30 /* Get the currently active console from the given
31 * console file descriptor. Returns console number
32 * (starting from 1) on success, -1 on error. */
33 #ifdef __FreeBSD__
34 static int get_active_console(int consfd) {
35 int n;
37 if (ioctl(consfd, VT_GETACTIVE, &n) == 0)
38 return n;
39 else
40 return -1;
42 #else
43 static int get_active_console(int consfd) {
44 struct vt_stat vtstate;
46 /* get the virtual console status */
47 if (ioctl(consfd, VT_GETSTATE, &vtstate) == 0)
48 return vtstate.v_active;
49 else
50 return -1;
52 #endif
54 /* Get the device name for the given console number.
55 * Returns the device name or NULL on error. */
56 static char *get_console_name(int n) {
57 static char name[sizeof VTNAME + 2];
58 size_t namelen;
60 if (n <= 0)
61 return NULL;
63 /* format the virtual terminal filename from the number */
64 #ifdef __FreeBSD__
65 namelen = snprintf(name, sizeof name, VTNAME, n-1);
66 #else
67 namelen = snprintf(name, sizeof name, VTNAME, n);
68 #endif
70 if (namelen > sizeof name) {
71 fprintf(stderr, "vlock-new: virtual terminal number too large\n");
72 return NULL;
74 else {
75 return name;
79 /* Run vlock-all on a new console. */
80 int main(void) {
81 int consfd = STDIN_FILENO;
82 int old_vtno;
83 int vtno;
84 int vtfd;
85 char *vtname;
86 int pid = -1;
87 int status;
89 /* get the number of the currently active console */
90 old_vtno = get_active_console(consfd);
92 if (old_vtno < 0) {
93 /* stdin is does not a virtual console */
94 (void) close(consfd);
96 /* XXX: add optional PAM check here */
98 /* open the virtual console directly */
99 if ((consfd = open(CONSOLE, O_RDWR)) < 0) {
100 perror("vlock-new: cannot open virtual console");
101 exit (111);
104 /* get the number of the currently active console, again */
105 old_vtno = get_active_console(consfd);
107 if (old_vtno < 0) {
108 perror("vlock-new: could not find out currently active console");
109 exit (111);
113 /* get a free virtual terminal number */
114 if (ioctl(consfd, VT_OPENQRY, &vtno) < 0) {
115 perror("vlock-new: could not find a free virtual terminal");
116 exit (111);
119 /* get the device name for the new virtual console */
120 vtname = get_console_name(vtno);
122 /* open the free virtual terminal */
123 if ((vtfd = open(vtname, O_RDWR)) < 0) {
124 perror("vlock-new: cannot open new console");
125 exit (111);
128 /* switch to the virtual terminal */
129 if (ioctl(consfd, VT_ACTIVATE, vtno) < 0
130 || ioctl(consfd, VT_WAITACTIVE, vtno) < 0) {
131 perror("vlock-new: could not activate new terminal");
132 exit (111);
135 pid = fork();
137 if (pid == 0) {
138 /* child */
140 /* close the virtual console file descriptor */
141 (void) close(consfd);
143 /* drop privleges */
144 (void) setuid(getuid());
146 /* make this process a session leader */
147 (void) setsid();
149 /* make new virtual terminal controlling tty of this process */
150 if (ioctl(vtfd, TIOCSCTTY, 1) < 0) {
151 perror("vlock-new: could not set controlling terminal");
152 _exit(111);
155 /* redirect stdio */
156 dup2(vtfd, STDIN_FILENO);
157 dup2(vtfd, STDOUT_FILENO);
158 dup2(vtfd, STDERR_FILENO);
159 close(vtfd);
161 /* run child */
162 execl(VLOCK_ALL, VLOCK_ALL, (char *) NULL);
163 perror("vlock-new: exec of vlock-all failed");
164 _exit(127);
165 } else if (pid < 0) {
166 perror("vlock-new: could not create child process");
169 /* close virtual terminal file descriptor */
170 (void) close(vtfd);
172 if (pid > 0 && waitpid(pid, &status, 0) < 0) {
173 perror("vlock-new: child process missing");
174 pid = -1;
177 /* switch back to former virtual terminal */
178 if (ioctl(consfd, VT_ACTIVATE, old_vtno) < 0
179 || ioctl(consfd, VT_WAITACTIVE, old_vtno) < 0)
180 perror("vlock-new: could not activate previous console");
182 #ifndef __FreeBSD__
183 /* deallocate virtual terminal */
184 if (ioctl(consfd, VT_DISALLOCATE, vtno) < 0)
185 perror("vlock-new: could not disallocate console");
186 #endif
188 (void) close(consfd);
190 /* exit with the exit status of the child or 128+signal if it was killed */
191 if (pid > 0) {
192 if (WIFEXITED(status))
193 exit (WEXITSTATUS(status));
194 else if (WIFSIGNALED(status))
195 exit (128+WTERMSIG(status));
198 return 0;