diff options
Diffstat (limited to 'kdesktop/lock/main.cpp')
-rw-r--r-- | kdesktop/lock/main.cpp | 563 |
1 files changed, 563 insertions, 0 deletions
diff --git a/kdesktop/lock/main.cpp b/kdesktop/lock/main.cpp new file mode 100644 index 000000000..f1407070c --- /dev/null +++ b/kdesktop/lock/main.cpp @@ -0,0 +1,563 @@ +/* This file is part of the TDE project + Copyright (C) 1999 David Faure + Copyright (c) 2003 Oswald Buddenhagen <ossi@kde.org> + Copyright (c) 2010-2015 Timothy Pearson <kb9vqf@pearsoncomputing.net> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <config.h> + +#include "lockprocess.h" +#include "main.h" +#include "kdesktopsettings.h" + +#include <tqfileinfo.h> + +#include <tdecmdlineargs.h> +#include <tdelocale.h> +#include <tdeglobal.h> +#include <kdebug.h> +#include <tdeglobalsettings.h> +#include <dcopref.h> +#include <ksimpleconfig.h> +#include <kstandarddirs.h> + +#include <tdmtsak.h> + +#include <stdlib.h> + +#if defined(Q_WS_X11) && defined(HAVE_XRENDER) && TQT_VERSION >= 0x030300 +#define COMPOSITE +#endif + +#ifdef COMPOSITE +# include <X11/Xlib.h> +# include <X11/extensions/Xrender.h> +# include <fixx11h.h> +# include <dlfcn.h> +#else +# include <X11/Xlib.h> +# include <fixx11h.h> +#endif + + +TQXLibWindowList trinity_desktop_lock_hidden_window_list; + +// [FIXME] Add GUI configuration checkboxes for these three settings (see kdesktoprc [ScreenSaver] UseUnmanagedLockWindows, DelaySaverStart, and UseTDESAK) +bool trinity_desktop_lock_use_system_modal_dialogs = FALSE; +bool trinity_desktop_lock_delay_screensaver_start = FALSE; +bool trinity_desktop_lock_use_sak = FALSE; +bool trinity_desktop_lock_hide_active_windows = FALSE; +bool trinity_desktop_lock_hide_cancel_button = FALSE; +bool trinity_desktop_lock_forced = FALSE; + +LockProcess* trinity_desktop_lock_process = NULL; + +bool signalled_forcelock; +bool signalled_dontlock; +bool signalled_securedialog; +bool signalled_blank; +bool signalled_run; +bool in_internal_mode = FALSE; + +bool argb_visual = FALSE; +pid_t kdesktop_pid = -1; +bool trinity_desktop_lock_settings_initialized = FALSE; + +static void sigusr1_handler(int) +{ + signalled_forcelock = TRUE; +} + +static void sigusr2_handler(int) +{ + signalled_dontlock = TRUE; +} + +static void sigusr3_handler(int) +{ + signalled_securedialog = TRUE; +} + +static void sigusr4_handler(int) +{ + signalled_blank = TRUE; +} + +static void sigusr5_handler(int) +{ + signalled_run = TRUE; +} + +static int trapXErrors(Display *, XErrorEvent *) +{ + return 0; +} + +bool MyApp::x11EventFilter( XEvent *ev ) +{ + if (ev->type == ButtonPress || ev->type == ButtonRelease || ev->type == MotionNotify) { + emit mouseInteraction(ev); + } + if (ev->type == XKeyPress || ev->type == ButtonPress) { + emit activity(); + } + else if (ev->type == MotionNotify) { + time_t tick = time( 0 ); + if (tick != lastTick) { + lastTick = tick; + emit activity(); + } + } + else if (ev->type == MapNotify) { + // HACK + // Hide all tooltips and notification windows + XMapEvent map_event = ev->xmap; + XWindowAttributes childAttr; + Window childTransient; + if (XGetWindowAttributes(map_event.display, map_event.window, &childAttr) && XGetTransientForHint(map_event.display, map_event.window, &childTransient)) { + if((childAttr.map_state == IsViewable) && (childAttr.override_redirect) && (childTransient)) { + if (!trinity_desktop_lock_hidden_window_list.contains(map_event.window)) { + trinity_desktop_lock_hidden_window_list.append(map_event.window); + } + XLowerWindow(map_event.display, map_event.window); + XFlush(map_event.display); + } + } + } + else if (ev->type == VisibilityNotify) { + // HACK + // Hide all tooltips and notification windows + XVisibilityEvent visibility_event = ev->xvisibility; + XWindowAttributes childAttr; + Window childTransient; + if ((visibility_event.state == VisibilityUnobscured) || (visibility_event.state == VisibilityPartiallyObscured)) { + if (XGetWindowAttributes(visibility_event.display, visibility_event.window, &childAttr) && XGetTransientForHint(visibility_event.display, visibility_event.window, &childTransient)) { + if((childAttr.map_state == IsViewable) && (childAttr.override_redirect) && (childTransient)) { + if (!trinity_desktop_lock_hidden_window_list.contains(visibility_event.window)) { + trinity_desktop_lock_hidden_window_list.append(visibility_event.window); + } + XLowerWindow(visibility_event.display, visibility_event.window); + XFlush(visibility_event.display); + } + } + } + } + else if (ev->type == CreateNotify) { + // HACK + // Close all tooltips and notification windows + XCreateWindowEvent create_event = ev->xcreatewindow; + XWindowAttributes childAttr; + Window childTransient; + + // XGetWindowAttributes may generate BadWindow errors, so make sure they are silently ignored + int (*oldHandler)(Display *, XErrorEvent *); + oldHandler = XSetErrorHandler(trapXErrors); + if (XGetWindowAttributes(create_event.display, create_event.window, &childAttr) && XGetTransientForHint(create_event.display, create_event.window, &childTransient)) { + if ((childAttr.override_redirect) && (childTransient)) { + if (!trinity_desktop_lock_hidden_window_list.contains(create_event.window)) { + trinity_desktop_lock_hidden_window_list.append(create_event.window); + } + XLowerWindow(create_event.display, create_event.window); + XFlush(create_event.display); + } + } + XSetErrorHandler(oldHandler); + } + else if (ev->type == DestroyNotify) { + XDestroyWindowEvent destroy_event = ev->xdestroywindow; + if (trinity_desktop_lock_hidden_window_list.contains(destroy_event.window)) { + trinity_desktop_lock_hidden_window_list.remove(destroy_event.window); + } + } +#if 0 + else if (ev->type == CreateNotify) { + // HACK + // Close all tooltips and notification windows + XCreateWindowEvent create_event = ev->xcreatewindow; + XWindowAttributes childAttr; + Window childTransient; + if (XGetWindowAttributes(create_event.display, create_event.window, &childAttr) && XGetTransientForHint(create_event.display, create_event.window, &childTransient)) { + if ((childAttr.override_redirect) && (childTransient)) { + XDestroyWindow(create_event.display, create_event.window); + } + } + } +#endif + return TDEApplication::x11EventFilter( ev ); +} + + +static TDECmdLineOptions options[] = +{ + { "forcelock", I18N_NOOP("Force session locking"), 0 }, + { "dontlock", I18N_NOOP("Only start screensaver"), 0 }, + { "securedialog", I18N_NOOP("Launch the secure dialog"), 0 }, + { "blank", I18N_NOOP("Only use the blank screensaver"), 0 }, + { "internal <pid>", I18N_NOOP("TDE internal command for background process loading"), 0 }, + TDECmdLineLastOption +}; + +void restore_hidden_override_redirect_windows() { + TQXLibWindowList::iterator it; + for (it = trinity_desktop_lock_hidden_window_list.begin(); it != trinity_desktop_lock_hidden_window_list.end(); ++it) { + Window win = *it; + XRaiseWindow(tqt_xdisplay(), win); + } +} + +// ----------------------------------------------------------------------------- + +int main( int argc, char **argv ) +{ + TDELocale::setMainCatalogue("kdesktop"); + + TDECmdLineArgs::init( argc, argv, "kdesktop_lock", I18N_NOOP("KDesktop Locker"), I18N_NOOP("Session Locker for KDesktop"), "2.1" ); + TDECmdLineArgs::addCmdLineOptions( options ); + TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); + + putenv(strdup("SESSION_MANAGER=")); + + TDEApplication::disableAutoDcopRegistration(); // not needed + + XSetErrorHandler(trapXErrors); + + MyApp* app = NULL; + + while (1 == 1) { + sigset_t new_mask; + sigset_t orig_mask; + + // Block reception of all signals in this thread + sigfillset(&new_mask); + sigprocmask(SIG_BLOCK, &new_mask, NULL); + + signalled_forcelock = FALSE; + signalled_dontlock = FALSE; + signalled_securedialog = FALSE; + signalled_blank = FALSE; + signalled_run = FALSE; + + int kdesktop_screen_number = 0; + int starting_screen = 0; + + bool child = false; + int parent_connection = 0; // socket to the parent saver + TQValueList<int> child_sockets; + + if (TDEGlobalSettings::isMultiHead()) { + Display *dpy = XOpenDisplay(NULL); + if (! dpy) { + fprintf(stderr, + "%s: FATAL ERROR: couldn't open display '%s'\n", + argv[0], XDisplayName(NULL)); + exit(1); + } + + int number_of_screens = ScreenCount(dpy); + starting_screen = kdesktop_screen_number = DefaultScreen(dpy); + int pos; + TQCString display_name = XDisplayString(dpy); + XCloseDisplay(dpy); + kdDebug() << "screen " << number_of_screens << " " << kdesktop_screen_number << " " << display_name << " " << starting_screen << endl; + dpy = 0; + + if ((pos = display_name.findRev('.')) != -1) { + display_name.remove(pos, 10); + } + + TQCString env; + if (number_of_screens != 1) { + for (int i = 0; i < number_of_screens; i++) { + if (i != starting_screen) { + int fd[2]; + if (pipe(fd)) { + perror("pipe"); + break; + } + if (fork() == 0) { + child = true; + kdesktop_screen_number = i; + parent_connection = fd[0]; + // break here because we are the child process, we don't + // want to fork() anymore + break; + } + else { + child_sockets.append(fd[1]); + } + } + } + + env.sprintf("DISPLAY=%s.%d", display_name.data(), + kdesktop_screen_number); + kdDebug() << "env " << env << endl; + + if (putenv(strdup(env.data()))) { + fprintf(stderr, + "%s: WARNING: unable to set DISPLAY environment variable\n", + argv[0]); + perror("putenv()"); + } + } + } + + if (!app) { +#ifdef COMPOSITE + app = new MyApp(TDEApplication::openX11RGBADisplay()); + argb_visual = app->isX11CompositionAvailable(); +#else + app = new MyApp; +#endif + } + + TDELockFile lock(locateLocal("tmp", TQString("kdesktop_lock_lockfile.%1").arg(getenv("DISPLAY")))); + lock.setStaleTime(0); + TDELockFile::LockResult lockRet = lock.lock(); + if (lockRet != TDELockFile::LockOK) { + // Terminate existing (stale) process if needed + int pid; + TQString hostName; + TQString appName; + if (lock.getLockInfo(pid, hostName, appName)) { + // Verify that the pid in question is an instance of kdesktop_lock + int len; + char procpath[PATH_MAX]; + char fullpath[PATH_MAX]; +#if defined(__dilos__) + snprintf(procpath, sizeof(procpath), "/proc/%d/path/a.out", pid); +#elif defined(__FreeBSD__) || defined (__DragonFly__) + snprintf(procpath, sizeof(procpath), "/compat/linux/proc/%d/exe", pid); +#else /* Linux way as default */ + snprintf(procpath, sizeof(procpath), "/proc/%d/exe", pid); +#endif + len = readlink( procpath, fullpath, sizeof(fullpath) ); + if (len >= 0) { + fullpath[len] = 0; + TQFileInfo fileInfo(fullpath); + if (fileInfo.baseName() == "kdesktop_lock") { + // Verify that pid in question is owned by current user before killing it + uid_t current_uid = geteuid(); + + struct stat info; + if (lstat(procpath, &info) == 0) { + if (info.st_uid == current_uid) { + kill(pid, SIGKILL); + } + } + } + } + } + } + + // Force a relock as a stale lockfile or process may have been dealt with above + if (!lock.isLocked()) { + lockRet = lock.lock(TDELockFile::LockNoBlock | TDELockFile::LockForce); + } + + kdDebug() << "app " << kdesktop_screen_number << " " << starting_screen << " " << child << " " << child_sockets.count() << " " << parent_connection << endl; + app->disableSessionManagement(); + TDEGlobal::locale()->insertCatalogue("libdmctl"); + + struct stat st; + KSimpleConfig* tdmconfig; + if( stat( KDE_CONFDIR "/tdm/tdmdistrc" , &st ) == 0) { + tdmconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmdistrc" )); + } + else { + tdmconfig = new KSimpleConfig( TQString::fromLatin1( KDE_CONFDIR "/tdm/tdmrc" )); + } + tdmconfig->setGroup("X-:*-Greeter"); + + // Create new LockProcess, which also spawns threads inheriting the blocked signal mask + trinity_desktop_lock_process = new LockProcess; + + // Unblock reception of all signals in this thread + sigprocmask(SIG_UNBLOCK, &new_mask, NULL); + + // Start loading core functions, such as the desktop wallpaper interface + app->processEvents(); + + if (args->isSet( "internal" )) { + kdesktop_pid = atoi(args->getOption( "internal" )); + struct sigaction act; + + in_internal_mode = TRUE; + + // handle SIGUSR1 + act.sa_handler= sigusr1_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGUSR1); + act.sa_flags = 0; + sigaction(SIGUSR1, &act, 0L); + // handle SIGUSR2 + act.sa_handler= sigusr2_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGUSR2); + act.sa_flags = 0; + sigaction(SIGUSR2, &act, 0L); + // handle SIGWINCH (an ersatz SIGUSR3) + act.sa_handler= sigusr3_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGWINCH); + act.sa_flags = 0; + sigaction(SIGWINCH, &act, 0L); + // handle SIGTTIN (an ersatz SIGUSR4) + act.sa_handler= sigusr4_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGTTIN); + act.sa_flags = 0; + sigaction(SIGTTIN, &act, 0L); + // handle SIGTTOU (an ersatz SIGUSR5) + act.sa_handler= sigusr5_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGTTOU); + act.sa_flags = 0; + sigaction(SIGTTOU, &act, 0L); + + // initialize the signal masks + sigemptyset(&new_mask); + sigaddset(&new_mask,SIGUSR1); + sigaddset(&new_mask,SIGUSR2); + sigaddset(&new_mask,SIGWINCH); + sigaddset(&new_mask,SIGTTIN); + sigaddset(&new_mask,SIGTTOU); + + while (signalled_run == FALSE) { + // let kdesktop know the saver process is ready + if (kill(kdesktop_pid, SIGTTIN) < 0) { + // The controlling kdesktop process probably died. Commit suicide... + return 12; + } + + // Get root window attributes + XWindowAttributes rootAttr; + XGetWindowAttributes(tqt_xdisplay(), RootWindow(tqt_xdisplay(), tqt_xscreen()), &rootAttr); + + // Disable reception of all X11 events on the root window + XSelectInput( tqt_xdisplay(), tqt_xrootwin(), 0 ); + app->processEvents(); + + // wait for SIGUSR1, SIGUSR2, SIGWINCH, SIGTTIN, or SIGTTOU + sigprocmask(SIG_BLOCK, &new_mask, &orig_mask); + if (signalled_run != TRUE) { + sigsuspend(&orig_mask); + } + sigprocmask(SIG_UNBLOCK, &new_mask, NULL); + + // Reenable reception of X11 events on the root window + XSelectInput( tqt_xdisplay(), tqt_xrootwin(), rootAttr.your_event_mask ); + } + + // Block reception of all signals in this thread + sigprocmask(SIG_BLOCK, &new_mask, NULL); + } + + // (re)load settings here so that they actually reflect reality + // we need to read from the right rc file - possibly taking screen number in account + if (!trinity_desktop_lock_settings_initialized) { + KDesktopSettings::instance("kdesktoprc"); + trinity_desktop_lock_settings_initialized = true; + } + else { + KDesktopSettings::self()->readConfig(); + } + trinity_desktop_lock_use_system_modal_dialogs = !KDesktopSettings::useUnmanagedLockWindows(); + trinity_desktop_lock_delay_screensaver_start = KDesktopSettings::delaySaverStart(); + if (trinity_desktop_lock_use_system_modal_dialogs) { +#ifdef BUILD_TSAK + trinity_desktop_lock_use_sak = tdmconfig->readBoolEntry("UseSAK", false) && KDesktopSettings::useTDESAK(); +#else + trinity_desktop_lock_use_sak = false; +#endif + } + else { + trinity_desktop_lock_use_sak = false; // If SAK is enabled with unmanaged windows, the SAK dialog will never close and will "burn in" the screen + trinity_desktop_lock_delay_screensaver_start = false; // If trinity_desktop_lock_delay_screensaver_start is true with unmanaged windows, the lock dialog may never appear + } + trinity_desktop_lock_hide_active_windows = KDesktopSettings::hideActiveWindowsFromSaver(); + trinity_desktop_lock_hide_cancel_button = KDesktopSettings::hideCancelButton(); + + delete tdmconfig; + + if (args->isSet( "forcelock" ) || (signalled_forcelock == TRUE)) { + trinity_desktop_lock_forced = TRUE; + } + + trinity_desktop_lock_process->init(child, (args->isSet( "blank" ) || (signalled_blank == TRUE))); + if (!child) { + trinity_desktop_lock_process->setChildren(child_sockets); + } + else { + trinity_desktop_lock_process->setParent(parent_connection); + } + + bool rt; + if( (((!child) && (args->isSet( "forcelock" ))) || (signalled_forcelock == TRUE))) { + rt = trinity_desktop_lock_process->lock(); + } + else if( child || (args->isSet( "dontlock" ) || (signalled_dontlock == TRUE))) { + rt = trinity_desktop_lock_process->dontLock(); + } + else if( child || (args->isSet( "securedialog" ) || (signalled_securedialog == TRUE))) { + int retcode = tde_sak_verify_calling_process(); + if (retcode == 0) { + rt = trinity_desktop_lock_process->runSecureDialog(); + } + else { + return 1; + } + } + else { + rt = trinity_desktop_lock_process->defaultSave(); + } + if (!rt) { + return 0; + } + + if (in_internal_mode == FALSE) { + trinity_desktop_lock_hidden_window_list.clear(); + int ret = app->exec(); + restore_hidden_override_redirect_windows(); + return ret; + } + else { + if (kill(kdesktop_pid, 0) < 0) { + // The controlling kdesktop process probably died. Commit suicide... + return 12; + } + trinity_desktop_lock_hidden_window_list.clear(); + app->exec(); + restore_hidden_override_redirect_windows(); + if (kill(kdesktop_pid, SIGUSR1) < 0) { + // The controlling kdesktop process probably died. Commit suicide... + return 12; + } + + delete trinity_desktop_lock_process; + trinity_desktop_lock_process = NULL; + + // FIXME + // We should not have to return (restart) at all, + // but it seems that some X11 connections are left active, + // preventing the lock process from restarting properly in the while() loop above. + return 0; + } + } +} + +#include "main.moc" |