move signal functions into own file
[vlock.git] / src / vlock-main.c
blob183f9dc80cd849d83dfc5b39b6a0f0a7aea6c153
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 <unistd.h>
22 #include <sys/types.h>
23 #include <errno.h>
24 #include <time.h>
26 #include <glib.h>
27 #include <glib/gprintf.h>
29 #include "prompt.h"
30 #include "auth.h"
31 #include "console_switch.h"
32 #include "signals.h"
33 #include "terminal.h"
34 #include "util.h"
35 #include "logging.h"
37 #ifdef USE_PLUGINS
38 #include "plugins.h"
39 #endif
41 static GList *atexit_functions;
43 void invoke_atexit_functions(void)
45 while (atexit_functions != NULL) {
46 (*(void (**)())&atexit_functions->data)();
47 atexit_functions = g_list_delete_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 int auth_tries;
63 static void auth_loop(const char *username)
65 GError *err = NULL;
66 struct timespec *prompt_timeout;
67 struct timespec *wait_timeout;
68 char *vlock_message;
69 const char *auth_names[] = { username, "root", NULL };
71 /* If NO_ROOT_PASS is defined or the username is "root" ... */
72 #ifndef NO_ROOT_PASS
73 if (strcmp(username, "root") == 0)
74 #endif
75 /* ... do not fall back to "root". */
76 auth_names[1] = NULL;
78 /* Get the vlock message from the environment. */
79 vlock_message = getenv("VLOCK_MESSAGE");
81 if (vlock_message == NULL) {
82 if (console_switch_locked)
83 vlock_message = getenv("VLOCK_ALL_MESSAGE");
84 else
85 vlock_message = getenv("VLOCK_CURRENT_MESSAGE");
88 /* Get the timeouts from the environment. */
89 prompt_timeout = parse_seconds(getenv("VLOCK_PROMPT_TIMEOUT"));
90 #ifdef USE_PLUGINS
91 wait_timeout = parse_seconds(getenv("VLOCK_TIMEOUT"));
92 #else
93 wait_timeout = NULL;
94 #endif
96 for (;;) {
97 char c;
99 /* Print vlock message if there is one. */
100 if (vlock_message && *vlock_message) {
101 fputs(vlock_message, stderr);
102 fputc('\n', stderr);
105 /* Wait for enter or escape to be pressed. */
106 c = wait_for_character("\n\033", wait_timeout);
108 /* Escape was pressed or the timeout occurred. */
109 if (c == '\033' || c == 0) {
110 #ifdef USE_PLUGINS
111 plugin_hook("vlock_save");
112 /* Wait for any key to be pressed. */
113 c = wait_for_character(NULL, NULL);
114 plugin_hook("vlock_save_abort");
116 /* Do not require enter to be pressed twice. */
117 if (c != '\n')
118 continue;
119 #else
120 continue;
121 #endif
124 for (size_t i = 0; auth_names[i] != NULL; i++) {
125 if (auth(auth_names[i], prompt_timeout, &err))
126 goto auth_success;
128 g_assert(err != NULL);
130 if (g_error_matches(err,
131 VLOCK_PROMPT_ERROR,
132 VLOCK_PROMPT_ERROR_TIMEOUT)) {
133 fprintf(stderr, "Timeout!\n");
134 } else {
135 fprintf(stderr, "vlock: %s\n", err->message);
137 if (g_error_matches(err,
138 VLOCK_AUTH_ERROR,
139 VLOCK_AUTH_ERROR_FAILED)) {
140 fputc('\n', stderr);
141 fprintf(stderr, "******************************************************************\n");
142 fprintf(stderr, "*** You may not be able to able to unlock your terminal now. ***\n");
143 fprintf(stderr, "*** ***\n");
144 fprintf(stderr, "*** Log into another terminal and kill the vlock-main process. ***\n");
145 fprintf(stderr, "******************************************************************\n");
146 fputc('\n', stderr);
147 sleep(3);
151 g_clear_error(&err);
152 sleep(1);
155 auth_tries++;
158 auth_success:
159 /* Free timeouts memory. */
160 free(wait_timeout);
161 free(prompt_timeout);
164 void display_auth_tries(void)
166 if (auth_tries > 0)
167 fprintf(stderr, "%d failed authentication %s.\n", auth_tries, auth_tries > 1 ? "tries" : "try");
170 #ifdef USE_PLUGINS
171 static void call_end_hook(void)
173 (void) plugin_hook("vlock_end");
175 #endif
177 __attribute__((noreturn))
178 static void display_error_and_exit(GError *error)
180 g_fprintf(stderr, "vlock: %s\n", error->message);
181 exit(1);
184 __attribute__((noreturn, format(printf, 1, 2)))
185 static void printf_and_exit(const char *format, ...)
187 char *error_message = NULL;
188 va_list ap;
189 va_start(ap, format);
190 g_vasprintf(&error_message, format, ap);
191 va_end(ap);
192 g_fprintf(stderr, "vlock: %s\n", error_message);
193 exit(1);
196 /* Lock the current terminal until proper authentication is received. */
197 int main(int argc, char *const argv[])
199 const char *username = NULL;
201 /* Initialize GLib. */
202 g_set_prgname(argv[0]);
204 /* Initialize logging. */
205 vlock_initialize_logging();
207 install_signal_handlers();
209 /* Get the user name from the environment if started as root. */
210 if (getuid() == 0)
211 username = g_getenv("USER");
213 if (username == NULL)
214 username = g_get_user_name();
216 ensure_atexit(display_auth_tries);
218 #ifdef USE_PLUGINS
219 for (int i = 1; i < argc; i++)
220 if (!load_plugin(argv[i]))
221 printf_and_exit("loading plugin '%s' failed: %s", argv[i], STRERROR);
223 ensure_atexit(unload_plugins);
225 if (!resolve_dependencies()) {
226 if (errno == 0)
227 exit(EXIT_FAILURE);
228 else
229 printf_and_exit("error resolving plugin dependencies: %s", STRERROR);
232 plugin_hook("vlock_start");
233 ensure_atexit(call_end_hook);
234 #else /* !USE_PLUGINS */
235 /* Emulate pseudo plugin "all". */
236 if (argc == 2 && (strcmp(argv[1], "all") == 0)) {
237 if (!lock_console_switch()) {
238 if (errno)
239 perror("vlock: could not disable console switching");
241 exit(EXIT_FAILURE);
244 ensure_atexit((void (*)(void))unlock_console_switch);
245 } else if (argc > 1) {
246 printf_and_exit("plugin support disabled");
248 #endif
250 if (!isatty(STDIN_FILENO))
251 printf_and_exit("stdin is not a terminal");
253 /* Delay securing the terminal until here because one of the plugins might
254 * have changed the active terminal. */
255 secure_terminal();
256 ensure_atexit(restore_terminal);
258 auth_loop(username);
260 exit(0);