src/vlock-main.c: reraise termination signal so parent may notice
[vlock.git] / src / vlock-main.c
blobe56f53bdfde90afd6c56b0c070a461f80f5b7832
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 #include <stdlib.h>
15 #include <string.h>
16 #include <stdio.h>
18 #include <pwd.h>
20 #include <termios.h>
21 #include <unistd.h>
22 #include <sys/types.h>
23 #include <signal.h>
24 #include <errno.h>
25 #include <time.h>
27 #include <glib.h>
29 #include "prompt.h"
30 #include "auth.h"
31 #include "console_switch.h"
32 #include "util.h"
34 #ifdef USE_PLUGINS
35 #include "plugins.h"
36 #endif
38 int vlock_debug = 0;
40 static GList *atexit_functions;
42 static void invoke_atexit_functions(void)
44 for (GList *item = atexit_functions;
45 item != NULL;
46 item = g_list_next(item)) {
47 (*(void (**)())&item->data)();
51 static void ensure_atexit(void (*function)(void))
53 if (atexit_functions == NULL)
54 atexit(invoke_atexit_functions);
56 atexit_functions = g_list_prepend(atexit_functions,
57 *(void **)&function);
60 static char *get_username(void)
62 uid_t uid = getuid();
63 char *username = NULL;
65 /* Get the user name from the environment if started as root. */
66 if (uid == 0)
67 username = getenv("USER");
69 if (username == NULL) {
70 struct passwd *pw;
72 /* Get the password entry. */
73 pw = getpwuid(uid);
75 if (pw == NULL)
76 return NULL;
78 username = pw->pw_name;
81 return strdup(username);
84 static void terminate(int signum)
86 fprintf(stderr, "vlock: Terminated!\n");
87 invoke_atexit_functions();
88 raise(signum);
91 static void block_signals(void)
93 struct sigaction sa;
95 /* Ignore some signals. */
96 /* These signals shouldn't be delivered anyway, because terminal signals are
97 * disabled below. */
98 (void) sigemptyset(&(sa.sa_mask));
99 sa.sa_flags = SA_RESTART;
100 sa.sa_handler = SIG_IGN;
101 (void) sigaction(SIGINT, &sa, NULL);
102 (void) sigaction(SIGQUIT, &sa, NULL);
103 (void) sigaction(SIGTSTP, &sa, NULL);
105 /* Install special handler for SIGTERM. */
106 sa.sa_flags = SA_RESETHAND;
107 sa.sa_handler = terminate;
108 (void) sigaction(SIGTERM, &sa, NULL);
111 static struct termios term;
112 static tcflag_t lflag;
114 static void secure_terminal(void)
116 /* Disable terminal echoing and signals. */
117 (void) tcgetattr(STDIN_FILENO, &term);
118 lflag = term.c_lflag;
119 term.c_lflag &= ~(ECHO | ISIG);
120 (void) tcsetattr(STDIN_FILENO, TCSANOW, &term);
123 static void restore_terminal(void)
125 /* Restore the terminal. */
126 term.c_lflag = lflag;
127 (void) tcsetattr(STDIN_FILENO, TCSANOW, &term);
130 static int auth_tries;
132 static void auth_loop(const char *username)
134 GError *err = NULL;
135 struct timespec *prompt_timeout;
136 struct timespec *wait_timeout;
137 char *vlock_message;
138 const char *auth_names[] = { username, "root", NULL };
140 /* If NO_ROOT_PASS is defined or the username is "root" ... */
141 #ifndef NO_ROOT_PASS
142 if (strcmp(username, "root") == 0)
143 #endif
144 /* ... do not fall back to "root". */
145 auth_names[1] = NULL;
147 /* Get the vlock message from the environment. */
148 vlock_message = getenv("VLOCK_MESSAGE");
150 if (vlock_message == NULL) {
151 if (console_switch_locked)
152 vlock_message = getenv("VLOCK_ALL_MESSAGE");
153 else
154 vlock_message = getenv("VLOCK_CURRENT_MESSAGE");
157 /* Get the timeouts from the environment. */
158 prompt_timeout = parse_seconds(getenv("VLOCK_PROMPT_TIMEOUT"));
159 #ifdef USE_PLUGINS
160 wait_timeout = parse_seconds(getenv("VLOCK_TIMEOUT"));
161 #else
162 wait_timeout = NULL;
163 #endif
165 for (;;) {
166 char c;
168 /* Print vlock message if there is one. */
169 if (vlock_message && *vlock_message) {
170 fputs(vlock_message, stderr);
171 fputc('\n', stderr);
174 /* Wait for enter or escape to be pressed. */
175 c = wait_for_character("\n\033", wait_timeout);
177 /* Escape was pressed or the timeout occurred. */
178 if (c == '\033' || c == 0) {
179 #ifdef USE_PLUGINS
180 plugin_hook("vlock_save");
181 /* Wait for any key to be pressed. */
182 c = wait_for_character(NULL, NULL);
183 plugin_hook("vlock_save_abort");
185 /* Do not require enter to be pressed twice. */
186 if (c != '\n')
187 continue;
188 #else
189 continue;
190 #endif
193 for (size_t i = 0; auth_names[i] != NULL; i++) {
194 if (auth(auth_names[i], prompt_timeout, &err))
195 goto auth_success;
197 g_assert(err != NULL);
199 if (g_error_matches(err,
200 VLOCK_PROMPT_ERROR,
201 VLOCK_PROMPT_ERROR_TIMEOUT))
202 fprintf(stderr, "Timeout!\n");
203 else
204 fprintf(stderr, "vlock: %s\n", err->message);
206 g_clear_error(&err);
207 sleep(1);
210 auth_tries++;
213 auth_success:
214 /* Free timeouts memory. */
215 free(wait_timeout);
216 free(prompt_timeout);
219 void display_auth_tries(void)
221 if (auth_tries > 0)
222 fprintf(stderr, "%d failed authentication %s.\n", auth_tries, auth_tries > 1 ? "tries" : "try");
225 #ifdef USE_PLUGINS
226 static void call_end_hook(void)
228 (void) plugin_hook("vlock_end");
230 #endif
232 /* Lock the current terminal until proper authentication is received. */
233 int main(int argc, char *const argv[])
235 char *username;
237 vlock_debug = (getenv("VLOCK_DEBUG") != NULL);
239 block_signals();
241 username = get_username();
243 if (username == NULL)
244 fatal_perror("vlock: could not get username");
246 ensure_atexit(display_auth_tries);
248 #ifdef USE_PLUGINS
249 for (int i = 1; i < argc; i++)
250 if (!load_plugin(argv[i]))
251 fatal_error("vlock: loading plugin '%s' failed: %s", argv[i], STRERROR);
253 ensure_atexit(unload_plugins);
255 if (!resolve_dependencies()) {
256 if (errno == 0)
257 exit(EXIT_FAILURE);
258 else
259 fatal_error("vlock: error resolving plugin dependencies: %s", STRERROR);
262 plugin_hook("vlock_start");
263 ensure_atexit(call_end_hook);
264 #else /* !USE_PLUGINS */
265 /* Emulate pseudo plugin "all". */
266 if (argc == 2 && (strcmp(argv[1], "all") == 0)) {
267 if (!lock_console_switch()) {
268 if (errno)
269 perror("vlock: could not disable console switching");
271 exit(EXIT_FAILURE);
274 ensure_atexit((void (*)(void))unlock_console_switch);
275 } else if (argc > 1) {
276 fatal_error("vlock: plugin support disabled");
278 #endif
280 if (!isatty(STDIN_FILENO))
281 fatal_error("vlock: stdin is not a terminal");
283 secure_terminal();
284 ensure_atexit(restore_terminal);
286 auth_loop(username);
288 free(username);
290 exit(0);