2013-09-19 19:41:24 +02:00
# 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>. \n The 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>. \n The 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 ;
}