summaryrefslogtreecommitdiffstats
path: root/ulock.c
diff options
context:
space:
mode:
Diffstat (limited to 'ulock.c')
-rw-r--r--ulock.c315
1 files changed, 315 insertions, 0 deletions
diff --git a/ulock.c b/ulock.c
index e69de29..aaa1ecd 100644
--- a/ulock.c
+++ b/ulock.c
@@ -0,0 +1,315 @@
+#include <config.h>
+
+#include <sys/types.h>
+
+#include <locale.h>
+#include <libintl.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <security/pam_appl.h>
+
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+
+#include <X11/Xlib.h>
+
+
+#define _(s) gettext(s)
+#define DEBUG(args...) syslog(LOG_NOTICE, args)
+
+
+enum lock_state {
+ LOCKED, UNLOCKING, LOGOUT
+};
+
+
+static enum lock_state state = LOCKED;
+
+static GtkWidget *window, *unlockWindow, *lockLabel, *promptEntry, *messageLabel, *logoutButton, *unlockButton;
+static gboolean enablePromptEntry = TRUE, enableLogoutButton = FALSE, enableUnlockButton = TRUE;
+
+static char *username;
+static char *logoutCommand = NULL;
+static int logoutTime = 0;
+
+
+static gboolean do_grab_devices(gpointer user_data) {
+ DEBUG("do_grab_devices");
+
+ gtk_window_present(GTK_WINDOW(window));
+
+ GdkDeviceManager *deviceManager = gdk_display_get_device_manager(gdk_display_get_default());
+ GList *devices = gdk_device_manager_list_devices(deviceManager, GDK_DEVICE_TYPE_MASTER);
+
+ gboolean error = FALSE;
+
+ for (; devices; devices = g_list_next(devices)) {
+ GdkDevice *device = devices->data;
+
+ if (gdk_device_grab(device, gtk_widget_get_window(window), GDK_OWNERSHIP_APPLICATION, TRUE, GDK_ALL_EVENTS_MASK, NULL, GDK_CURRENT_TIME) != GDK_GRAB_SUCCESS)
+ error = TRUE;
+ }
+
+ g_list_free(devices);
+
+ return error;
+}
+
+static void grab_devices(void) {
+ if (do_grab_devices(NULL))
+ g_timeout_add_seconds(1, do_grab_devices, NULL);
+}
+
+static void update(void) {
+ gboolean locked = (state == LOCKED);
+
+ gtk_widget_set_sensitive(promptEntry, enablePromptEntry && locked);
+ gtk_widget_set_sensitive(logoutButton, enableLogoutButton && locked);
+ gtk_widget_set_sensitive(unlockButton, enableUnlockButton && locked);
+}
+
+static gboolean do_update_logout_time(gpointer user_data) {
+ gboolean ret = FALSE;
+ char *escaped_username = g_markup_escape_text(username, -1);
+ char *message;
+
+ if (!logoutCommand) {
+ message = g_strdup_printf(_("This computer is currently locked by the user <i>%s</i>."), escaped_username);
+ enableLogoutButton = FALSE;
+ }
+ else if (logoutTime > 0) {
+ message = g_strdup_printf(_("This computer is currently locked by the user <i>%s</i>.\nThe user can be logged out in %02u:%02u minutes."), escaped_username, logoutTime/60, logoutTime%60);
+ enableLogoutButton = FALSE;
+ ret = TRUE;
+ logoutTime--;
+ }
+ else {
+ message = g_strdup_printf(_("This computer is currently locked by the user <i>%s</i>.\nThe user can be logged out now."), escaped_username);
+ enableLogoutButton = TRUE;
+ }
+
+ gtk_label_set_markup(GTK_LABEL(lockLabel), message);
+ g_free(message);
+
+ update();
+
+ return ret;
+}
+
+static void update_logout_time() {
+ if (do_update_logout_time(NULL))
+ g_timeout_add_seconds(1, do_update_logout_time, NULL);
+}
+
+static void lock(void) {
+ if (state != LOCKED)
+ return;
+
+ DEBUG("lock");
+
+ update();
+ grab_devices();
+ gtk_widget_grab_focus(promptEntry);
+}
+
+static int ulock_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
+ char *password = appdata_ptr;
+
+ *resp = calloc(num_msg, sizeof(struct pam_response));
+ int i;
+ for (i = 0; i < num_msg; i++) {
+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
+ resp[i]->resp = strdup(password);
+ }
+
+ return 0;
+}
+
+static gboolean try_unlock_return(gpointer message) {
+ DEBUG("try_unlock_return");
+
+ if (message) {
+ state = LOCKED;
+ gtk_entry_set_text(GTK_ENTRY(promptEntry), "");
+ gtk_label_set_label(GTK_LABEL(messageLabel), message);
+ lock();
+ }
+ else {
+ gtk_main_quit();
+ }
+
+ return FALSE;
+}
+
+/* this thread must not access any GTK resources */
+static void* try_unlock_thread(void *arg) {
+ const struct pam_conv conv = {.conv = ulock_conv, .appdata_ptr = arg};
+
+ pam_handle_t *handle = NULL;
+ int ret = pam_start("login", username, &conv, &handle);
+
+ if (!ret)
+ ret = pam_authenticate(handle, 0);
+
+ const char *message = NULL;
+ if (ret)
+ message = pam_strerror(handle, ret);
+
+ pam_end(handle, ret);
+
+ g_idle_add(try_unlock_return, (gpointer)message);
+
+ free(arg);
+ return NULL;
+}
+
+static void try_unlock(void) {
+ if (state != LOCKED)
+ return;
+
+ DEBUG("try_unlock");
+
+ state = UNLOCKING;
+ update();
+ gtk_label_set_label(GTK_LABEL(messageLabel), "");
+
+ pthread_t thread;
+ pthread_create(&thread, NULL, try_unlock_thread, strdup((char*)gtk_entry_get_text(GTK_ENTRY(promptEntry))));
+ pthread_detach(thread);
+}
+
+static void logout(void) {
+ if (state != LOCKED)
+ return;
+
+ if (!logoutCommand)
+ return;
+
+ if (logoutTime > 0)
+ return;
+
+ DEBUG("logout");
+
+ state = LOGOUT;
+ update();
+
+ system(logoutCommand);
+}
+
+static GdkFilterReturn xevent_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) {
+ XEvent *ev = xevent;
+
+ switch (ev->xany.type) {
+ case MapNotify:
+ case ConfigureNotify:
+ if (ev->xany.window != gdk_x11_window_get_xid(gtk_widget_get_window(window)))
+ gtk_window_present(GTK_WINDOW(window));
+ break;
+
+ default:
+ break;
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+
+static void create_lock_window(void) {
+ window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+
+ gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
+ gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), TRUE);
+ gtk_window_set_skip_pager_hint(GTK_WINDOW(window), TRUE);
+ gtk_window_set_keep_above(GTK_WINDOW(window), TRUE);
+ gtk_window_fullscreen(GTK_WINDOW(window));
+
+ GtkWidget *overlay = gtk_overlay_new();
+ gtk_container_add(GTK_CONTAINER(window), overlay);
+
+ GtkWidget *bg = gtk_image_new_from_file(DATA_DIR "/bg.svg");
+ gtk_container_add(GTK_CONTAINER(overlay), bg);
+
+ GtkBuilder *builder = gtk_builder_new();
+
+ if (!gtk_builder_add_from_file(builder, DATA_DIR "/unlock.ui", NULL))
+ exit(1);
+
+ unlockWindow = GTK_WIDGET(gtk_builder_get_object(builder, "unlock_window"));
+ lockLabel = GTK_WIDGET(gtk_builder_get_object(builder, "lock_label"));
+ promptEntry = GTK_WIDGET(gtk_builder_get_object(builder, "prompt_entry"));
+ messageLabel = GTK_WIDGET(gtk_builder_get_object(builder, "message_label"));
+ logoutButton = GTK_WIDGET(gtk_builder_get_object(builder, "logout_button"));
+ unlockButton = GTK_WIDGET(gtk_builder_get_object(builder, "unlock_button"));
+
+ gtk_overlay_add_overlay(GTK_OVERLAY(overlay), unlockWindow);
+
+ gtk_widget_show_all(overlay);
+
+ gtk_entry_set_text(GTK_ENTRY(promptEntry), "");
+ gtk_label_set_label(GTK_LABEL(messageLabel), "");
+
+ g_signal_connect(promptEntry, "activate", G_CALLBACK(try_unlock), NULL);
+ g_signal_connect(unlockButton, "clicked", G_CALLBACK(try_unlock), NULL);
+ g_signal_connect(logoutButton, "clicked", G_CALLBACK(logout), NULL);
+ g_signal_connect(window, "map-event", G_CALLBACK(lock), NULL);
+ g_signal_connect(window, "delete-event", G_CALLBACK(gtk_true), NULL);
+
+ XSelectInput(gdk_x11_get_default_xdisplay(), gdk_x11_get_default_root_xwindow(), SubstructureNotifyMask);
+ gdk_window_add_filter(NULL, (GdkFilterFunc)xevent_filter, NULL);
+
+ update_logout_time();
+}
+
+static void get_username(void) {
+ struct passwd *passwd = getpwuid(getuid());
+ if (!passwd)
+ exit(2);
+
+ username = strdup(passwd->pw_name);
+}
+
+static void load_config(void) {
+ GKeyFile *keyFile = g_key_file_new();
+ if (!g_key_file_load_from_file(keyFile, CONFIG_FILE, G_KEY_FILE_NONE, NULL))
+ return;
+
+ gchar *theme = g_key_file_get_string(keyFile, "ulock", "theme", NULL);
+ if (theme)
+ gtk_settings_set_string_property(gtk_settings_get_default(), "gtk-theme-name", theme, "ulock");
+
+ logoutTime = g_key_file_get_integer(keyFile, "ulock", "logout_timeout", NULL);
+
+ gchar *logoutCommandConfig = g_key_file_get_string(keyFile, "ulock", "logout_string", NULL);
+ if (logoutCommandConfig)
+ logoutCommand = strdup(logoutCommandConfig);
+
+ g_key_file_free(keyFile);
+}
+
+int main(int argc, char *argv[]) {
+ get_username();
+ logoutCommand = strdup("echo foo");
+
+ setlocale(LC_ALL, "");
+ bindtextdomain("ulock", PREFIX "/share/locale");
+ textdomain("ulock");
+
+ openlog("ulock", LOG_PID, LOG_DAEMON);
+
+ gtk_init(&argc, &argv);
+ load_config();
+
+ create_lock_window();
+ gtk_widget_show(window);
+
+ gtk_main();
+
+ return 0;
+}