src/vlock-main.c: handle signals differently
[vlock.git] / src / vlock-main.c
blob6c8eb38cf9f1b592c702a30e30a30d7bb8153c2e
1 /* vlock-main.c -- main 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 #define _GNU_SOURCE
15 #include <stdlib.h>
16 #include <string.h>
17 #include <stdio.h>
19 #include <pwd.h>
21 #include <termios.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <signal.h>
25 #include <errno.h>
26 #include <time.h>
28 #include <glib.h>
30 #include "prompt.h"
31 #include "auth.h"
32 #include "console_switch.h"
33 #include "util.h"
35 #ifdef USE_PLUGINS
36 #include "plugins.h"
37 #endif
39 int vlock_debug = 0;
41 static GList *atexit_functions;
43 static void invoke_atexit_functions(void)
45 while (atexit_functions != NULL) {
46 (*(void (**)())&atexit_functions->data)();
47 atexit_functions = g_list_remove_link(atexit_functions,
48 atexit_functions);
52 static void ensure_atexit(void (*function)(void))
54 if (atexit_functions == NULL)
55 atexit(invoke_atexit_functions);
57 atexit_functions = g_list_prepend(atexit_functions,
58 *(void **)&function);
61 static char *get_username(void)
63 uid_t uid = getuid();
64 char *username = NULL;
66 /* Get the user name from the environment if started as root. */
67 if (uid == 0)
68 username = getenv("USER");
70 if (username == NULL) {
71 struct passwd *pw;
73 /* Get the password entry. */
74 pw = getpwuid(uid);
76 if (pw == NULL)
77 return NULL;
79 username = pw->pw_name;
82 return strdup(username);
85 static void terminate(int signum)
87 invoke_atexit_functions();
88 fprintf(stderr, "vlock: Killed by signal %d (%s)!\n", signum, strsignal(signum));
89 raise(signum);
92 static void install_signal_handlers(void)
94 struct sigaction sa;
96 /* Ignore some signals. */
97 (void) sigemptyset(&(sa.sa_mask));
98 sa.sa_flags = SA_RESTART;
99 sa.sa_handler = SIG_IGN;
100 (void) sigaction(SIGTSTP, &sa, NULL);
102 /* Handle termination signals. None of these should be delivered in a normal
103 * run of the program because terminal signals (INT, QUIT) are disabled
104 * below. */
105 sa.sa_flags = SA_RESETHAND;
106 sa.sa_handler = terminate;
107 (void) sigaction(SIGINT, &sa, NULL);
108 (void) sigaction(SIGQUIT, &sa, NULL);
109 (void) sigaction(SIGTERM, &sa, NULL);
110 (void) sigaction(SIGHUP, &sa, NULL);
111 (void) sigaction(SIGABRT, &sa, NULL);
112 (void) sigaction(SIGSEGV, &sa, NULL);
115 static struct termios term;
116 static tcflag_t lflag;
118 static void secure_terminal(void)
120 /* Disable terminal echoing and signals. */
121 (void) tcgetattr(STDIN_FILENO, &term);
122 lflag = term.c_lflag;
123 term.c_lflag &= ~(ECHO | ISIG);
124 (void) tcsetattr(STDIN_FILENO, TCSANOW, &term);
127 static void restore_terminal(void)
129 /* Restore the terminal. */
130 term.c_lflag = lflag;
131 (void) tcsetattr(STDIN_FILENO, TCSANOW, &term);
134 static int auth_tries;
136 static void auth_loop(const char *username)
138 GError *err = NULL;
139 struct timespec *prompt_timeout;
140 struct timespec *wait_timeout;
141 char *vlock_message;
142 const char *auth_names[] = { username, "root", NULL };
144 /* If NO_ROOT_PASS is defined or the username is "root" ... */
145 #ifndef NO_ROOT_PASS
146 if (strcmp(username, "root") == 0)
147 #endif
148 /* ... do not fall back to "root". */
149 auth_names[1] = NULL;
151 /* Get the vlock message from the environment. */
152 vlock_message = getenv("VLOCK_MESSAGE");
154 if (vlock_message == NULL) {
155 if (console_switch_locked)
156 vlock_message = getenv("VLOCK_ALL_MESSAGE");
157 else
158 vlock_message = getenv("VLOCK_CURRENT_MESSAGE");
161 /* Get the timeouts from the environment. */
162 prompt_timeout = parse_seconds(getenv("VLOCK_PROMPT_TIMEOUT"));
163 #ifdef USE_PLUGINS
164 wait_timeout = parse_seconds(getenv("VLOCK_TIMEOUT"));
165 #else
166 wait_timeout = NULL;
167 #endif
169 for (;;) {
170 char c;
172 /* Print vlock message if there is one. */
173 if (vlock_message && *vlock_message) {
174 fputs(vlock_message, stderr);
175 fputc('\n', stderr);
178 /* Wait for enter or escape to be pressed. */
179 c = wait_for_character("\n\033", wait_timeout);
181 /* Escape was pressed or the timeout occurred. */
182 if (c == '\033' || c == 0) {
183 #ifdef USE_PLUGINS
184 plugin_hook("vlock_save");
185 /* Wait for any key to be pressed. */
186 c = wait_for_character(NULL, NULL);
187 plugin_hook("vlock_save_abort");
189 /* Do not require enter to be pressed twice. */
190 if (c != '\n')
191 continue;
192 #else
193 continue;
194 #endif
197 for (size_t i = 0; auth_names[i] != NULL; i++) {
198 if (auth(auth_names[i], prompt_timeout, &err))
199 goto auth_success;
201 g_assert(err != NULL);
203 if (g_error_matches(err,
204 VLOCK_PROMPT_ERROR,
205 VLOCK_PROMPT_ERROR_TIMEOUT)) {
206 fprintf(stderr, "Timeout!\n");
207 } else {
208 fprintf(stderr, "vlock: %s\n", err->message);
210 if (g_error_matches(err,
211 VLOCK_AUTH_ERROR,
212 VLOCK_AUTH_ERROR_FAILED)) {
213 fputc('\n', stderr);
214 fprintf(stderr, "******************************************************************\n");
215 fprintf(stderr, "*** You may not be able to able to unlock your terminal now. ***\n");
216 fprintf(stderr, "*** ***\n");
217 fprintf(stderr, "*** Log into another terminal and kill the vlock-main process. ***\n");
218 fprintf(stderr, "******************************************************************\n");
219 fputc('\n', stderr);
220 sleep(3);
224 g_clear_error(&err);
225 sleep(1);
228 auth_tries++;
231 auth_success:
232 /* Free timeouts memory. */
233 free(wait_timeout);
234 free(prompt_timeout);
237 void display_auth_tries(void)
239 if (auth_tries > 0)
240 fprintf(stderr, "%d failed authentication %s.\n", auth_tries, auth_tries > 1 ? "tries" : "try");
243 #ifdef USE_PLUGINS
244 static void call_end_hook(void)
246 (void) plugin_hook("vlock_end");
248 #endif
250 /* Lock the current terminal until proper authentication is received. */
251 int main(int argc, char *const argv[])
253 char *username;
255 vlock_debug = (getenv("VLOCK_DEBUG") != NULL);
257 install_signal_handlers();
259 username = get_username();
261 if (username == NULL)
262 fatal_perror("vlock: could not get username");
264 ensure_atexit(display_auth_tries);
266 #ifdef USE_PLUGINS
267 for (int i = 1; i < argc; i++)
268 if (!load_plugin(argv[i]))
269 fatal_error("vlock: loading plugin '%s' failed: %s", argv[i], STRERROR);
271 ensure_atexit(unload_plugins);
273 if (!resolve_dependencies()) {
274 if (errno == 0)
275 exit(EXIT_FAILURE);
276 else
277 fatal_error("vlock: error resolving plugin dependencies: %s", STRERROR);
280 plugin_hook("vlock_start");
281 ensure_atexit(call_end_hook);
282 #else /* !USE_PLUGINS */
283 /* Emulate pseudo plugin "all". */
284 if (argc == 2 && (strcmp(argv[1], "all") == 0)) {
285 if (!lock_console_switch()) {
286 if (errno)
287 perror("vlock: could not disable console switching");
289 exit(EXIT_FAILURE);
292 ensure_atexit((void (*)(void))unlock_console_switch);
293 } else if (argc > 1) {
294 fatal_error("vlock: plugin support disabled");
296 #endif
298 if (!isatty(STDIN_FILENO))
299 fatal_error("vlock: stdin is not a terminal");
301 secure_terminal();
302 ensure_atexit(restore_terminal);
304 auth_loop(username);
306 free(username);
308 exit(0);