xsecurelock-1.9.0/0000755000175000017500000000000014540330460011050 500000000000000xsecurelock-1.9.0/auth_child.c0000644000175000017500000001351013757234024013250 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "auth_child.h" #include // for NULL, EXIT_FAILURE #include // for strlen #include // for close, _exit, dup2, execl, fork, pipe #include "env_settings.h" // for GetIntSetting #include "logging.h" // for LogErrno, Log #include "wait_pgrp.h" // for KillPgrp, WaitPgrp #include "xscreensaver_api.h" // for ExportWindowID //! The PID of a currently running saver child, or 0 if none is running. static pid_t auth_child_pid = 0; //! If auth_child_pid != 0, the FD which connects to stdin of the auth child. static int auth_child_fd = 0; void KillAuthChildSigHandler(int signo) { // This is a signal handler, so we're not going to make this too complicated. // Just kill it. if (auth_child_pid != 0) { KillPgrp(auth_child_pid, signo); } } /*! \brief Return whether the wake-up keypress should be discarded and not be * sent to the auth child. * * Sending the wake-up keypress to the auth child is usually a bad idea because * many people use "any" key, not their password's, to wake up the screen saver. * Also, when using a blanking screen saver, one can't easily distinguish a * locked screen from a turned-off screen, and may thus accidentally start * entering the password into a web browser or similar "bad" place. * * However, it was requested by a user, so why not add it. Usage: * * XSECURELOCK_DISCARD_FIRST_KEYPRESS=0 xsecurelock */ static int DiscardFirstKeypress() { return GetIntSetting("XSECURELOCK_DISCARD_FIRST_KEYPRESS", !GetIntSetting("XSECURELOCK_WANT_FIRST_KEYPRESS", 0)); } int WantAuthChild(int force_auth) { if (force_auth) { return 1; } return (auth_child_pid != 0); } /*! \brief Return whether buf contains exclusively control characters. * * Because there is no portable way of doing this (other than relying on wchar * routines that are nowhere else exercised in the main program), I'll just * match precisely those that ASCII defines as control codes - 00 to 1f as well * as 7f (DEL). * * We do this so we do not forward control keys to the auth child when just * waking it up (e.g. because the user tried to unlock the screen with ESC or * ENTER). * * \param buf The string to check. * \return 1 if buf contains at least one non-control character, and 0 * otherwise. */ static int ContainsNonControl(const char *buf) { while (*buf) { // Note: this almost isprint but not quite - isprint returns false on // high bytes in UTF-8 locales but we do want to forward anything UTF-8. // An alternative could be walking the string with multibyte functions and // using iswprint - but I'd rather not do that anywhere security critical. if (*buf < '\000' || (*buf > '\037' && *buf != '\177')) { return 1; } ++buf; } return 0; } int WatchAuthChild(Window w, const char *executable, int force_auth, const char *stdinbuf, int *auth_running) { if (auth_child_pid != 0) { // Check if auth child returned. int status; if (WaitPgrp("auth", &auth_child_pid, 0, 0, &status)) { // Clean up. close(auth_child_fd); // Handle success; this will exit the screen lock. if (status == 0) { *auth_running = 0; return 1; } // To handle failure, we just fall through, as we may want to immediately // launch a new auth child and send it a keypress. } } if (force_auth && auth_child_pid == 0) { // Start auth child. int pc[2]; if (pipe(pc)) { LogErrno("pipe"); } else { pid_t pid = ForkWithoutSigHandlers(); if (pid == -1) { LogErrno("fork"); } else if (pid == 0) { // Child process. StartPgrp(); ExportWindowID(w); close(pc[1]); if (pc[0] != 0) { if (dup2(pc[0], 0) == -1) { LogErrno("dup2"); _exit(EXIT_FAILURE); } close(pc[0]); } { const char *args[2] = {executable, NULL}; ExecvHelper(executable, args); sleep(2); // Reduce log spam or other effects from failed execv. _exit(EXIT_FAILURE); } } else { // Parent process after successful fork. close(pc[0]); auth_child_fd = pc[1]; auth_child_pid = pid; if (stdinbuf != NULL && (DiscardFirstKeypress() || !ContainsNonControl(stdinbuf))) { // The auth child has just been started. Do not send any keystrokes to // it immediately. Exception: when the user requested different // behavior by XSECURELOCK_DISCARD_FIRST_KEYPRESS=0 and there is a // printable character. stdinbuf = NULL; } } } } // Report whether the auth child is running. *auth_running = (auth_child_pid != 0); // Send the provided keyboard buffer to stdin. if (stdinbuf != NULL && stdinbuf[0] != 0) { if (auth_child_pid != 0) { ssize_t to_write = (ssize_t)strlen(stdinbuf); ssize_t written = write(auth_child_fd, stdinbuf, to_write); if (written < 0) { LogErrno("Failed to send all data to the auth child"); } else if (written != to_write) { Log("Failed to send all data to the auth child"); } } else { Log("No auth child. Can't send key events"); } } return 0; } xsecurelock-1.9.0/xscreensaver_api.h0000644000175000017500000000234014313046751014506 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef XSCREENSAVER_API_H #define XSCREENSAVER_API_H #include // for Window /*! \brief Export the given window ID to the environment for a saver/auth child. * * This simply sets $XSCREENSAVER_WINDOW. * * \param w The window the child should draw on. */ void ExportWindowID(Window w); /*! \brief Export the given saver index to the environment for a saver/auth child. * * This simply sets $XSCREENSAVER_SAVER_INDEX. * * \param index The index of the saver. */ void ExportSaverIndex(int index); /*! \brief Reads the window ID to draw on from the environment. * * This simply reads $XSCREENSAVER_WINDOW. */ Window ReadWindowID(void); #endif xsecurelock-1.9.0/aclocal.m40000644000175000017500000015341214540330455012642 00000000000000# generated automatically by aclocal 1.16.5 -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.71],, [m4_warning([this file was generated for autoconf 2.71. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # pkg.m4 - Macros to locate and use pkg-config. -*- Autoconf -*- # serial 12 (pkg-config-0.29.2) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.2]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurrence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $2]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.5], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.5])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. If GNU make was not used, consider re-running the configure script with MAKE="gmake" (or whatever is necessary). You can also try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl m4_ifdef([_$0_ALREADY_INIT], [m4_fatal([$0 expanded multiple times ]m4_defn([_$0_ALREADY_INIT]))], [m4_define([_$0_ALREADY_INIT], m4_expansion_stack)])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifset([AC_PACKAGE_NAME], [ok]):m4_ifset([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) # Variables for tags utilities; see am/tags.am if test -z "$CTAGS"; then CTAGS=ctags fi AC_SUBST([CTAGS]) if test -z "$ETAGS"; then ETAGS=etags fi AC_SUBST([ETAGS]) if test -z "$CSCOPE"; then CSCOPE=cscope fi AC_SUBST([CSCOPE]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then MISSING="\${SHELL} '$am_aux_dir/missing'" fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2021 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR xsecurelock-1.9.0/wait_pgrp.h0000644000175000017500000000704413757234024013152 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef WAIT_PGRP_H #define WAIT_PGRP_H #include // for INT_MIN #include // for pid_t #define WAIT_ALREADY_DEAD INT_MIN #define WAIT_NONPOSITIVE_SIGNAL (INT_MIN + 1) /*! \brief Initializes WaitPgrp. * * Actually just installs an empty SIGCHLD handler so select(), sigsuspend() * etc. get interrupted by the signal. */ void InitWaitPgrp(void); /*! \brief Fork a subprocess, but do not inherit our signal handlers. * * Otherwise behaves exactly like fork(). */ pid_t ForkWithoutSigHandlers(void); /*! \brief Starts a new process group. * * Must be called from a child process, which will become the process group * leader. The process group will never die, unless killed using KillPgrp (which * WaitPgrp calls implicitly when the leader process terminates). * * \return Zero if the operation succeeded. */ void StartPgrp(void); /*! \brief Spawns a helper process. * * Works just like execv(), but if path is a relative path, it looks it up * within HELPER_PATH. * * If it fails, it logs a message about what it tried to execute and how it * failed. */ int ExecvHelper(const char *path, const char *const argv[]); /*! \brief Kills the given process group. * * \param pid The process group ID. * \param signo The signal to send to the process group. * \return Zero if and only if sending the signal succeeded. */ int KillPgrp(pid_t pid, int signo); /*! \brief Waits for the given process group to terminate, or checks its status. * If the leader process died, kill the entire group. * * \param name The name of the process group for logging. * \param pid The process group ID; it is set to zero if the process group died. * \param do_block Whether to wait for the process group to terminate. * \param already_killed Whether the caller already sent SIGTERM to the process * group. If so, we will not log this signal as that'd be spam. * \param exit_status Variable that receives the exit status of the leader when * it terminated. Will be negative for a signal, positive for a regular exit, * or one of the WAIT_* constants. * \return True if the process group is still alive. */ int WaitPgrp(const char *name, pid_t *pid, int do_block, int already_killed, int *exit_status); /*! \brief Waits for the given process to terminate, or checks its status. * * \param name The name of the process for logging. * \param pid The process ID; it is set to zero if the process died. * \param do_block Whether to wait for the process to terminate. * \param already_killed Whether the caller already sent SIGTERM to the process. * If so, we will not log this signal as that'd be spam. \param exit_status * Variable that receives the exit status of the leader when it terminated. * Will be negative for a signal, positive for a regular exit, or one of the * WAIT_* constants. * \return True if the process is still alive. */ int WaitProc(const char *name, pid_t *pid, int do_block, int already_killed, int *exit_status); #endif xsecurelock-1.9.0/missing0000755000175000017500000001533614077246515012413 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2020 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: xsecurelock-1.9.0/env_info.h0000644000175000017500000000225113757234024012754 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef ENV_INFO_H #define ENV_INFO_H #include /*! \brief Loads the current host name. * * \param hostname_buf The buffer to write the host name to. * \param hostname_buflen The size of the buffer. * \return Whether fetching the host name succeeded. */ int GetHostName(char* hostname_buf, size_t hostname_buflen); /*! \brief Loads the current user name. * * \param username_buf The buffer to write the user name to. * \param username_buflen The size of the buffer. * \return Whether fetching the user name succeeded. */ int GetUserName(char* username_buf, size_t username_buflen); #endif xsecurelock-1.9.0/run-linters.sh0000755000175000017500000000411613757234024013623 00000000000000#!/bin/bash # clang-tidy. if which clang-tidy; then set -- \ 'bugprone-*' \ 'cert-*' \ 'clang-analyzer-*' \ 'misc-*' \ 'performance-*' \ 'readability-*' \ '-cert-env33-c' \ '-cert-msc30-c' \ '-cert-msc50-cpp' \ '-clang-analyzer-alpha.core.FixedAddr' \ '-clang-analyzer-alpha.core.PointerArithm' \ '-clang-analyzer-alpha.deadcode.UnreachableCode' checks=$(echo "$*" | tr ' ' ,) # Try once without extensions. clang-tidy -checks="$checks" \ -extra-arg=-DHELPER_PATH=\"\" \ -extra-arg=-DDOCS_PATH=\"\" \ -extra-arg=-DAUTH_EXECUTABLE=\"\" \ -extra-arg=-DAUTHPROTO_EXECUTABLE=\"\" \ -extra-arg=-DGLOBAL_SAVER_EXECUTABLE=\"\" \ -extra-arg=-DSAVER_EXECUTABLE=\"\" \ -extra-arg=-DPAM_SERVICE_NAME=\"\" \ *.[ch] */*.[ch] # Try again with all extensions. clang-tidy -checks="$checks" \ -extra-arg=-I/usr/include/freetype2 \ -extra-arg=-DHELPER_PATH=\"\" \ -extra-arg=-DDOCS_PATH=\"\" \ -extra-arg=-DAUTH_EXECUTABLE=\"\" \ -extra-arg=-DAUTHPROTO_EXECUTABLE=\"\" \ -extra-arg=-DGLOBAL_SAVER_EXECUTABLE=\"\" \ -extra-arg=-DSAVER_EXECUTABLE=\"\" \ -extra-arg=-DPAM_SERVICE_NAME=\"\" \ -extra-arg=-DHAVE_DPMS_EXT \ -extra-arg=-DHAVE_XCOMPOSITE_EXT \ -extra-arg=-DHAVE_XFIXES_EXT \ -extra-arg=-DHAVE_XKB_EXT \ -extra-arg=-DHAVE_XFT_EXT \ -extra-arg=-DHAVE_XRANDR_EXT \ -extra-arg=-DHAVE_XSCREENSAVER_EXT \ -extra-arg=-DHAVE_XSYNC_EXT \ *.[ch] */*.[ch] fi # CPPCheck. if which cppcheck; then cppcheck --enable=all --inconclusive --std=posix . fi # Clang Analyzer. if which scan-build; then make clean scan-build make fi # Build for Coverity Scan. if which cov-build; then make clean rm -rf cov-int cov-build --dir cov-int make tar cvjf cov-int.tbz2 cov-int/ rm -rf cov-int rev=$(git describe --always --dirty) curl --form token="$COVERITY_TOKEN" \ --form email="$COVERITY_EMAIL" \ --form file=@cov-int.tbz2 \ --form version="$rev" \ --form description="$rev" \ https://scan.coverity.com/builds?project=xsecurelock rm -f cov-int.tbz2 fi xsecurelock-1.9.0/CONTRIBUTING0000644000175000017500000000036113757234024012632 00000000000000In order to contribute to this project, a Contributor License Agreement needs to be signed. These agreements are available at: https://developers.google.com/open-source/cla/individual https://developers.google.com/open-source/cla/corporate xsecurelock-1.9.0/saver_child.c0000644000175000017500000000520114313046751013422 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "saver_child.h" #include // for sigemptyset, sigprocmask, SIG_SETMASK #include // for NULL, EXIT_FAILURE #include // for pid_t, _exit, execl, fork, setsid, sleep #include "logging.h" // for LogErrno, Log #include "wait_pgrp.h" // for KillPgrp, WaitPgrp #include "xscreensaver_api.h" // for ExportWindowID and ExportSaverIndex //! The PIDs of currently running saver children, or 0 if not running. static pid_t saver_child_pid[MAX_SAVERS] = {0}; void KillAllSaverChildrenSigHandler(int signo) { // This is a signal handler, so we're not going to make this too // complicated. Just kill 'em all. for (int i = 0; i < MAX_SAVERS; ++i) { if (saver_child_pid[i] != 0) { KillPgrp(saver_child_pid[i], signo); } } } void WatchSaverChild(Display* dpy, Window w, int index, const char* executable, int should_be_running) { if (index < 0 || index >= MAX_SAVERS) { Log("Saver index out of range: !(0 <= %d < %d)", index, MAX_SAVERS); return; } if (saver_child_pid[index] != 0) { if (!should_be_running) { KillPgrp(saver_child_pid[index], SIGTERM); } int status; if (WaitPgrp("saver", &saver_child_pid[index], !should_be_running, !should_be_running, &status)) { // Now is the time to remove anything the child may have displayed. XClearWindow(dpy, w); } } if (should_be_running && saver_child_pid[index] == 0) { pid_t pid = ForkWithoutSigHandlers(); if (pid == -1) { LogErrno("fork"); } else if (pid == 0) { // Child process. StartPgrp(); ExportWindowID(w); ExportSaverIndex(index); { const char* args[3] = { executable, "-root", // For XScreenSaver hacks, unused by our own. NULL}; ExecvHelper(executable, args); sleep(2); // Reduce log spam or other effects from failed execv. _exit(EXIT_FAILURE); } } else { // Parent process after successful fork. saver_child_pid[index] = pid; } } } xsecurelock-1.9.0/doc/0000755000175000017500000000000014540330460011615 500000000000000xsecurelock-1.9.0/doc/xsecurelock.1.md0000644000175000017500000000245413757234024014562 00000000000000% XSECURELOCK(1) XSecureLock User Manual % Rudolf Polzer % April 15, 2019 # NAME XSecureLock - X11 screen lock utility # SYNPOSIS [*options*] xsecurelock [-- *command-to-run-when-locked*] # DESCRIPTION XSecureLock is an X11 screen lock utility designed with the primary goal of security. It locks the current X11 session, and only allows unlocking if the user authenticates to it (typically with the login password). While locked, it can launch a screen saver process and then waits for input events. Upon an input event, it displays a login dialog to allow for unlocking. # OPTIONS Options are set as environment variables prior to invoking XSecureLock; the following variables are available: Additionally, XSecureLock spawns the *command-to-run-when-locked* once locking is complete; this can be used as a notification mechanism if desired. # REPORTING BUGS The official bug tracker is at . # COPYRIGHT The code is released unser the Apache 2.0 license. See the LICENSE file for more details. # SEE ALSO `xss-lock` (1), `xautolock` (1). The *README.md* file included with XSecureLock contains full documentation. The XSecureLock source code and all documentation may be downloaded on . xsecurelock-1.9.0/doc/examples/0000755000175000017500000000000014540330460013433 500000000000000xsecurelock-1.9.0/doc/examples/saver_livestreams0000755000175000017500000000175613757234024017060 00000000000000#!/bin/bash # This screensaver displays livestreams that are listed in the feed file located # at "~/.streamsaver-feeds". The feeds are chosen by the current day of year # and the index of the monitor as shown in `xrandr`. The entries started with # "#"-sign is ignored. # # Please install livestreamer (http://livestreamer.io) before using this saver. # # A sample feed file (~/.streamsaver-feeds): # > http://ustream.tv/channel/iss-hdev-payload # > http://ustream.tv/channel/live-iss-stream # > # http://ustream.tv/channel/live-mir-stream feeds=(`cat ~/.streamsaver-feeds | grep -Ev '^#'`) monitor_index=`xrandr | grep -wi "connected" | sort | grep -nho "$(xwininfo -id "$XSCREENSAVER_WINDOW" | awk '/^ Corners:/ { print $2 }')" | awk -F: '{ print $1; exit 0; }'` day=`date +"%-j"` i=$(( ($monitor_index+$day)%${#feeds[@]} )) /usr/bin/livestreamer "${feeds[i]}" "best" --quiet --player="/usr/bin/mpv --no-input-terminal --really-quiet --no-audio --no-stop-screensaver --wid=${XSCREENSAVER_WINDOW}" xsecurelock-1.9.0/README.md0000644000175000017500000007705214503371750012270 00000000000000# About XSecureLock XSecureLock is an X11 screen lock utility designed with the primary goal of security. Screen lock utilities are widespread. However, in the past they often had security issues regarding authentication bypass (a crashing screen locker would unlock the screen), information disclosure (notifications may appear on top of the screen saver), or sometimes even worse. In XSecureLock, security is achieved using a modular design to avoid the usual pitfalls of screen locking utility design on X11. Details are available in the [Security Design](#security-design) section. # Requirements The following packages need to be installed; their names depend on your Linux distribution of choice, but will be similar: * apache2-utils (for the `auth_htpasswd` module) * autotools-dev * autoconf (for Ubuntu 18.04 and newer) * binutils * gcc * libc6-dev * libpam0g-dev (for Ubuntu 18.04 and newer) * libpam-dev (for the `authproto_pam` module) * libx11-dev * libxcomposite-dev * libxext-dev * libxfixes-dev * libxft-dev * libxmuu-dev * libxrandr-dev * libxss-dev * make * mplayer (for the `saver_mplayer` module) * mpv (for the `saver_mpv` module) * pamtester (for the `authproto_pamtester` module) * pkg-config * x11proto-core-dev * xscreensaver (for the `saver_xscreensaver` module) # Installation NOTE: In these instructions, please replace SERVICE-NAME by the name of an appropriate and existing file in `/etc/pam.d`. If xscreensaver is installed, `xscreensaver` should always be a good choice; otherwise, on Debian and Ubuntu, `common-auth` would work. This will be used as default and can be overridden with [`XSECURELOCK_PAM_SERVICE`](#options). Configuring a broken or missing SERVICE-NAME will render unlocking the screen impossible! If this should happen to you, switch to another terminal (`Ctrl-Alt-F1`), log in there, and run: `killall xsecurelock` to force unlocking of the screen. ``` git clone https://github.com/google/xsecurelock.git cd xsecurelock sh autogen.sh ./configure --with-pam-service-name=SERVICE-NAME make sudo make install ``` ## Special notes for FreeBSD and NetBSD First of all, on BSD systems, `/usr/local` is owned by the ports system, so unless you are creating a port, it is recommended to install to a separate location by specifying something like `--prefix=/opt/xsecurelock` in the `./configure` call. You can then run XSecureLock as `/opt/xsecurelock/bin/xsecurelock`. Also, in order to authenticate with PAM on FreeBSD and NetBSD, you must be root so you can read the shadow password database. The `authproto_pam` binary can be made to acquire these required privileges like this: ``` chmod +s /opt/xsecurelock/libexec/xsecurelock/authproto_pam ``` ## Special notes for OpenBSD First of all, on BSD systems, `/usr/local` is owned by the ports system, so unless you are creating a port, it is recommended to install to a separate location by specifying something like `--prefix=/opt/xsecurelock` in the `./configure` call. You can then run XSecureLock as `/opt/xsecurelock/bin/xsecurelock`. Also, in order to authenticate with PAM on OpenBSD, you must be in the `auth` group so you can run a setuid helper called `login_passwd` that can read the shadow password database. The `authproto_pam` binary can be made to acquire these required privileges like this: ``` chgrp auth /opt/xsecurelock/libexec/xsecurelock/authproto_pam chmod g+s /opt/xsecurelock/libexec/xsecurelock/authproto_pam ``` Note that this adds substantially less attack surface than adding your own user to the `auth` group, as the `login_passwd` binary can try out passwords of any user, while `authproto_pam` is restricted to trying your own user. # Setup Pick one of the [authentication modules](#authentication-modules) and one of the [screen saver modules](#screen-saver-modules). Tell your desktop environment to run XSecureLock by using a command line such as one of the following: ``` xsecurelock env XSECURELOCK_SAVER=saver_xscreensaver xsecurelock env XSECURELOCK_SAVER=saver_mplayer XSECURELOCK_DISCARD_FIRST_KEYPRESS=0 xsecurelock env XSECURELOCK_FONT=`xlsfonts | grep '\' | shuf | head -n 1` xsecurelock ``` Just kidding about the last one :) IMPORTANT: Make sure your desktop environment does not launch any other locker, be it via autostart file or its own configuration, as multiple screen lockers may interfere with each other. You have been warned! ## Authentication on resume from suspend/hibernate To have the authentication process start up without a keypress when the system exits suspend/hibernate, arrange for the system to send the `SIGUSR2` signal to the XSecureLock process. For example, you can copy the following script to the file `/usr/lib/systemd/system-sleep/xsecurelock`: ``` #!/bin/bash if [[ "$1" = "post" ]] ; then pkill -x -USR2 xsecurelock fi exit 0 ``` Don't forget to mark the script executable. # Automatic Locking To automatically lock the screen after some time of inactivity, use [xss-lock](https://bitbucket.org/raymonad/xss-lock) as follows: ``` xset s 300 5 xss-lock -n /usr/lib/xsecurelock/dimmer -l -- xsecurelock ``` The option `-l` is critical as it makes sure not to allow machine suspend before the screen saver is active - otherwise previous screen content may show up for a short time after wakeup! NOTE: When using `xss-lock`, it's recommended to not launch `xsecurelock` directly for manual locking, but to manually lock using `xset s activate`. This ensures that `xss-lock` knows about the locking state and won't try again, which would spam the X11 error log. WARNING: Never rely on automatic locking for security, for the following reasons: - An attacker can, of course, use your computer after you leave it alone and before it locks or you return. - Automatic locking is unreliable by design - for example, it could simply be misconfigured, or a pointer grab (due to open context menu) could prevent the screen lock from ever activating. Media players also often suspend screen saver activation for usability reasons. Automatic locking should merely be seen as a fallback for the case of the user forgetting to lock explicitly, and not as a security feature. If you really want to use this as a security feature, make sure to kill the session whenever attempts to lock fail (in which case `xsecurelock` will return a non-zero exit status). ## Alternatives ### xautolock `xautolock` can be used instead of `xss-lock` as long as you do not care for suspend events (like on laptops): ``` xautolock -time 10 -notify 5 -notifier '/usr/lib/xsecurelock/until_nonidle /usr/lib/xsecurelock/dimmer' -locker xsecurelock ``` ### Possible other tools Ideally, an environment integrating `xsecurelock` should provide the following facilities: 1. Wait for one of the following events: 1. When idle for a sufficient amount of time: 1. Run `dimmer`. 2. When no longer idle while dimmed, kill `dimmer` and go back to the start. 3. When `dimmer` exits, run `xsecurelock` and wait for it. 2. When locking was requested, run `xsecurelock` and wait for it. 3. When suspending, run `xsecurelock` while passing along `XSS_SLEEP_LOCK_FD` and wait for it. 2. Repeat. This is, of course precisely what `xss-lock` does, and - apart from the suspend handling - what `xautolock` does. As an alternative, we also support this way of integrating: 1. Wait for one of the following events: 1. When idle for a sufficient amount of time: 1. Run `until_nonidle dimmer || exec xsecurelock` and wait for it. 2. Reset your idle timer (optional when your idle timer is either the X11 Screen Saver extension's idle timer or the X Synchronization extension's `"IDLETIME"` timer, as this command can never exit without those being reset). 2. When locking was requested, run `xsecurelock` and wait for it. 3. When suspending, run `xsecurelock` while passing along `XSS_SLEEP_LOCK_FD` and wait for it. 2. Repeat. NOTE: When using `until_nonidle` with other dimming tools than the included `dimmer`, please set `XSECURELOCK_DIM_TIME_MS` and `XSECURELOCK_WAIT_TIME_MS` to match the time your dimming tool takes for dimming, and how long you want to wait in dimmed state before locking. # Options Options to XSecureLock can be passed by environment variables: * `XSECURELOCK_AUTH`: specifies the desired authentication module (the part that displays the authentication prompt). * `XSECURELOCK_AUTHPROTO`: specifies the desired authentication protocol module (the part that talks to the system). * `XSECURELOCK_AUTH_BACKGROUND_COLOR`: specifies the X11 color (see manpage of XParseColor) for the background of the auth dialog. * `XSECURELOCK_AUTH_CURSOR_BLINK`: if set, the cursor will blink in the auth dialog. Enabled by default, can be set to 0 to disable. * `XSECURELOCK_AUTH_SOUNDS`: specifies whether to play sounds during authentication to indicate status. Sounds are defined as follows: * High-pitch ascending: prompt for user input. * High-pitch constant: an info message was displayed. * Low-pitch descending: an error message was displayed. * Medium-pitch ascending: authentication successful. * `XSECURELOCK_AUTH_FOREGROUND_COLOR`: specifies the X11 color (see manpage of XParseColor) for the foreground text of the auth dialog. * `XSECURELOCK_AUTH_TIMEOUT`: specifies the time (in seconds) to wait for response to a prompt by `auth_x11` before giving up and reverting to the screen saver. * `XSECURELOCK_AUTH_WARNING_COLOR`: specifies the X11 color (see manpage of XParseColor) for the warning text of the auth dialog. * `XSECURELOCK_BACKGROUND_COLOR`: specifies the X11 color (see manpage of XParseColor) for the background of the main and saver windows. * `XSECURELOCK_BLANK_TIMEOUT`: specifies the time (in seconds) before telling X11 to fully blank the screen; a negative value disables X11 blanking. The time is measured since the closing of the auth window or xsecurelock startup. Setting this to 0 is rather nonsensical, as key-release events (e.g. from the keystroke to launch xsecurelock or from pressing escape to close the auth dialog) always wake up the screen. * `XSECURELOCK_BLANK_DPMS_STATE`: specifies which DPMS state to put the screen in when blanking (one of standby, suspend, off and on, where "on" means to not invoke DPMS at all). * `XSECURELOCK_BURNIN_MITIGATION`: specifies the number of pixels the prompt of `auth_x11` may be moved at startup to mitigate possible burn-in effects due to the auth dialog being displayed all the time (e.g. when spurious mouse events wake up the screen all the time). * `XSECURELOCK_BURNIN_MITIGATION_DYNAMIC`: if set to a non-zero value, `auth_x11` will move the prompt while it is being displayed, but stay within the bounds of `XSECURELOCK_BURNIN_MITIGATION`. The value of this variable is the maximum allowed shift per screen refresh. This mitigates short-term burn-in effects but is probably annoying to most users, and thus disabled by default. * `XSECURELOCK_COMPOSITE_OBSCURER`: create a second full-screen window to obscure window content in case a running compositor unmaps its own window. Helps with some instances of bad compositor behavior (such as compositor crashes/restarts, but also compton has been caught at drawing notification icons above the screen locker when not using the GLX backend), should prevent compositors from unredirecting as it's 1 pixel smaller than the screen from every side, and should otherwise be harmless, so it's enabled by default. * `XSECURELOCK_DATETIME_FORMAT`: the date format to show. Defaults to the locale settings. (see `man date` for possible formats) * `XSECURELOCK_DEBUG_ALLOW_LOCKING_IF_INEFFECTIVE`: Normally we don't allow locking sessions that are likely not any useful to lock, such as the X11 part of a Wayland session (one could still use Wayland applicatione when locked) or VNC sessions (as it'd only lock the server side session while users will likely think they locked the client, allowing for an easy escape). These checks can be bypassed by setting this variable to 1. Not recommended other than for debugging XSecureLock itself via such connections. * `XSECURELOCK_DEBUG_WINDOW_INFO`: When complaining about another window misbehaving, print not just the window ID but also some info about it. Uses the `xwininfo` and `xprop` tools. * `XSECURELOCK_DIM_ALPHA`: Linear-space opacity to fade the screen to. * `XSECURELOCK_DIM_COLOR`: X11 color to fade the screen to. * `XSECURELOCK_DIM_FPS`: Target framerate to attain during the dimming effect of `dimmer`. Ideally matches the display refresh rate. * `XSECURELOCK_DIM_MAX_FILL_SIZE`: Maximum size (in width or height) to fill at once using an XFillRectangle call. Low values may cause performance loss or noticeable tearing during dimming; high values may cause crashes or hangs with some graphics drivers or a temporarily unresponsive X server. * `XSECURELOCK_DIM_OVERRIDE_COMPOSITOR_DETECTION`: When set to 1, always try to use transparency for dimming; when set to 0, always use a dither pattern. Default is to autodetect whether transparency will likely work. * `XSECURELOCK_DIM_TIME_MS`: Milliseconds to dim for when above xss-lock command line with `dimmer` is used; also used by `wait_nonidle` to know when to assume dimming and waiting has finished and exit. * `XSECURELOCK_DISCARD_FIRST_KEYPRESS`: If set to 0, the key pressed to stop the screen saver and spawn the auth child is sent to the auth child (and thus becomes part of the password entry). By default we always discard the key press that started the authentication flow, to prevent users from getting used to type their password on a blank screen (which could be just powered off and have a chat client behind or similar). * `XSECURELOCK_FONT`: X11 or FontConfig font name to use for `auth_x11`. You can get a list of supported font names by running `xlsfonts` and `fc-list`. * `XSECURELOCK_FORCE_GRAB`: When grabbing fails, try stealing the grab from other windows (a value of `2` steals from all descendants of the root window, while a value of `1` only steals from client windows). This works only sometimes and is incompatible with many window managers, so use with care. See the "Forcing Grabs" section below for details. * `XSECURELOCK_GLOBAL_SAVER`: specifies the desired global screen saver module (by default this is a multiplexer that runs `XSECURELOCK_SAVER` on each screen). * `XSECURELOCK_IDLE_TIMERS`: comma-separated list of idle time counters used by `until_nonidle`. Typical values are either empty (relies on the X Screen Saver extension instead), "IDLETIME" and "DEVICEIDLETIME " where n is an XInput device index (run `xinput` to see them). If multiple time counters are specified, the idle time is the minimum of them all. All listed timers must have the same unit. * `XSECURELOCK_IMAGE_DURATION_SECONDS`: how long to show each still image played by `saver_mpv`. Defaults to 1. * `XSECURELOCK_KEY_%s_COMMAND` where `%s` is the name of an X11 keysym (find using `xev`): a shell command to execute when the specified key is pressed. Useful e.g. for media player control. Beware: be cautious about what you run with this, as it may yield attackers control over your computer. * `XSECURELOCK_LIST_VIDEOS_COMMAND`: shell command to list all video files to potentially play by `saver_mpv` or `saver_mplayer`. Defaults to `find ~/Videos -type f`. * `XSECURELOCK_NO_COMPOSITE`: disables covering the composite overlay window. This switches to a more traditional way of locking, but may allow desktop notifications to be visible on top of the screen lock. Not recommended. * `XSECURELOCK_NO_PAM_RHOST`: do not set `PAM_RHOST` to `localhost`, despite [recommendation](http://www.linux-pam.org/Linux-PAM-html/adg-security-user-identity.html) to do so by the Linux-PAM Application Developers' Guide. This may work around bugs in third-party PAM authentication modules. If this solves a problem for you, please report a bug against said PAM module. * `XSECURELOCK_NO_XRANDR`: disables multi monitor support using XRandR. * `XSECURELOCK_NO_XRANDR15`: disables multi monitor support using XRandR 1.5 and fall back to XRandR 1.2. Not recommended. * `XSECURELOCK_PAM_SERVICE`: pam service name. You should have a file with that name in `/etc/pam.d`. * `XSECURELOCK_PASSWORD_PROMPT`: Choose password prompt mode: * `asterisks`: shows asterisks, like classic password prompts. This is the least secure option because password length is visible. ***_ *******_ * `cursor`: shows a cursor that jumps around on each key press. This is the default. ________|_______________________ ___________________|____________ * `disco`: shows dancers, which dance around on each key press. Requires a font that can handle Unicode line drawing characters, and FontConfig. ┏(・o・)┛ ♪ ┗(・o・)┓ ♪ ┏(・o・)┛ ♪ ┗(・o・)┓ ♪ ┏(・o・)┛ ┗(・o・)┓ ♪ ┏(・o・)┛ ♪ ┏(・o・)┛ ♪ ┏(・o・)┛ ♪ ┏(・o・)┛ * `emoji`: shows an emoji, changing which one on each key press. Requires a font that can handle emoji, and FontConfig. 👍 🎶 💕 * `emoticon`: shows an ascii emoticon, changing which one on each key press. :-O d-X X-\ * `hidden`: completely hides the password, and there's no feedback for keypresses. This would almost be most secure - however as it gives no feedback to input whatsoever, you may not be able to notice accidentally typing to another computer and sending your password to some chatroom. ``` ``` * `kaomoji`: shows a kaomoji (Japanese emoticon), changing which one on each key press. Requires a Japanese font, and FontConfig. (͡°͜ʖ͡°) (^u^) ¯\_(ツ)_/¯ * `time`: shows the current time since the epoch on each keystroke. This may be the most secure mode, as it gives feedback to keystroke based exclusively on public information, and does not carry over any state between keystrokes whatsoever - not even some form of randomness. 1559655410.922329 * `time_hex`: same as `time`, but in microseconds and hexadecimal. "Because we can". 0x58a7f92bd7359 * `XSECURELOCK_SAVER`: specifies the desired screen saver module. * `XSECURELOCK_SAVER_RESET_ON_AUTH_CLOSE`: specifies whether to reset the saver module when the auth dialog closes. Resetting is done by sending `SIGUSR1` to the saver, which may either just terminate, or handle this specifically to do a cheaper reset. * `XSECURELOCK_SHOW_DATETIME`: whether to show local date and time on the login. Disabled by default. * `XSECURELOCK_SHOW_HOSTNAME`: whether to show the hostname on the login screen of `auth_x11`. Possible values are 0 for not showing the hostname, 1 for showing the short form, and 2 for showing the long form. * `XSECURELOCK_SHOW_KEYBOARD_LAYOUT`: whether to show the name of the current keyboard layout. Enabled by default. * `XSECURELOCK_SHOW_USERNAME`: whether to show the username on the login screen of `auth_x11`. * `XSECURELOCK_SINGLE_AUTH_WINDOW`: whether to show only a single auth window from `auth_x11`, as opposed to one per screen. * `XSECURELOCK_SWITCH_USER_COMMAND`: shell command to execute when `Win-O` or `Ctrl-Alt-O` are pressed (think "_other_ user"). Typical values could be `lxdm -c USER_SWITCH`, `dm-tool switch-to-greeter`, `gdmflexiserver` or `kdmctl reserve`, depending on your desktop environment. * `XSECURELOCK_VIDEOS_FLAGS`: flags to append when invoking mpv/mplayer with `saver_mpv` or `saver_mplayer`. Defaults to empty. * `XSECURELOCK_WAIT_TIME_MS`: Milliseconds to wait after dimming (and before locking) when above xss-lock command line is used. Should be at least as large as the period time set using "xset s". Also used by `wait_nonidle` to know when to assume dimming and waiting has finished and exit. * `XSECURELOCK_SAVER_DELAY_MS`: Milliseconds to wait after starting children process and before mapping windows to let children be ready to display and reduce the black flash. * `XSECURELOCK_SAVER_STOP_ON_BLANK`: specifies if saver is stopped when screen is blanked (DPMS or XSS) to save power. * `XSECURELOCK_XSCREENSAVER_PATH`: Location where XScreenSaver hacks are installed for use by `saver_xscreensaver`. Additionally, command line arguments following a "--" argument will be executed via `execvp` once locking is successful; this can be used to notify a calling process of successful locking. # Authentication Modules The following authentication modules are included: * `auth_x11`: Authenticates via an authproto module using keyboard input (X11 based; recommended). ## Writing Your Own Module The authentication module is a separate executable, whose name must start with `auth_` and be installed together with the included `auth_` modules (default location: `/usr/local/libexec/xsecurelock/helpers`). * Input: it may receive keystroke input from standard input in a locale-dependent multibyte encoding (usually UTF-8). Use the `mb*` C functions to act on these. * Output: it may draw on or create windows below `$XSCREENSAVER_WINDOW`. * Exit status: if authentication was successful, it must return with status zero. If it returns with any other status (including e.g. a segfault), XSecureLock assumes failed authentication. * It is recommended that it shall spawn the configured authentication protocol module and let it do the actual authentication; that way the authentication module can focus on the user interface alone. # Authentication Protocol Modules The following authentication protocol ("authproto") modules are included: * `authproto_htpasswd`: Authenticates via a htpasswd style file stored in `~/.xsecurelock.pw`. To generate this file, run: `( umask 077; htpasswd -cB ~/.xsecurelock.pw "$USER" )` Use this only if you for some reason can't use PAM! * `authproto_pam`: Authenticates via PAM. Use this. * `authproto_pamtester`: Authenticates via PAM using pamtester. Shouldn't be required unless you can't compile `authproto_pam`. Only supports simple password based conversations. ## Writing Your Own Module The authentication protocol module is a separate executable, whose name must start with `authproto_` and be installed together with the included `authproto_` modules (default location: `/usr/local/libexec/xsecurelock/helpers`). * Input: in response to some output messages, it may receive authproto messages. See helpers/authproto.h for details. * Output: it should output authproto messages; see helpers/authproto.h for details. * Exit status: if authentication was successful, it must return with status zero. If it returns with any other status (including e.g. a segfault), XSecureLock assumes failed authentication. # Screen Saver Modules The following screen saver modules are included: * `saver_blank`: Simply blanks the screen. * `saver_mplayer` and `saver_mpv`: Plays a video using mplayer or mpv, respectively. The video to play is selected at random among all files in `~/Videos`. * `saver_multiplex`: Watches the display configuration and runs another screen saver module once on each screen; used internally. * `saver_xscreensaver`: Runs an XScreenSaver hack from an existing XScreenSaver setup. NOTE: some screen savers included by this may display arbitrary pictures from your home directory; if you care about this, either run `xscreensaver-demo` and disable screen savers that may do this, or stay away from this one! ## Writing Your Own Module The screen saver module is a separate executable, whose name must start with `saver_` and be installed together with the included `auth_` modules (default location: `/usr/local/libexec/xsecurelock/helpers`). * Input: receives the 0-based index of the screen saver (remember: one saver is started per display by the multiplexer) via `$XSCREENSAVER_SAVER_INDEX`. * Output: it may draw on or create windows below `$XSCREENSAVER_WINDOW`. * Exit condition: the saver child will receive SIGTERM when the user wishes to unlock the screen. It should exit promptly. * Reset condition: the saver child will receive SIGUSR1 when the auth dialog is closed and `XSECURELOCK_SAVER_RESET_ON_AUTH_CLOSE`. # Security Design In order to achieve maximum possible security against screen lock bypass exploits, the following measures are taken: * Authentication dialog, authentication checking and screen saving are done using separate processes. Therefore a crash of these processes will not unlock the screen, which means that these processes are allowed to do "possibly dangerous" things. * This also means that on operating systems where authentication checking requires special privileges (such as FreeBSD), only that module can be set to run at elevated privileges, unlike most other screen lockers which in this scenario also run graphical user interface code as root. * The main process is kept minimal and only uses C, POSIX and X11 APIs. This limits the possible influence from bugs in external libraries, and allows for easy auditing. * The main process regularly refreshes the screen grabs in case they get lost for whatever reason. * The main process regularly brings its window to the front, to avoid leaking information from notification messages that are OverrideRedirect. * The main process resizes its window to the size of the root window, should the root window size change, to avoid leaking information by attaching a secondary display. * The main processes uses only a single buffer - to hold a single keystroke. Therefore it is impossible to exploit a buffer overrun in the main process by e.g. an overlong password entry. * The only exit conditions of the program is the Authentication Module returning with exit status zero, on which xsecurelock itself will return with status zero; therefore especially security-conscious users might want to run it as `sh -c "xsecurelock ... || kill -9 -1"` :) # Known Security Issues * Locking the screen will fail while other applications already have a keyboard or pointer grab open (for example while running a fullscreen game, or after opening a context menu). This will be noticeable as the screen will not turn black and should thus usually not be an issue - however when relying on automatic locking via `xss-lock`, this could leave a workstation open for days. Above `... || kill -9 -1` workaround would mitigate this issue too by simply killing the entire session if locking it fails. * As XSecureLock relies on an event notification after a screen configuration change, window content may be visible for a short time after attaching a monitor. No usual interaction with applications should be possible though. On desktop systems where monitors are usually not hotplugged, I'd recommend [turning off automatic screen reconfiguration](http://tech.draiser.net/2015/07/14/ignoring-hotplug-monitor-events-on-arch-linux/). * XSecureLock relies on a keyboard and pointer grab in order to prevent other applications from receiving keyboard events (and thus an unauthorized user from controlling the machine). However, there are various other ways for applications - in particular games - to receive input: * Polling current keyboard status (`XQueryKeymap`). * Polling current mouse position (`XQueryPointer`). * Receiving input out-of-band (`/dev/input`), including other input devices than keyboard and mouse, such as gamepads or joysticks. Most these issues are inherent with X11 and can only really be fixed by migrating to an alternative such as Wayland; some of the issues (in particular the gamepad input issue) will probably persist even with Wayland. ## Forcing Grabs As a workaround to the issue of another window already holding a grab, we offer an `XSECURELOCK_FORCE_GRAB` option. This adds a last measure attempt to force grabbing by iterating through all subwindows of the root window, unmapping them (which closes down their grabs), then taking the grab and mapping them again. This has the following known issues: * Grabs owned by the root window cannot be closed down this way. However, only screen lockers and fullscreen games should be doing that. * If the grab was owned by a full screen window (e.g. a game using `OverrideRedirect` to gain fullscreen mode), the window will become unresponsive, as your actions will be interpreted by another window - which you can't see - instead. Alt-Tabbing around may often work around this. * If the grab was owned by a context menu, it may become impossible to close the menu other than by selecting an item in it. * It will also likely confuse window managers: * Probably all window managers will rearrange the windows in response to this. * Cinnamon (and probably other GNOME-derived WMs) may become unresponsive and needs to be restarted. * As a mitigation we try to hit only client windows - but then we lose the ability of closing down window manager owned grabs. * Negative side effects as described are still likely to happen in case the measure fails. # Known Compatibility Issues * There is an open issue with the NVidia graphics driver in conjunction with some compositors. Workarounds include switching to the `nouveau` graphics driver, using a compositor that uses the Composite Overlay Window (e.g. `compton` with the flags `--backend glx --paint-on-overlay`) or passing `XSECURELOCK_NO_COMPOSITE=1` to XSecureLock (which however may make notifications appear on top of the screen lock). * XSecureLock is incompatible with the compositor built into `metacity` (a GNOME component) because it draws on the Compositor Overlay Window with `IncludeInferiors` set (i.e. it explicitly requests to draw on top of programs like XSecureLock). It likely does this because the same is necessary when drawing on top of the root window, which it had done in the past but no longer does. Workarounds include disabling its compositor with `gsettings set org.gnome.metacity compositing-manager false` or passing `XSECURELOCK_NO_COMPOSITE=1` to XSecureLock. * Picom doesn't remove windows in the required order causing a window with the text "INCOMPATIBLE COMPOSITOR, PLEASE FIX!" to be displayed. To fix this you can disable composite obscurer with `XSECURELOCK_COMPOSITE_OBSCURER=0` to stop the window from being drawn all together. * In general, most compositor issues will become visible in form of a text "INCOMPATIBLE COMPOSITOR, PLEASE FIX!" being displayed. A known good compositor is `compton --backend glx --paint-on-overlay`. In worst case you can turn off our workaround for transparent windows by setting `XSECURELOCK_NO_COMPOSITE=1`. # License The code is released under the Apache 2.0 license. See the LICENSE file for more details. This project is not an official Google project. It is not supported by Google and Google specifically disclaims all warranties as to its quality, merchantability, or fitness for a particular purpose. xsecurelock-1.9.0/ensure-documented-settings.sh0000755000175000017500000000415013757234024016623 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Simple script to ensure all settings variables are documented. # List all settings (usually from Get*Settings call). all_settings=$( for file in *.[ch] */*.[ch] */auth_* */saver_*; do <"$file" perl -ne ' print "$_\n" for /\bXSECURELOCK_[A-Za-z0-9_%]+\b/g; ' done | sort -u ) # List of internal settings. These shall not be documented. internal_settings=' XSECURELOCK_INSIDE_SAVER_MULTIPLEX ' # List of deprecated settings. These shall not be documented. deprecated_settings=' XSECURELOCK_PARANOID_PASSWORD XSECURELOCK_SHOW_LOCKS_AND_LATCHES XSECURELOCK_WANT_FIRST_KEYPRESS ' public_settings=$( { echo "$all_settings" echo "$internal_settings" echo "$internal_settings" echo "$deprecated_settings" echo "$deprecated_settings" } | sort | uniq -u ) # List all documented settings. documented_settings=$( // for Window #include // for Display #include // for size_t typedef struct { int x, y, width, height; } Monitor; /*! \brief Queries the current monitor configuration. * * Note: out_monitors will be zero padded and sorted in some deterministic order * so memcmp can be used to check if the monitor configuration has actually * changed. * * \param dpy The current display. * \param w The window this application intends to draw in. * \param out_monitors A pointer to an array that will receive the monitor * configuration (in coordinates relative and clipped to the window w. * \param max_monitors The size of the array. * \return The number of monitors returned in the array. */ size_t GetMonitors(Display* dpy, Window window, Monitor* out_monitors, size_t max_monitors); /*! \brief Enable receiving monitor change events for the given display at w. */ void SelectMonitorChangeEvents(Display* dpy, Window window); /*! \brief Returns the event type that indicates a change to the monitor * configuration. * * \param dpy The current display. * \param type The received event type. * * \returns 1 if the received event is a monitor change event and GetMonitors * should be called, or 0 otherwise. */ int IsMonitorChangeEvent(Display* dpy, int type); #endif xsecurelock-1.9.0/helpers/dimmer.c0000644000175000017500000003317413757234024014073 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /*! *\brief Screen dimmer. * *A simple tool to dim the screen, then wait a little so a screen locker can *take over. * *Sample usage: * xset s 300 2 * xss-lock -n dim-screen -l xsecurelock */ #include // for Window, Atom, CopyFromParent, GCForegr... #include // for XA_CARDINAL #include // for Display, XColor, XSetWindowAttributes #include // for pow, ceil, frexp, nextafter, sqrt #include // for NULL, snprintf #include // for abort #include // for nanosleep, timespec #include "../env_settings.h" // for GetIntSetting, GetDoubleSetting, GetSt... #include "../logging.h" // for Log #include "../wm_properties.h" // for SetWMProperties // Get the entry of value index of the Bayer matrix for n = 2^power. void Bayer(int index, int power, int *x, int *y) { // M_1 = [1]. if (power == 0) { *x = 0; *y = 0; return; } // M_{2n} = [[4Mn 4M_n+2] [4M_n+3 4M_n+1]] int subx, suby; Bayer(index >> 2, power - 1, &subx, &suby); int n = 1 << (power - 1); switch (index & 3) { case 0: *x = subx; *y = suby; break; case 1: *x = subx + n; *y = suby + n; break; case 2: *x = subx + n; *y = suby; break; case 3: *x = subx; *y = suby + n; break; default: // Logically impossible, but clang-analyzer needs help here. abort(); break; } } int HaveCompositor(Display *display) { char buf[64]; int buflen = snprintf(buf, sizeof(buf), "_NET_WM_CM_S%d", (int)DefaultScreen(display)); if (buflen <= 0 || (size_t)buflen >= sizeof(buf)) { Log("Wow, pretty long screen number you got there"); return 0; } Atom atom = XInternAtom(display, buf, False); return XGetSelectionOwner(display, atom) != None; } int dim_time_ms; int wait_time_ms; double dim_fps; double dim_alpha; XColor dim_color; struct DimEffect { void (*PreCreateWindow)(void *self, Display *display, XSetWindowAttributes *dimattrs, unsigned long *dimmask); void (*PostCreateWindow)(void *self, Display *display, Window dim_window); void (*DrawFrame)(void *self, Display *display, Window dim_window, int frame, int w, int h); int frame_count; }; struct DitherEffect { struct DimEffect super; int pattern_power; int pattern_frames; int max_fill_size; Pixmap pattern; XGCValues gc_values; GC dim_gc, pattern_gc; }; void DitherEffectPreCreateWindow(void *unused_self, Display *unused_display, XSetWindowAttributes *unused_dimattrs, unsigned long *unused_dimmask) { (void)unused_self; (void)unused_display; (void)unused_dimattrs; *unused_dimmask = *unused_dimmask; // Shut up clang-analyzer. } void DitherEffectPostCreateWindow(void *self, Display *display, Window dim_window) { struct DitherEffect *dimmer = self; // Create a pixmap to define the pattern we want to set as the window shape. dimmer->gc_values.foreground = 0; dimmer->pattern = XCreatePixmap(display, dim_window, 1 << dimmer->pattern_power, 1 << dimmer->pattern_power, 1); dimmer->pattern_gc = XCreateGC(display, dimmer->pattern, GCForeground, &dimmer->gc_values); XFillRectangle(display, dimmer->pattern, dimmer->pattern_gc, 0, 0, 1 << dimmer->pattern_power, 1 << dimmer->pattern_power); XSetForeground(display, dimmer->pattern_gc, 1); // Create a pixmap to define the shape of the screen-filling window (which // will increase over time). dimmer->gc_values.fill_style = FillStippled; dimmer->gc_values.foreground = dim_color.pixel; dimmer->gc_values.stipple = dimmer->pattern; dimmer->dim_gc = XCreateGC(display, dim_window, GCFillStyle | GCForeground | GCStipple, &dimmer->gc_values); } void DitherEffectDrawFrame(void *self, Display *display, Window dim_window, int frame, int w, int h) { struct DitherEffect *dimmer = self; // Move the pattern forward to the next display frame. One display frame can // have multiple pattern frames. int start_pframe = frame * dimmer->pattern_frames / dimmer->super.frame_count; int end_pframe = (frame + 1) * dimmer->pattern_frames / dimmer->super.frame_count; for (int pframe = start_pframe; pframe < end_pframe; ++pframe) { int x, y; Bayer(pframe, dimmer->pattern_power, &x, &y); XDrawPoint(display, dimmer->pattern, dimmer->pattern_gc, x, y); } // Draw the pattern on the window. XChangeGC(display, dimmer->dim_gc, GCStipple, &dimmer->gc_values); // But do it in some sub-rectangles to be easier on the X server on large // screens. for (int y = 0; y < h; y += dimmer->max_fill_size) { int hh = h - y; if (hh > dimmer->max_fill_size) { hh = dimmer->max_fill_size; } for (int x = 0; x < w; x += dimmer->max_fill_size) { int ww = w - x; if (ww > dimmer->max_fill_size) { ww = dimmer->max_fill_size; } XFillRectangle(display, dim_window, dimmer->dim_gc, x, y, ww, hh); // We must flush here, or Xlib will coaelesce the rectangles to a single // call, still keeping processing time per request on the X server // potentially high. XFlush(display); } } } void DitherEffectInit(struct DitherEffect *dimmer, Display *unused_display) { (void)unused_display; // Ensure dimming at least at a defined frame rate. dimmer->pattern_power = 3; // Total time of effect if we wouldn't stop after dim_alpha of fading out. double total_time_ms = dim_time_ms / dim_alpha; // Minimum "total" frame count of the animation. double total_frames_min = total_time_ms / 1000.0 * dim_fps; // This actually computes ceil(log2(sqrt(total_frames_min))) but cannot fail. (void)frexp(sqrt(total_frames_min), &dimmer->pattern_power); // Clip extreme/unsupported values. if (dimmer->pattern_power < 2) { dimmer->pattern_power = 2; } if (dimmer->pattern_power > 8) { dimmer->pattern_power = 8; } // Generate the frame count and vtable. dimmer->pattern_frames = ceil(pow(1 << dimmer->pattern_power, 2) * dim_alpha); dimmer->super.frame_count = ceil(dim_time_ms * dim_fps / 1000.0); // Limit the pattern fill size. int max_fill_size = GetIntSetting("XSECURELOCK_DIM_MAX_FILL_SIZE", 2048); int max_fill_patterns = max_fill_size >> dimmer->pattern_power; if (max_fill_patterns == 0) { max_fill_patterns = 1; } dimmer->max_fill_size = max_fill_patterns << dimmer->pattern_power; dimmer->super.PreCreateWindow = DitherEffectPreCreateWindow; dimmer->super.PostCreateWindow = DitherEffectPostCreateWindow; dimmer->super.DrawFrame = DitherEffectDrawFrame; } struct OpacityEffect { struct DimEffect super; Atom property_atom; double dim_color_brightness; }; void OpacityEffectPreCreateWindow(void *unused_self, Display *unused_display, XSetWindowAttributes *dimattrs, unsigned long *dimmask) { (void)unused_self; (void)unused_display; dimattrs->background_pixel = dim_color.pixel; *dimmask |= CWBackPixel; } void OpacityEffectPostCreateWindow(void *self, Display *display, Window dim_window) { struct OpacityEffect *dimmer = self; long value = 0; XChangeProperty(display, dim_window, dimmer->property_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); } double sRGBToLinear(double value) { return (value <= 0.04045) ? value / 12.92 : pow((value + 0.055) / 1.055, 2.4); } double LinearTosRGB(double value) { return (value <= 0.0031308) ? 12.92 * value : 1.055 * pow(value, 1.0 / 2.4) - 0.055; } void OpacityEffectDrawFrame(void *self, Display *display, Window dim_window, int frame, int unused_w, int unused_h) { struct OpacityEffect *dimmer = self; (void)unused_w; (void)unused_h; // Calculate the linear-space alpha we want to be fading to. double linear_alpha = (frame + 1) * dim_alpha / dimmer->super.frame_count; double linear_min = linear_alpha * dimmer->dim_color_brightness; double linear_max = linear_alpha * dimmer->dim_color_brightness + (1.0 - linear_alpha); // Calculate the sRGB-space alpha we thus must select to get the same color // range. double srgb_min = LinearTosRGB(linear_min); double srgb_max = LinearTosRGB(linear_max); double srgb_alpha = 1.0 - (srgb_max - srgb_min); // Note: this may have a different brightness level, here we're simply // solving for the same contrast as the "dither" mode. // Log("Got: [%f..%f], want: [%f..%f]", // srgb_alpha * LinearTosRGB(dimmer->dim_color_brightness), // srgb_alpha * LinearTosRGB(dimmer->dim_color_brightness) + // (1.0 - srgb_alpha), // srgb_min, srgb_max); // Convert to an opacity value. long value = nextafter(0xffffffff, 0) * srgb_alpha; XChangeProperty(display, dim_window, dimmer->property_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); // Make it actually visible. XFlush(display); } void OpacityEffectInit(struct OpacityEffect *dimmer, Display *display) { dimmer->property_atom = XInternAtom(display, "_NET_WM_WINDOW_OPACITY", False); dimmer->dim_color_brightness = sRGBToLinear(dim_color.red / 65535.0) * 0.2126 + sRGBToLinear(dim_color.green / 65535.0) * 0.7152 + sRGBToLinear(dim_color.blue / 65535.0) * 0.0722; // Generate the frame count and vtable. dimmer->super.frame_count = ceil(dim_time_ms * dim_fps / 1000.0); dimmer->super.PreCreateWindow = OpacityEffectPreCreateWindow; dimmer->super.PostCreateWindow = OpacityEffectPostCreateWindow; dimmer->super.DrawFrame = OpacityEffectDrawFrame; } int main(int argc, char **argv) { Display *display = XOpenDisplay(NULL); if (display == NULL) { Log("Could not connect to $DISPLAY"); return 1; } Window root_window = DefaultRootWindow(display); // Load global settings. dim_time_ms = GetIntSetting("XSECURELOCK_DIM_TIME_MS", 2000); wait_time_ms = GetIntSetting("XSECURELOCK_WAIT_TIME_MS", 5000); dim_fps = GetDoubleSetting( "XSECURELOCK_DIM_FPS", GetDoubleSetting("XSECURELOCK_" /* REMOVE IN v2 */ "DIM_MIN_FPS", 60)); dim_alpha = GetDoubleSetting("XSECURELOCK_DIM_ALPHA", 0.875); int have_compositor = GetIntSetting( "XSECURELOCK_DIM_OVERRIDE_COMPOSITOR_DETECTION", HaveCompositor(display)); if (dim_alpha <= 0 || dim_alpha > 1) { Log("XSECURELOCK_DIM_ALPHA must be in ]0..1] - using default"); dim_alpha = 0.875; } // Prepare the background color. Colormap colormap = DefaultColormap(display, DefaultScreen(display)); const char *color_name = GetStringSetting("XSECURELOCK_DIM_COLOR", "black"); XParseColor(display, colormap, color_name, &dim_color); if (XAllocColor(display, colormap, &dim_color)) { // Log("Allocated color %lu = %d %d %d", dim_color.pixel, dim_color.red, // dim_color.green, dim_color.blue); } else { dim_color.pixel = BlackPixel(display, DefaultScreen(display)); XQueryColor(display, colormap, &dim_color); Log("Could not allocate color or unknown color name: %s", color_name); } // Set up the filter. struct DitherEffect dither_dimmer; struct OpacityEffect opacity_dimmer; struct DimEffect *dimmer; if (have_compositor) { OpacityEffectInit(&opacity_dimmer, display); dimmer = &opacity_dimmer.super; } else { DitherEffectInit(&dither_dimmer, display); dimmer = &dither_dimmer.super; } // Create a simple screen-filling window. int w = DisplayWidth(display, DefaultScreen(display)); int h = DisplayHeight(display, DefaultScreen(display)); XSetWindowAttributes dimattrs = {0}; dimattrs.save_under = 1; dimattrs.override_redirect = 1; unsigned long dimmask = CWSaveUnder | CWOverrideRedirect; dimmer->PreCreateWindow(dimmer, display, &dimattrs, &dimmask); Window dim_window = XCreateWindow(display, root_window, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, dimmask, &dimattrs); // Not using the xsecurelock WM_CLASS here as this window shouldn't prevent // forcing grabs. SetWMProperties(display, dim_window, "xsecurelock-dimmer", "dim", argc, argv); dimmer->PostCreateWindow(dimmer, display, dim_window); // Precalculate the sleep time per step. unsigned long long sleep_time_ns = (dim_time_ms * 1000000ULL) / dimmer->frame_count; struct timespec sleep_ts; sleep_ts.tv_sec = sleep_time_ns / 1000000000; sleep_ts.tv_nsec = sleep_time_ns % 1000000000; XMapRaised(display, dim_window); for (int i = 0; i < dimmer->frame_count; ++i) { // Advance the dim pattern by one step. dimmer->DrawFrame(dimmer, display, dim_window, i, w, h); // Sleep a while. Yes, even at the end now - we want the user to see this // after all. nanosleep(&sleep_ts, NULL); } // Wait a bit at the end (to hand over to the screen locker without // flickering). sleep_ts.tv_sec = wait_time_ms / 1000; sleep_ts.tv_nsec = (wait_time_ms % 1000) * 1000000L; nanosleep(&sleep_ts, NULL); return 0; } xsecurelock-1.9.0/helpers/authproto.c0000644000175000017500000001132513757234024014635 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "authproto.h" #include // for errno #include // for snprintf #include // for malloc, size_t #include // for strlen #include // for read, write, ssize_t #include "../logging.h" // for LogErrno, Log #include "../mlock_page.h" // for MLOCK_PAGE #include "../util.h" // for explicit_bzero static size_t WriteChars(int fd, const char *buf, size_t n) { size_t total = 0; while (total < n) { ssize_t got = write(fd, buf + total, n - total); if (got < 0) { LogErrno("write"); return 0; } if (got == 0) { Log("write: could not write anything, send buffer full"); return 0; } if ((size_t)got > n - total) { Log("write: overlong write (should never happen)"); } total += got; } return total; } void WritePacket(int fd, char type, const char *message) { size_t len_s = strlen(message); if (len_s >= 0xFFFF) { Log("overlong message, cannot write (hardcoded limit)"); return; } int len = len_s; if (len < 0 || (size_t)len != len_s) { Log("overlong message, cannot write (does not fit in int)"); return; } char prefix[16]; int prefixlen = snprintf(prefix, sizeof(prefix), "%c %d\n", type, len); if (prefixlen <= 0 || (size_t)prefixlen >= sizeof(prefix)) { Log("overlong prefix, cannot write"); return; } // Yes, we're wasting syscalls here. This doesn't need to be fast though, and // this way we can avoid an extra buffer. if (!WriteChars(fd, prefix, prefixlen)) { return; } if (len != 0 && !WriteChars(fd, message, len)) { return; } if (!WriteChars(fd, "\n", 1)) { return; } } static size_t ReadChars(int fd, char *buf, size_t n, int eof_permitted) { size_t total = 0; while (total < n) { ssize_t got = read(fd, buf + total, n - total); if (got < 0) { LogErrno("read"); return 0; } if (got == 0) { if (!eof_permitted) { Log("read: unexpected end of file"); return 0; } break; } if ((size_t)got > n - total) { Log("read: overlong read (should never happen)"); } total += got; } return total; } char ReadPacket(int fd, char **message, int eof_permitted) { char type; *message = NULL; if (!ReadChars(fd, &type, 1, eof_permitted)) { return 0; } if (type == 0) { Log("invalid packet type 0"); return 0; } char c; if (!ReadChars(fd, &c, 1, 0)) { return 0; } if (c != ' ') { Log("invalid character after packet type, expecting space"); return 0; } int len = 0; for (;;) { errno = 0; if (!ReadChars(fd, &c, 1, 0)) { return 0; } switch (c) { case '\n': goto have_len; case '0': len = len * 10 + 0; break; case '1': len = len * 10 + 1; break; case '2': len = len * 10 + 2; break; case '3': len = len * 10 + 3; break; case '4': len = len * 10 + 4; break; case '5': len = len * 10 + 5; break; case '6': len = len * 10 + 6; break; case '7': len = len * 10 + 7; break; case '8': len = len * 10 + 8; break; case '9': len = len * 10 + 9; break; default: Log("invalid character during packet length, expecting 0-9 or newline"); return 0; } } have_len: if (len < 0 || len >= 0xFFFF) { Log("invalid length %d", len); return 0; } *message = malloc((size_t)len + 1); if ((type == PTYPE_RESPONSE_LIKE_PASSWORD) && MLOCK_PAGE(*message, len + 1) < 0) { // We continue anyway, as the user being unable to unlock the screen is // worse. LogErrno("mlock"); } if (len != 0 && !ReadChars(fd, *message, len, 0)) { explicit_bzero(*message, len + 1); free(*message); *message = NULL; return 0; } (*message)[len] = 0; if (!ReadChars(fd, &c, 1, 0)) { explicit_bzero(*message, len + 1); free(*message); *message = NULL; return 0; } if (c != '\n') { Log("invalid character after packet message, expecting newline"); return 0; } return type; } xsecurelock-1.9.0/helpers/saver_xscreensaver.in0000755000175000017500000001054113757234024016706 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. : ${XSECURELOCK_XSCREENSAVER_PATH:=@path_to_xscreensaver@} TAB=' ' # Note: the following logic is somewhat derived from parse_screenhack in # XScreenSaver. convert_xscreensaver_programs() { i=0 while IFS= read -r line; do skipwhite() { while :; do case "$line" in $TAB*) line=${line#$TAB} ;; \ *) line=${line# } ;; *) break ;; esac done } skipwhite # Read disabled field. case "$line" in -*) enabled=false; line=${line#-}; skipwhite ;; *) enabled=true ;; esac # Strip visual name (VISUAL:, where VISUAL can't contain " or whitespace). case "${line%%[\" $TAB]*}" in *:*) line=${line#*:}; skipwhite ;; esac # Strip textual description ("description"). case "$line" in '"'*) line=${line#\"*\"}; skipwhite ;; esac # What's remaining is the program name with its options. echo "$i $enabled $line" i=$((i+1)) done } convert_program_list() { i=0 while IFS= read -r line; do echo "$i true $line -root" i=$((i+1)) done } list_savers() { want_all=$1 if [ -f ~/.xscreensaver ]; then printf "%b" "$( xrdb -n ~/.xscreensaver 2>/dev/null |\ grep ^programs: |\ cut -d : -f 2- )" | convert_xscreensaver_programs else ls "$XSECURELOCK_XSCREENSAVER_PATH" | convert_program_list fi | while read -r number enabled saver flags; do $want_all || $enabled || continue [ -x "$XSECURELOCK_XSCREENSAVER_PATH/$saver" ] || continue printf '%d\t%s/%s\n' "$number" "$XSECURELOCK_XSCREENSAVER_PATH" "$saver $flags" done } mode= # Debug mode to list all savers. case "$1" in --list_savers) list_savers false exit 0 ;; --list_all_savers) list_savers true exit 0 ;; --internal-override-mode=*) mode=${1#*=} ;; esac if [ -z "$mode" ]; then mode=$( xrdb -n ~/.xscreensaver 2>/dev/null |\ grep ^mode: |\ cut -f 2 ) fi selected= case "$mode" in one) selected=$( xrdb -n ~/.xscreensaver 2>/dev/null |\ grep ^selected: |\ cut -f 2 ) ;; random) # NOT random-same. # Try bash's $RANDOM, but if it's not there, just use the PID. selected=${RANDOM:-$$} ;; esac if [ -z "$selected" ]; then # Note: random-same hits this. # We're using the parent process ID here, which may be a saver_multiplex # instance. This ensures that multiple instances of this always spawn the same # saver on each screen. selected=$PPID fi # Prepare the saver list so we only parse once. case "$mode" in one) savers=$(list_savers true) count=$(printf '%s\n' "$savers" | wc -l) ;; *) savers=$(list_savers false) count=$(printf '%s\n' "$savers" | wc -l) ;; esac select_saver() { case "$mode" in one) printf '%s\n' "$savers" | grep "^$selected$(printf '\t')" ;; *) printf '%s\n' "$savers" | tail -n +$((selected % count + 1)) ;; esac | head -n 1 | cut -f 2- } # On SIGUSR1, we exit the saver and retry the selection. sigusr1_caught=false trap 'sigusr1_caught=true' USR1 while :; do saver=$(select_saver) if [ -z "$saver" ]; then echo >&2 "xsecurelock: No saver selected. Giving up." exec ./saver_blank fi sigusr1_caught=false eval $saver status=$? if [ $status -eq 0 ] || $sigusr1_caught; then # Immediately try the next saver. case "$mode" in one) ;; *) selected=$((selected + 1)) ;; esac else # Saver failed entirely. Just give up. echo >&2 "xsecurelock: Screen saver failed with status $status: $saver." sleep 2 # Anti-spam delay. if [ x"$mode" != x"random" ]; then # As a fallback, when the saver failed, try random. exec "$0" --internal-override-mode=random fi exit $status fi done xsecurelock-1.9.0/helpers/until_nonidle.c0000644000175000017500000001615613757234024015462 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /*! * \brief Screen dimmer helper. * * A simple tool to run a tool to dim the screen, and - depending on which comes * first: * - On leaving idle status, kill the dimming tool and exit with success status. * - On dimming tool exiting, exit with error status. * * Sample usage: * until_nonidle dim-screen || xsecurelock */ #include // for Window #include // for Display, XOpenDisplay, Default... #include // for sigaction, raise, sigemptyset #include // for uint64_t #include // for NULL, size_t, EXIT_FAILURE #include // for memcpy, NULL, strcmp, strcspn #include // for gettimeofday, timeval #include // for nanosleep, timespec #include // for _exit, execvp, fork, setsid #ifdef HAVE_XSCREENSAVER_EXT #include // for XScreenSaverAllocInfo, XScreen... #endif #ifdef HAVE_XSYNC_EXT #include // for XSyncSystemCounter, XSyncListS... #include // for XSyncValue #endif #include "../env_settings.h" // for GetIntSetting, GetStringSetting #include "../logging.h" // for Log, LogErrno #include "../wait_pgrp.h" // for KillPgrp, WaitPgrp #ifdef HAVE_XSCREENSAVER_EXT int have_xscreensaver_ext; XScreenSaverInfo *saver_info; #endif #ifdef HAVE_XSYNC_EXT int have_xsync_ext; int num_xsync_counters; XSyncSystemCounter *xsync_counters; #endif pid_t childpid = 0; static void HandleSIGTERM(int signo) { if (childpid != 0) { KillPgrp(childpid, signo); // Dirty, but quick. } raise(signo); } uint64_t GetIdleTimeForSingleTimer(Display *display, Window w, const char *timer) { if (*timer == 0) { #ifdef HAVE_XSCREENSAVER_EXT if (have_xscreensaver_ext) { XScreenSaverQueryInfo(display, w, saver_info); return saver_info->idle; } #endif } else { #ifdef HAVE_XSYNC_EXT if (have_xsync_ext) { for (int i = 0; i < num_xsync_counters; ++i) { if (!strcmp(timer, xsync_counters[i].name)) { // I know this is inefficient. XSyncValue value; XSyncQueryCounter(display, xsync_counters[i].counter, &value); return (((uint64_t)XSyncValueHigh32(value)) << 32) | (uint64_t)XSyncValueLow32(value); } } } #endif } Log("Timer \"%s\" not supported", timer); (void)display; (void)w; return (uint64_t)-1; } uint64_t GetIdleTime(Display *display, Window w, const char *timers) { uint64_t min_idle_time = (uint64_t)-1; for (;;) { size_t len = strcspn(timers, ","); if (timers[len] == 0) { // End of string. uint64_t this_idle_time = GetIdleTimeForSingleTimer(display, w, timers); if (this_idle_time < min_idle_time) { min_idle_time = this_idle_time; } return min_idle_time; } char this_timer[64]; if (len < sizeof(this_timer)) { memcpy(this_timer, timers, len); this_timer[len] = 0; uint64_t this_idle_time = GetIdleTimeForSingleTimer(display, w, this_timer); if (this_idle_time < min_idle_time) { min_idle_time = this_idle_time; } } else { Log("Too long timer name - skipping: %s", timers); } timers += len + 1; } } int main(int argc, char **argv) { if (argc <= 1) { Log("Usage: %s program args... - runs the given program until non-idle", argv[0]); Log("Meant to be used with dimming tools, like: %s dimmer || xsecurelock", argv[0]); Log("Returns 0 when no longer idle, and 1 when still idle"); return 1; } int dim_time_ms = GetIntSetting("XSECURELOCK_DIM_TIME_MS", 2000); int wait_time_ms = GetIntSetting("XSECURELOCK_WAIT_TIME_MS", 5000); const char *timers = GetStringSetting("XSECURELOCK_IDLE_TIMERS", #ifdef HAVE_XSCREENSAVER_EXT "" #else "IDLETIME" #endif ); Display *display = XOpenDisplay(NULL); if (display == NULL) { Log("Could not connect to $DISPLAY."); return 1; } Window root_window = DefaultRootWindow(display); // Initialize the extensions. #ifdef HAVE_XSCREENSAVER_EXT have_xscreensaver_ext = 0; int scrnsaver_event_base, scrnsaver_error_base; if (XScreenSaverQueryExtension(display, &scrnsaver_event_base, &scrnsaver_error_base)) { have_xscreensaver_ext = 1; saver_info = XScreenSaverAllocInfo(); } #endif #ifdef HAVE_XSYNC_EXT have_xsync_ext = 0; int sync_event_base, sync_error_base; if (XSyncQueryExtension(display, &sync_event_base, &sync_error_base)) { have_xsync_ext = 1; xsync_counters = XSyncListSystemCounters(display, &num_xsync_counters); } #endif // Capture the initial idle time. uint64_t prev_idle = GetIdleTime(display, root_window, timers); if (prev_idle == (uint64_t)-1) { Log("Could not initialize idle timers. Bailing out."); return 1; } // Start the subprocess. childpid = ForkWithoutSigHandlers(); if (childpid == -1) { LogErrno("fork"); return 1; } if (childpid == 0) { // Child process. StartPgrp(); execvp(argv[1], argv + 1); LogErrno("execvp"); _exit(EXIT_FAILURE); } // Parent process. struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESETHAND; // It re-raises to suicide. sa.sa_handler = HandleSIGTERM; // To kill children. if (sigaction(SIGTERM, &sa, NULL) != 0) { LogErrno("sigaction(SIGTERM)"); } InitWaitPgrp(); struct timeval start_time; gettimeofday(&start_time, NULL); int still_idle = 1; while (childpid != 0) { nanosleep(&(const struct timespec){0, 10000000L}, NULL); // 10ms. uint64_t cur_idle = GetIdleTime(display, root_window, timers); still_idle = cur_idle >= prev_idle; prev_idle = cur_idle; // Also exit when both dim and wait time expire. This allows using // xss-lock's dim-screen.sh without changes. struct timeval current_time; gettimeofday(¤t_time, NULL); int active_ms = (current_time.tv_sec - start_time.tv_sec) * 1000 + (current_time.tv_usec - start_time.tv_usec) / 1000; int should_be_running = still_idle && (active_ms <= dim_time_ms + wait_time_ms); if (!should_be_running) { KillPgrp(childpid, SIGTERM); } int status; WaitPgrp("idle", &childpid, !should_be_running, !should_be_running, &status); } // This is the point where we can exit. return still_idle ? 1 // Dimmer exited - now it's time to lock. : 0; // No longer idle - don't lock. } xsecurelock-1.9.0/helpers/monitors.c0000644000175000017500000002305713757234024014467 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "monitors.h" #include // for XWindowAttributes, Display, XGetW... #include // for qsort #include // for memcmp, memset #ifdef HAVE_XRANDR_EXT #include // for XRRMonitorInfo, XRRCrtcInfo, XRRO... #include // for RANDR_MAJOR, RRNotify, RANDR_MINOR #if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 5) #define HAVE_XRANDR15_EXT #endif #endif #include "../env_settings.h" // for GetIntSetting #include "../logging.h" // for Log #ifdef HAVE_XRANDR_EXT static Display* initialized_for = NULL; static int have_xrandr12_ext; #ifdef HAVE_XRANDR15_EXT static int have_xrandr15_ext; #endif static int event_base; static int error_base; static int MaybeInitXRandR(Display* dpy) { if (dpy == initialized_for) { return have_xrandr12_ext; } have_xrandr12_ext = 0; #ifdef HAVE_XRANDR15_EXT have_xrandr15_ext = 0; #endif if (XRRQueryExtension(dpy, &event_base, &error_base)) { int major, minor; if (XRRQueryVersion(dpy, &major, &minor)) { // XRandR before 1.2 can't connect multiple screens to one, so the // default root window size tracking is sufficient for that. if (major > 1 || (major == 1 && minor >= 2)) { if (!GetIntSetting("XSECURELOCK_NO_XRANDR", 0)) { have_xrandr12_ext = 1; } } #ifdef HAVE_XRANDR15_EXT if (major > 1 || (major == 1 && minor >= 5)) { if (!GetIntSetting("XSECURELOCK_NO_XRANDR15", 0)) { have_xrandr15_ext = 1; } } #endif } } initialized_for = dpy; return have_xrandr12_ext; } #endif #define CLAMP(x, mi, ma) ((x) < (mi) ? (mi) : (x) > (ma) ? (ma) : (x)) static int CompareMonitors(const void* a, const void* b) { return memcmp(a, b, sizeof(Monitor)); } static int IntervalsOverlap(int astart, int asize, int bstart, int bsize) { // Compute exclusive bounds. int aend = astart + asize; int bend = bstart + bsize; // If one interval starts at or after the other, there's no overlap. if (astart >= bend || bstart >= aend) { return 0; } // Otherwise, there must be an overlap. return 1; } static void AddMonitor(Monitor* out_monitors, size_t* num_monitors, size_t max_monitors, int x, int y, int w, int h) { #ifdef DEBUG_EVENTS Log("AddMonitor %d %d %d %d", x, y, w, h); #endif // Too many monitors? Stop collecting them. if (*num_monitors >= max_monitors) { #ifdef DEBUG_EVENTS Log("Skip (too many)"); #endif return; } // Skip empty "monitors". if (w <= 0 || h <= 0) { #ifdef DEBUG_EVENTS Log("Skip (zero)"); #endif return; } // Skip overlapping "monitors" (typically in cloned display setups). for (size_t i = 0; i < *num_monitors; ++i) { if (IntervalsOverlap(x, w, out_monitors[i].x, out_monitors[i].width) && IntervalsOverlap(y, h, out_monitors[i].y, out_monitors[i].height)) { #ifdef DEBUG_EVENTS Log("Skip (overlap with %d)", (int)i); #endif return; } } #ifdef DEBUG_EVENTS Log("Monitor %d = %d %d %d %d", (int)*num_monitors, x, y, w, h); #endif out_monitors[*num_monitors].x = x; out_monitors[*num_monitors].y = y; out_monitors[*num_monitors].width = w; out_monitors[*num_monitors].height = h; ++*num_monitors; } #ifdef HAVE_XRANDR_EXT static int GetMonitorsXRandR12(Display* dpy, Window window, int wx, int wy, int ww, int wh, Monitor* out_monitors, size_t* out_num_monitors, size_t max_monitors) { XRRScreenResources* screenres = XRRGetScreenResources(dpy, window); if (screenres == NULL) { return 0; } for (int i = 0; i < screenres->noutput; ++i) { XRROutputInfo* output = XRRGetOutputInfo(dpy, screenres, screenres->outputs[i]); if (output == NULL) { continue; } if (output->connection == RR_Connected) { // NOTE: If an output has multiple Crtcs (i.e. if the screen is // cloned), we only look at the first. Let's assume that the center // of that one should always be onscreen anyway (even though they // may not be, as cloned displays can have different panning // settings). RRCrtc crtc = (output->crtc ? output->crtc : output->ncrtc ? output->crtcs[0] : 0); XRRCrtcInfo* info = (crtc ? XRRGetCrtcInfo(dpy, screenres, crtc) : 0); if (info != NULL) { int x = CLAMP(info->x, wx, wx + ww) - wx; int y = CLAMP(info->y, wy, wy + wh) - wy; int w = CLAMP(info->x + (int)info->width, wx + x, wx + ww) - (wx + x); int h = CLAMP(info->y + (int)info->height, wy + y, wy + wh) - (wy + y); AddMonitor(out_monitors, out_num_monitors, max_monitors, x, y, w, h); XRRFreeCrtcInfo(info); } } XRRFreeOutputInfo(output); } XRRFreeScreenResources(screenres); return *out_num_monitors != 0; } #ifdef HAVE_XRANDR15_EXT static int GetMonitorsXRandR15(Display* dpy, Window window, int wx, int wy, int ww, int wh, Monitor* out_monitors, size_t* out_num_monitors, size_t max_monitors) { if (!have_xrandr15_ext) { return 0; } int num_rrmonitors; XRRMonitorInfo* rrmonitors = XRRGetMonitors(dpy, window, 1, &num_rrmonitors); if (rrmonitors == NULL) { return 0; } for (int i = 0; i < num_rrmonitors; ++i) { XRRMonitorInfo* info = &rrmonitors[i]; int x = CLAMP(info->x, wx, wx + ww) - wx; int y = CLAMP(info->y, wy, wy + wh) - wy; int w = CLAMP(info->x + info->width, wx + x, wx + ww) - (wx + x); int h = CLAMP(info->y + info->height, wy + y, wy + wh) - (wy + y); AddMonitor(out_monitors, out_num_monitors, max_monitors, x, y, w, h); } XRRFreeMonitors(rrmonitors); return *out_num_monitors != 0; } #endif static int GetMonitorsXRandR(Display* dpy, Window window, const XWindowAttributes* xwa, Monitor* out_monitors, size_t* out_num_monitors, size_t max_monitors) { if (!MaybeInitXRandR(dpy)) { return 0; } // Translate to absolute coordinates so we can compare them to XRandR data. int wx, wy; Window child; if (!XTranslateCoordinates(dpy, window, DefaultRootWindow(dpy), xwa->x, xwa->y, &wx, &wy, &child)) { Log("XTranslateCoordinates failed"); wx = xwa->x; wy = xwa->y; } #ifdef HAVE_XRANDR15_EXT if (GetMonitorsXRandR15(dpy, window, wx, wy, xwa->width, xwa->height, out_monitors, out_num_monitors, max_monitors)) { return 1; } #endif return GetMonitorsXRandR12(dpy, window, wx, wy, xwa->width, xwa->height, out_monitors, out_num_monitors, max_monitors); } #endif static void GetMonitorsGuess(const XWindowAttributes* xwa, Monitor* out_monitors, size_t* out_num_monitors, size_t max_monitors) { // XRandR-less dummy fallback. size_t guessed_monitors = CLAMP((size_t)(xwa->width * 9 + xwa->height * 8) / (size_t)(xwa->height * 16), // 1, max_monitors); for (size_t i = 0; i < guessed_monitors; ++i) { int x = xwa->width * i / guessed_monitors; int y = 0; int w = (xwa->width * (i + 1) / guessed_monitors) - (xwa->width * i / guessed_monitors); int h = xwa->height; AddMonitor(out_monitors, out_num_monitors, max_monitors, x, y, w, h); } } size_t GetMonitors(Display* dpy, Window window, Monitor* out_monitors, size_t max_monitors) { if (max_monitors < 1) { return 0; } size_t num_monitors = 0; // As outputs will be relative to the window, we have to query its attributes. XWindowAttributes xwa; XGetWindowAttributes(dpy, window, &xwa); do { #ifdef HAVE_XRANDR_EXT if (GetMonitorsXRandR(dpy, window, &xwa, out_monitors, &num_monitors, max_monitors)) { break; } #endif GetMonitorsGuess(&xwa, out_monitors, &num_monitors, max_monitors); } while (0); // Sort the monitors in some deterministic order. qsort(out_monitors, num_monitors, sizeof(*out_monitors), CompareMonitors); // Fill the rest with zeros. if (num_monitors < max_monitors) { memset(out_monitors + num_monitors, 0, (max_monitors - num_monitors) * sizeof(*out_monitors)); } return num_monitors; } void SelectMonitorChangeEvents(Display* dpy, Window window) { #ifdef HAVE_XRANDR_EXT if (MaybeInitXRandR(dpy)) { XRRSelectInput(dpy, window, RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputChangeNotifyMask); } #else (void)dpy; (void)window; #endif } int IsMonitorChangeEvent(Display* dpy, int type) { #ifdef HAVE_XRANDR_EXT if (MaybeInitXRandR(dpy)) { switch (type - event_base) { case RRScreenChangeNotify: case RRNotify + RRNotify_CrtcChange: case RRNotify + RRNotify_OutputChange: return 1; default: return 0; } } #else (void)dpy; (void)type; #endif // XRandR-less dummy fallback. return 0; } xsecurelock-1.9.0/helpers/authproto_htpasswd.in0000755000175000017500000000223613757234024016742 00000000000000#!/bin/sh # # Copyright 2018 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. echo "P 15" echo "Enter password:" read -r ptype len case "$ptype" in p) # Trick: reading len+1 characters get the password and the terminating # newline - just what we need here. if head -c "$((len+1))" |\ @path_to_htpasswd@ -v ~/.xsecurelock.pw "$USER" \ >/dev/null 2>&1; then echo "i 11" echo "I know you." exit 0 else echo "e 17" echo "Invalid password." exit 1 fi ;; x) exit 1 ;; *) echo >&2 "Unexpected packet type." exit 1 ;; esac # Shouldn't get here. exit 42 xsecurelock-1.9.0/helpers/saver_mplayer.in0000755000175000017500000000174314134122747015651 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. : ${XSECURELOCK_LIST_VIDEOS_COMMAND:=find ~/Videos -type f} : ${XSECURELOCK_VIDEOS_FLAGS:=} sh -c "$XSECURELOCK_LIST_VIDEOS_COMMAND" | shuf | head -n 256 |\ @path_to_mplayer@ \ -noconsolecontrols \ -really-quiet \ -nostop-xscreensaver \ -heartbeat-cmd "" \ -wid "${XSCREENSAVER_WINDOW}" \ -fixed-vo \ -nosound \ -shuffle \ ${XSECURELOCK_VIDEOS_FLAGS} \ -playlist - xsecurelock-1.9.0/helpers/saver_multiplex.c0000644000175000017500000001200214313046751016021 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include // for Window, CopyFromParent, CWBackPixel #include // for XEvent, XFlush, XNextEvent, XOpenDi... #include // for signal, SIGTERM #include // for fprintf, NULL, stderr #include // for setenv #include // for memcmp, memcpy #include // for select, FD_SET, FD_ZERO, fd_set #include // for sleep #include "../env_settings.h" // for GetStringSetting #include "../logging.h" // for Log, LogErrno #include "../saver_child.h" // for MAX_SAVERS #include "../wait_pgrp.h" // for InitWaitPgrp #include "../wm_properties.h" // for SetWMProperties #include "../xscreensaver_api.h" // for ReadWindowID #include "monitors.h" // for IsMonitorChangeEvent, Monitor, Sele... static void HandleSIGUSR1(int signo) { KillAllSaverChildrenSigHandler(signo); // Dirty, but quick. } static void HandleSIGTERM(int signo) { KillAllSaverChildrenSigHandler(signo); // Dirty, but quick. raise(signo); // Destroys windows we created anyway. } #define MAX_MONITORS MAX_SAVERS static const char* saver_executable; static Display* display; static Monitor monitors[MAX_MONITORS]; static size_t num_monitors; static Window windows[MAX_MONITORS]; static void WatchSavers(void) { for (size_t i = 0; i < num_monitors; ++i) { WatchSaverChild(display, windows[i], i, saver_executable, 1); } } static void SpawnSavers(Window parent, int argc, char* const* argv) { for (size_t i = 0; i < num_monitors; ++i) { windows[i] = XCreateWindow(display, parent, monitors[i].x, monitors[i].y, monitors[i].width, monitors[i].height, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); SetWMProperties(display, windows[i], "xsecurelock", "saver_multiplex_screen", argc, argv); XMapRaised(display, windows[i]); } // Need to flush the display so savers sure can access the window. XFlush(display); WatchSavers(); } static void KillSavers(void) { for (size_t i = 0; i < num_monitors; ++i) { WatchSaverChild(display, windows[i], i, saver_executable, 0); XDestroyWindow(display, windows[i]); } } /*! \brief The main program. * * Usage: XSCREENSAVER_WINDOW=window_id ./saver_multiplex * * Spawns spearate saver subprocesses, one on each screen. */ int main(int argc, char** argv) { if (GetIntSetting("XSECURELOCK_INSIDE_SAVER_MULTIPLEX", 0)) { Log("Starting saver_multiplex inside saver_multiplex?!?"); // If we die, the parent process will revive us, so let's sleep a while to // conserve battery and avoid log spam in this case. sleep(60); return 1; } setenv("XSECURELOCK_INSIDE_SAVER_MULTIPLEX", "1", 1); if ((display = XOpenDisplay(NULL)) == NULL) { Log("Could not connect to $DISPLAY"); return 1; } int x11_fd = ConnectionNumber(display); Window parent = ReadWindowID(); if (parent == None) { Log("Invalid/no parent ID in XSCREENSAVER_WINDOW"); return 1; } saver_executable = GetExecutablePathSetting("XSECURELOCK_SAVER", SAVER_EXECUTABLE, 0); SelectMonitorChangeEvents(display, parent); num_monitors = GetMonitors(display, parent, monitors, MAX_MONITORS); SpawnSavers(parent, argc, argv); struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = HandleSIGUSR1; // To kill children. if (sigaction(SIGUSR1, &sa, NULL) != 0) { LogErrno("sigaction(SIGUSR1)"); } sa.sa_flags = SA_RESETHAND; // It re-raises to suicide. sa.sa_handler = HandleSIGTERM; // To kill children. if (sigaction(SIGTERM, &sa, NULL) != 0) { LogErrno("sigaction(SIGTERM)"); } InitWaitPgrp(); for (;;) { fd_set in_fds; FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); select(x11_fd + 1, &in_fds, 0, 0, NULL); WatchSavers(); XEvent ev; while (XPending(display) && (XNextEvent(display, &ev), 1)) { if (IsMonitorChangeEvent(display, ev.type)) { Monitor new_monitors[MAX_SAVERS]; size_t new_num_monitors = GetMonitors(display, parent, new_monitors, MAX_SAVERS); if (new_num_monitors != num_monitors || memcmp(new_monitors, monitors, sizeof(monitors)) != 0) { KillSavers(); num_monitors = new_num_monitors; memcpy(monitors, new_monitors, sizeof(monitors)); SpawnSavers(parent, argc, argv); } } } } return 0; } xsecurelock-1.9.0/helpers/saver_mpv.in0000755000175000017500000000252614134122747015002 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. : ${XSECURELOCK_LIST_VIDEOS_COMMAND:=find ~/Videos -type f} : ${XSECURELOCK_IMAGE_DURATION_SECONDS:=1} : ${XSECURELOCK_VIDEOS_FLAGS:=} # Run mpv in a loop so we can quickly restart mpv in case it exits (has shown # the last picture/video). while true; do sh -c "$XSECURELOCK_LIST_VIDEOS_COMMAND" | shuf | head -n 256 |\ @path_to_mpv@ \ --no-input-terminal \ --really-quiet \ --no-stop-screensaver \ --wid="${XSCREENSAVER_WINDOW}" \ --no-audio \ --image-display-duration="${XSECURELOCK_IMAGE_DURATION_SECONDS}" \ --shuffle \ --loop-playlist=inf \ ${XSECURELOCK_VIDEOS_FLAGS} \ --playlist=- & # Avoid spinning if mpv exits immediately, but don't wait to restart mpv in # the common case. sleep 1 wait done xsecurelock-1.9.0/helpers/authproto.h0000644000175000017500000000425513757234024014646 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef AUTHPROTO_H #define AUTHPROTO_H // Packet format: // // // // where // // ptype = one of the below characters. // len = message length encoded in decimal ASCII. // message = len bytes that shall be shown to the user. // // By convention, uppercase packet types expect a reply and lowercase packet // types are "terminal". // PAM-to-user messages: #define PTYPE_INFO_MESSAGE 'i' #define PTYPE_ERROR_MESSAGE 'e' #define PTYPE_PROMPT_LIKE_USERNAME 'U' #define PTYPE_PROMPT_LIKE_PASSWORD 'P' // Note: there's no specific message type for successful authentication or // similar; the caller shall use the exit status of the helper only. // User-to-PAM messages: #define PTYPE_RESPONSE_LIKE_USERNAME 'u' #define PTYPE_RESPONSE_LIKE_PASSWORD 'p' #define PTYPE_RESPONSE_CANCELLED 'x' /** * \brief Writes a packet in above form. * * \param fd The file descriptor to write to. * \param type The packet type from above macros. * \param message The message to include with the packet (NUL-terminated). */ void WritePacket(int fd, char type, const char *message); /** * \brief Reads a packet in above form. * * \param fd The file descriptor to write to. * \param message A pointer to store the message (will be mlock()d). * Will always be set if function returns nonzero; caller must free it. * \param eof_permitted If enabled, encountering EOF at the beginning will not * count as an error but return 0 silently. * \return The packet type, or 0 if no packet has been read. Errors are logged. */ char ReadPacket(int fd, char **message, int eof_permitted); #endif xsecurelock-1.9.0/helpers/pgrp_placeholder.c0000644000175000017500000000233213757234024016120 00000000000000/* Copyright 2019 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /*! *\brief Process group placeholder. * * Does nothing except sitting around until killed. Spawned as extra process in * our process groups so that we can control on our own when the process group * ID is reclaimed to the kernel, namely by killing the entire process group. * This prevents a race condition of our process group getting reclaimed before * we try to kill possibly remaining processes in it, after which we would * possibly kill something else. * * Must be a separate executable so F_CLOEXEC applies as intended. */ #include #include int main() { for (;;) { pause(); } // Cannot get here. abort(); } xsecurelock-1.9.0/helpers/authproto_pam.c0000644000175000017500000002013014077246512015465 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include // for NULL, setlocale, LC_CTYPE #include // for pam_end, pam_start, pam_acct_mgmt #include // for free, calloc, exit, getenv #include // for strchr #include "../env_info.h" // for GetHostName, GetUserName #include "../env_settings.h" // for GetStringSetting #include "../logging.h" // for Log #include "../util.h" // for explicit_bzero #include "authproto.h" // for WritePacket, ReadPacket, PTYPE_ERRO... // IWYU pragma: no_include //! Set if a conversation error has happened during the last PAM call. static int conv_error = 0; /*! \brief Perform a single PAM conversation step. * * \param msg The PAM message. * \param resp The PAM response to store the output in. * \return The PAM status (PAM_SUCCESS in case of success, or anything else in * case of error). */ int ConverseOne(const struct pam_message *msg, struct pam_response *resp) { resp->resp = NULL; resp->resp_retcode = 0; // Unused but should be set to zero. switch (msg->msg_style) { case PAM_PROMPT_ECHO_OFF: { WritePacket(1, PTYPE_PROMPT_LIKE_PASSWORD, msg->msg); char type = ReadPacket(0, &resp->resp, 0); return type == PTYPE_RESPONSE_LIKE_PASSWORD ? PAM_SUCCESS : PAM_CONV_ERR; } case PAM_PROMPT_ECHO_ON: { WritePacket(1, PTYPE_PROMPT_LIKE_USERNAME, msg->msg); char type = ReadPacket(0, &resp->resp, 0); return type == PTYPE_RESPONSE_LIKE_USERNAME ? PAM_SUCCESS : PAM_CONV_ERR; } case PAM_ERROR_MSG: WritePacket(1, PTYPE_ERROR_MESSAGE, msg->msg); return PAM_SUCCESS; case PAM_TEXT_INFO: WritePacket(1, PTYPE_INFO_MESSAGE, msg->msg); return PAM_SUCCESS; default: return PAM_CONV_ERR; } } /*! \brief Perform a PAM conversation. * * \param num_msg The number of conversation steps to execute. * \param msg The PAM messages. * \param resp The PAM responses to store the output in. * \param appdata_ptr Unused. * \return The PAM status (PAM_SUCCESS in case of success, or anything else in * case of error). */ int Converse(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { (void)appdata_ptr; if (conv_error) { Log("Converse() got called again with %d messages (first: %s) after " "having failed before - this is very likely a bug in the PAM " "module having made the call. Bailing out", num_msg, num_msg <= 0 ? "(none)" : msg[0]->msg); exit(1); } *resp = calloc(num_msg, sizeof(struct pam_response)); for (int i = 0; i < num_msg; ++i) { int status = ConverseOne(msg[i], &(*resp)[i]); if (status != PAM_SUCCESS) { for (int j = 0; j < num_msg; ++j) { if ((*resp)[j].resp != NULL) { explicit_bzero((*resp)[j].resp, strlen((*resp)[j].resp)); } free((*resp)[j].resp); } free(*resp); *resp = NULL; conv_error = 1; return status; } } return PAM_SUCCESS; } /*! \brief Perform a single PAM operation with retrying logic. */ int CallPAMWithRetries(int (*pam_call)(pam_handle_t *, int), pam_handle_t *pam, int flags) { int attempt = 0; for (;;) { conv_error = 0; int status = pam_call(pam, flags); if (conv_error) { return status; } switch (status) { // Never retry these: case PAM_ABORT: // This is fine. case PAM_MAXTRIES: // D'oh. case PAM_NEW_AUTHTOK_REQD: // hunter2 no longer good enough. case PAM_SUCCESS: // Duh. return status; default: // Let's try again then. ++attempt; if (attempt >= 3) { return status; } break; } } } /*! \brief Perform PAM authentication. * * \param conv The PAM conversation handler. * \param pam The PAM handle will be returned here. * \return The PAM status (PAM_SUCCESS after successful authentication, or * anything else in case of error). */ int Authenticate(struct pam_conv *conv, pam_handle_t **pam) { const char *service_name = GetStringSetting("XSECURELOCK_PAM_SERVICE", PAM_SERVICE_NAME); if (strchr(service_name, '/')) { // As this binary might be running with setuid privileges, we should better // refuse potentially dangerous parameters. This works around PAM // implementations being potentially vulnerable to someone passing // "../shadow" as service name and then getting an error message containing // the encrypted root password. I am not aware of any implementations that // do fall for that - nevertheless let's better be safe. Log("PAM service name (%s) contains a slash - refusing", service_name); return 1; } char username[256]; if (!GetUserName(username, sizeof(username))) { return 1; } int status = pam_start(service_name, username, conv, pam); if (status != PAM_SUCCESS) { Log("pam_start: %d", status); // Or can one call pam_strerror on a NULL handle? return status; } if (!GetIntSetting("XSECURELOCK_NO_PAM_RHOST", 0)) { // This is a local login - by convention PAM_RHOST should be "localhost": // http://www.linux-pam.org/Linux-PAM-html/adg-security-user-identity.html status = pam_set_item(*pam, PAM_RHOST, "localhost"); if (status != PAM_SUCCESS) { Log("pam_set_item: %s", pam_strerror(*pam, status)); return status; } } status = pam_set_item(*pam, PAM_RUSER, username); if (status != PAM_SUCCESS) { Log("pam_set_item: %s", pam_strerror(*pam, status)); return status; } const char *display = getenv("DISPLAY"); status = pam_set_item(*pam, PAM_TTY, display); if (status != PAM_SUCCESS) { Log("pam_set_item: %s", pam_strerror(*pam, status)); return status; } status = CallPAMWithRetries(pam_authenticate, *pam, 0); if (status != PAM_SUCCESS) { if (!conv_error) { Log("pam_authenticate: %s", pam_strerror(*pam, status)); } return status; } int status2 = CallPAMWithRetries(pam_acct_mgmt, *pam, 0); if (status2 == PAM_NEW_AUTHTOK_REQD) { status2 = CallPAMWithRetries(pam_chauthtok, *pam, PAM_CHANGE_EXPIRED_AUTHTOK); #ifdef PAM_CHECK_ACCOUNT_TYPE if (status2 != PAM_SUCCESS) { if (!conv_error) { Log("pam_chauthtok: %s", pam_strerror(*pam, status2)); } return status2; } #else (void)status2; #endif } #ifdef PAM_CHECK_ACCOUNT_TYPE if (status2 != PAM_SUCCESS) { // If this one is true, it must be coming from pam_acct_mgmt, as // pam_chauthtok's result already has been checked against PAM_SUCCESS. if (!conv_error) { Log("pam_acct_mgmt: %s", pam_strerror(*pam, status2)); } return status2; } #endif // Have the authentication module refresh Kerberos tickets and such // if applicable. int sc_status = pam_setcred(*pam, PAM_REFRESH_CRED); if (sc_status != PAM_SUCCESS) { Log("pam_setcred: status=%d", sc_status); } return status; } /*! \brief The main program. * * Usage: ./authproto_pam; status=$? * * \return 0 if authentication successful, anything else otherwise. */ int main() { setlocale(LC_CTYPE, ""); struct pam_conv conv; conv.conv = Converse; conv.appdata_ptr = NULL; pam_handle_t *pam = NULL; int status = Authenticate(&conv, &pam); int status2 = pam == NULL ? PAM_SUCCESS : pam_end(pam, status); if (status != PAM_SUCCESS) { // The caller already displayed an error. return 1; } if (status2 != PAM_SUCCESS) { Log("pam_end: %s", pam_strerror(pam, status2)); return 1; } return 0; } xsecurelock-1.9.0/helpers/saver_blank0000755000175000017500000000132213757234024014655 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Just sleep for an arbitrary long time. The main process is already # taking care of blanking. exec sleep 86400 xsecurelock-1.9.0/helpers/auth_x11.c0000644000175000017500000016155014313047713014244 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include // for Success, None, Atom, KBBellPitch #include // for DefaultScreen, Screen, XFree, True #include // for NULL, setlocale, LC_CTYPE, LC_TIME #include #include // for free, rand, mblen, size_t, EXIT_... #include // for strlen, memcpy, memset, strcspn #include // for timeval, select, fd_set, FD_SET #include // for gettimeofday, timeval #include // for time, nanosleep, localtime_r #include // for close, _exit, dup2, pipe, dup #if __STDC_VERSION__ >= 199901L #include #include #endif #ifdef HAVE_XFT_EXT #include // for XftColorAllocValue, XftColorFree #include // for XRenderColor, XGlyphInfo #include // for FcChar8 #endif #ifdef HAVE_XKB_EXT #include // for XkbFreeKeyboard, XkbGetControls #include // for XkbUseCoreKbd, XkbGroupsWrapMask #include // for _XkbDesc, XkbStateRec, _XkbControls #endif #include "../env_info.h" // for GetHostName, GetUserName #include "../env_settings.h" // for GetIntSetting, GetStringSetting #include "../logging.h" // for Log, LogErrno #include "../mlock_page.h" // for MLOCK_PAGE #include "../util.h" // for explicit_bzero #include "../wait_pgrp.h" // for WaitPgrp #include "../wm_properties.h" // for SetWMProperties #include "../xscreensaver_api.h" // for ReadWindowID #include "authproto.h" // for WritePacket, ReadPacket, PTYPE_R... #include "monitors.h" // for Monitor, GetMonitors, IsMonitorC... #if __STDC_VERSION__ >= 201112L #define STATIC_ASSERT(state, message) _Static_assert(state, message) #else #define STATIC_ASSERT(state, message) \ extern int statically_asserted(int assertion[(state) ? 1 : -1]); #endif //! Number of args. int argc; //! Args. char *const *argv; //! The authproto helper to use. const char *authproto_executable; //! The blinking interval in microseconds. #define BLINK_INTERVAL (250 * 1000) //! The maximum time to wait at a prompt for user input in seconds. int prompt_timeout; //! Number of dancers in the disco password display #define DISCO_PASSWORD_DANCERS 5 //! Length of the "paranoid password display". #define PARANOID_PASSWORD_LENGTH (1 << DISCO_PASSWORD_DANCERS) //! Minimum distance the cursor shall move on keypress. #define PARANOID_PASSWORD_MIN_CHANGE 4 //! Border of the window around the text. #define WINDOW_BORDER 16 //! Draw border rectangle (mainly for debugging). #undef DRAW_BORDER //! Extra line spacing. #define LINE_SPACING 4 //! Actual password prompt selected enum PasswordPrompt { PASSWORD_PROMPT_CURSOR, PASSWORD_PROMPT_ASTERISKS, PASSWORD_PROMPT_HIDDEN, PASSWORD_PROMPT_DISCO, PASSWORD_PROMPT_EMOJI, PASSWORD_PROMPT_EMOTICON, PASSWORD_PROMPT_KAOMOJI, #if __STDC_VERSION__ >= 199901L PASSWORD_PROMPT_TIME, PASSWORD_PROMPT_TIME_HEX, #endif PASSWORD_PROMPT_COUNT, }; const char *PasswordPromptStrings[] = { /* PASSWORD_PROMPT_CURSOR= */ "cursor", /* PASSWORD_PROMPT_ASTERISKS= */ "asterisks", /* PASSWORD_PROMPT_HIDDEN= */ "hidden", /* PASSWORD_PROMPT_DISCO= */ "disco", /* PASSWORD_PROMPT_EMOJI= */ "emoji", /* PASSWORD_PROMPT_EMOTICON= */ "emoticon", /* PASSWORD_PROMPT_KAOMOJI= */ "kaomoji", #if __STDC_VERSION__ >= 199901L /* PASSWORD_PROMPT_TIME= */ "time", /* PASSWORD_PROMPT_TIME_HEX= */ "time_hex", #endif }; enum PasswordPrompt password_prompt; // A disco password is composed of multiple disco_dancers (each selected at // random from the array), joined by the disco_combiner const char *disco_combiner = " ♪ "; // Note: the disco_dancers MUST all have the same length const char *disco_dancers[] = { "┏(・o・)┛", "┗(・o・)┓", }; // Emoji to display in emoji mode. The length of the array must be equal to // PARANOID_PASSWORD_LENGTH. List taken from the top items in // http://emojitracker.com/ The first item is always display in an empty prompt // (before typing in the password) const char *emoji[] = { "_____", "😂", "❤", "♻", "😍", "♥", "😭", "😊", "😒", "💕", "😘", "😩", "☺", "👌", "😔", "😁", "😏", "😉", "👍", "⬅", "😅", "🙏", "😌", "😢", "👀", "💔", "😎", "🎶", "💙", "💜", "🙌", "😳", }; STATIC_ASSERT(sizeof(emoji) / sizeof(*emoji) == PARANOID_PASSWORD_LENGTH, "Emoji array size must be equal to PARANOID_PASSWORD_LENGTH"); // Emoticons to display in emoji mode. The length of the array must be equal to // PARANOID_PASSWORD_LENGTH. The first item is always display in an empty prompt // (before typing in the password) const char *emoticons[] = { ":-)", ":-p", ":-O", ":-\\", "(-:", "d-:", "O-:", "/-:", "8-)", "8-p", "8-O", "8-\\", "(-8", "d-8", "O-8", "/-8", "X-)", "X-p", "X-O", "X-\\", "(-X", "d-X", "O-X", "/-X", ":'-)", ":-S", ":-D", ":-#", "(-':", "S-:", "D-:", "#-:", }; STATIC_ASSERT(sizeof(emoticons) / sizeof(*emoticons) == PARANOID_PASSWORD_LENGTH, "Emoticons array size must be equal to PARANOID_PASSWORD_LENGTH"); // Kaomoji to display in kaomoji mode. The length of the array must be equal to // PARANOID_PASSWORD_LENGTH. The first item is always display in an empty prompt // (before typing in the password) const char *kaomoji[] = { "(͡°͜ʖ͡°)", "(>_<)", "O_ם", "(^_-)", "o_0", "o.O", "0_o", "O.o", "(°o°)", "^m^", "^_^", "((d[-_-]b))", "┏(・o・)┛", "┗(・o・)┓", "(゚Д゚)", "(°◇°)", "\\o/", "\\o|", "|o/", "|o|", "(●^o^●)", "(^v^)", "(^u^)", "(^◇^)", "¯\\_(ツ)_/¯", "(^0_0^)", "(☞゚∀゚)☞", "(-■_■)", "(┛ಠ_ಠ)┛彡┻━┻", "┬─┬ノ(º_ºノ)", "(˘³˘)♥", "❤(◍•ᴗ•◍)", }; STATIC_ASSERT(sizeof(kaomoji) / sizeof(*kaomoji) == PARANOID_PASSWORD_LENGTH, "Kaomoji array size must be equal to PARANOID_PASSWORD_LENGTH"); //! If set, we can start a new login session. int have_switch_user_command; //! If set, the prompt will be fixed by @. int show_username; //! If set, the prompt will be fixed by . If >1, the hostname will be // shown in full and not cut at the first dot. int show_hostname; //! If set, data and time will be shown. int show_datetime; //! The date format to display. const char *datetime_format = "%c"; //! The local hostname. char hostname[256]; //! The username to authenticate as. char username[256]; //! The X11 display. Display *display; //! The X11 window provided by main. Provided from $XSCREENSAVER_WINDOW. Window main_window; //! main_window's parent. Used to create per-monitor siblings. Window parent_window; //! The X11 core font for the PAM messages. XFontStruct *core_font; #ifdef HAVE_XFT_EXT //! The Xft font for the PAM messages. XftColor xft_color_foreground; XftColor xft_color_warning; XftFont *xft_font; #endif //! The background color. XColor xcolor_background; //! The foreground color. XColor xcolor_foreground; //! The warning color (used as foreground). XColor xcolor_warning; //! The cursor character displayed at the end of the masked password input. static const char cursor[] = "_"; //! The x offset to apply to the entire display (to mitigate burn-in). static int x_offset = 0; //! The y offset to apply to the entire display (to mitigate burn-in). static int y_offset = 0; //! Maximum offset value when dynamic changes are enabled. static int burnin_mitigation_max_offset = 0; //! How much the offsets are allowed to change dynamically, and if so, how high. static int burnin_mitigation_max_offset_change = 0; //! Whether to play sounds during authentication. static int auth_sounds = 0; //! Whether to blink the cursor in the auth dialog. static int auth_cursor_blink = 1; //! Whether we only want a single auth window. static int single_auth_window = 0; //! If set, we need to re-query monitor data and adjust windows. int per_monitor_windows_dirty = 1; #ifdef HAVE_XKB_EXT //! If set, we show Xkb keyboard layout name. int show_keyboard_layout = 1; //! If set, we show Xkb lock/latch status rather than Xkb indicators. int show_locks_and_latches = 0; #endif #define MAIN_WINDOW 0 #define MAX_WINDOWS 16 //! The number of active X11 per-monitor windows. size_t num_windows = 0; //! The X11 per-monitor windows to draw on. Window windows[MAX_WINDOWS]; //! The X11 graphics contexts to draw with. GC gcs[MAX_WINDOWS]; //! The X11 graphics contexts to draw warnings with. GC gcs_warning[MAX_WINDOWS]; #ifdef HAVE_XFT_EXT //! The Xft draw contexts to draw with. XftDraw *xft_draws[MAX_WINDOWS]; #endif int have_xkb_ext; enum Sound { SOUND_PROMPT, SOUND_INFO, SOUND_ERROR, SOUND_SUCCESS }; #define NOTE_DS3 156 #define NOTE_A3 220 #define NOTE_DS4 311 #define NOTE_E4 330 #define NOTE_B4 494 #define NOTE_E5 659 int sounds[][2] = { /* SOUND_PROMPT= */ {NOTE_B4, NOTE_E5}, // V|I I /* SOUND_INFO= */ {NOTE_E5, NOTE_E5}, // I 2x /* SOUND_ERROR= */ {NOTE_A3, NOTE_DS3}, // V7 2x /* SOUND_SUCCESS= */ {NOTE_DS4, NOTE_E4}, // V I }; #define SOUND_SLEEP_MS 125 #define SOUND_TONE_MS 100 /*! \brief Play a sound sequence. */ void PlaySound(enum Sound snd) { XKeyboardState state; XKeyboardControl control; struct timespec sleeptime; if (!auth_sounds) { return; } XGetKeyboardControl(display, &state); // bell_percent changes note length on Linux, so let's use the middle value // to get a 1:1 mapping. control.bell_percent = 50; control.bell_duration = SOUND_TONE_MS; control.bell_pitch = sounds[snd][0]; XChangeKeyboardControl(display, KBBellPercent | KBBellDuration | KBBellPitch, &control); XBell(display, 0); XFlush(display); sleeptime.tv_sec = SOUND_SLEEP_MS / 1000; sleeptime.tv_nsec = 1000000L * (SOUND_SLEEP_MS % 1000); nanosleep(&sleeptime, NULL); control.bell_pitch = sounds[snd][1]; XChangeKeyboardControl(display, KBBellPitch, &control); XBell(display, 0); control.bell_percent = state.bell_percent; control.bell_duration = state.bell_duration; control.bell_pitch = state.bell_pitch; XChangeKeyboardControl(display, KBBellPercent | KBBellDuration | KBBellPitch, &control); XFlush(display); nanosleep(&sleeptime, NULL); } /*! \brief Switch to the next keyboard layout. */ void SwitchKeyboardLayout(void) { #ifdef HAVE_XKB_EXT if (!have_xkb_ext) { return; } XkbDescPtr xkb; xkb = XkbGetMap(display, 0, XkbUseCoreKbd); if (XkbGetControls(display, XkbGroupsWrapMask, xkb) != Success) { Log("XkbGetControls failed"); XkbFreeKeyboard(xkb, 0, True); return; } if (xkb->ctrls->num_groups < 1) { Log("XkbGetControls returned less than 1 group"); XkbFreeKeyboard(xkb, 0, True); return; } XkbStateRec state; if (XkbGetState(display, XkbUseCoreKbd, &state) != Success) { Log("XkbGetState failed"); XkbFreeKeyboard(xkb, 0, True); return; } XkbLockGroup(display, XkbUseCoreKbd, (state.group + 1) % xkb->ctrls->num_groups); XkbFreeKeyboard(xkb, 0, True); #endif } /*! \brief Check which modifiers are active. * * \param warning Will be set to 1 if something's "bad" with the keyboard * layout (e.g. Caps Lock). * \param have_multiple_layouts Will be set to 1 if more than one keyboard * layout is available for switching. * * \return The current modifier mask as a string. */ const char *GetIndicators(int *warning, int *have_multiple_layouts) { #ifdef HAVE_XKB_EXT static char buf[128]; char *p; if (!have_xkb_ext) { return ""; } XkbDescPtr xkb; xkb = XkbGetMap(display, 0, XkbUseCoreKbd); if (XkbGetControls(display, XkbGroupsWrapMask, xkb) != Success) { Log("XkbGetControls failed"); XkbFreeKeyboard(xkb, 0, True); return ""; } if (XkbGetNames( display, XkbIndicatorNamesMask | XkbGroupNamesMask | XkbSymbolsNameMask, xkb) != Success) { Log("XkbGetNames failed"); XkbFreeKeyboard(xkb, 0, True); return ""; } XkbStateRec state; if (XkbGetState(display, XkbUseCoreKbd, &state) != Success) { Log("XkbGetState failed"); XkbFreeKeyboard(xkb, 0, True); return ""; } unsigned int istate = 0; if (!show_locks_and_latches) { if (XkbGetIndicatorState(display, XkbUseCoreKbd, &istate) != Success) { Log("XkbGetIndicatorState failed"); XkbFreeKeyboard(xkb, 0, True); return ""; } } // Detect Caps Lock. // Note: in very pathological cases the modifier might be set without an // XkbIndicator for it; then we show the line in red without telling the user // why. Such a situation has not been observd yet though. unsigned int implicit_mods = state.latched_mods | state.locked_mods; if (implicit_mods & LockMask) { *warning = 1; } // Provide info about multiple layouts. if (xkb->ctrls->num_groups > 1) { *have_multiple_layouts = 1; } p = buf; const char *word = "Keyboard: "; size_t n = strlen(word); if (n >= sizeof(buf) - (p - buf)) { Log("Not enough space to store intro '%s'", word); XkbFreeKeyboard(xkb, 0, True); return ""; } memcpy(p, word, n); p += n; int have_output = 0; if (show_keyboard_layout) { Atom layouta = xkb->names->groups[state.group]; // Human-readable. if (layouta == None) { layouta = xkb->names->symbols; // Machine-readable fallback. } if (layouta != None) { char *layout = XGetAtomName(display, layouta); n = strlen(layout); if (n >= sizeof(buf) - (p - buf)) { Log("Not enough space to store layout name '%s'", layout); XFree(layout); XkbFreeKeyboard(xkb, 0, True); return ""; } memcpy(p, layout, n); XFree(layout); p += n; have_output = 1; } } if (show_locks_and_latches) { #define ADD_INDICATOR(mask, name) \ do { \ if (!(implicit_mods & (mask))) { \ continue; \ } \ if (have_output) { \ if (2 >= sizeof(buf) - (p - buf)) { \ Log("Not enough space to store another modifier name"); \ break; \ } \ memcpy(p, ", ", 2); \ p += 2; \ } \ size_t n = strlen(name); \ if (n >= sizeof(buf) - (p - buf)) { \ Log("Not enough space to store modifier name '%s'", name); \ XFree(name); \ break; \ } \ memcpy(p, (name), n); \ p += n; \ have_output = 1; \ } while (0) // TODO(divVerent): There must be a better way to get the names of the // modifiers than explicitly enumerating them. Also, there may even be // something that knows that Mod1 is Alt/Meta and Mod2 is Num lock. ADD_INDICATOR(ShiftMask, "Shift"); ADD_INDICATOR(LockMask, "Lock"); ADD_INDICATOR(ControlMask, "Control"); ADD_INDICATOR(Mod1Mask, "Mod1"); ADD_INDICATOR(Mod2Mask, "Mod2"); ADD_INDICATOR(Mod3Mask, "Mod3"); ADD_INDICATOR(Mod4Mask, "Mod4"); ADD_INDICATOR(Mod5Mask, "Mod5"); } else { for (int i = 0; i < XkbNumIndicators; i++) { if (!(istate & (1U << i))) { continue; } Atom namea = xkb->names->indicators[i]; if (namea == None) { continue; } if (have_output) { if (2 >= sizeof(buf) - (p - buf)) { Log("Not enough space to store another modifier name"); break; } memcpy(p, ", ", 2); p += 2; } char *name = XGetAtomName(display, namea); size_t n = strlen(name); if (n >= sizeof(buf) - (p - buf)) { Log("Not enough space to store modifier name '%s'", name); XFree(name); break; } memcpy(p, name, n); XFree(name); p += n; have_output = 1; } } *p = 0; XkbFreeKeyboard(xkb, 0, True); return have_output ? buf : ""; #else *warning = *warning; // Shut up clang-analyzer. *have_multiple_layouts = *have_multiple_layouts; // Shut up clang-analyzer. return ""; #endif } void DestroyPerMonitorWindows(size_t keep_windows) { for (size_t i = keep_windows; i < num_windows; ++i) { #ifdef HAVE_XFT_EXT XftDrawDestroy(xft_draws[i]); #endif XFreeGC(display, gcs_warning[i]); XFreeGC(display, gcs[i]); if (i == MAIN_WINDOW) { XUnmapWindow(display, windows[i]); } else { XDestroyWindow(display, windows[i]); } } if (num_windows > keep_windows) { num_windows = keep_windows; } } void CreateOrUpdatePerMonitorWindow(size_t i, const Monitor *monitor, int region_w, int region_h, int x_offset, int y_offset) { // Desired box. int w = region_w; int h = region_h; int x = monitor->x + (monitor->width - w) / 2 + x_offset; int y = monitor->y + (monitor->height - h) / 2 + y_offset; // Clip to monitor. if (x < 0) { w += x; x = 0; } if (y < 0) { h += y; y = 0; } if (x + w > monitor->x + monitor->width) { w = monitor->x + monitor->width - x; } if (y + h > monitor->y + monitor->height) { h = monitor->y + monitor->height - y; } if (i < num_windows) { // Move the existing window. XMoveResizeWindow(display, windows[i], x, y, w, h); return; } if (i > num_windows) { // Need to add windows in ]num_windows..i[ first. Log("Unreachable code - can't create monitor sequences with holes"); abort(); } // Add a new window. XSetWindowAttributes attrs = {0}; attrs.background_pixel = xcolor_background.pixel; if (i == MAIN_WINDOW) { // Reuse the main_window (so this window gets protected from overlap by // main). XMoveResizeWindow(display, main_window, x, y, w, h); XChangeWindowAttributes(display, main_window, CWBackPixel, &attrs); windows[i] = main_window; } else { // Create a new window. windows[i] = XCreateWindow(display, parent_window, x, y, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel, &attrs); SetWMProperties(display, windows[i], "xsecurelock", "auth_x11_screen", argc, argv); // We should always make sure that main_window stays on top of all others. // I.e. our auth sub-windows shall between "sandwiched" between auth and // saver window. That way, main.c's protections of the auth window can stay // effective. Window stacking_order[2]; stacking_order[0] = main_window; stacking_order[1] = windows[i]; XRestackWindows(display, stacking_order, 2); } // Create its data structures. XGCValues gcattrs; gcattrs.function = GXcopy; gcattrs.foreground = xcolor_foreground.pixel; gcattrs.background = xcolor_background.pixel; if (core_font != NULL) { gcattrs.font = core_font->fid; } gcs[i] = XCreateGC(display, windows[i], GCFunction | GCForeground | GCBackground | (core_font != NULL ? GCFont : 0), &gcattrs); gcattrs.foreground = xcolor_warning.pixel; gcs_warning[i] = XCreateGC(display, windows[i], GCFunction | GCForeground | GCBackground | (core_font != NULL ? GCFont : 0), &gcattrs); #ifdef HAVE_XFT_EXT xft_draws[i] = XftDrawCreate( display, windows[i], DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display))); #endif // This window is now ready to use. XMapWindow(display, windows[i]); num_windows = i + 1; } void UpdatePerMonitorWindows(int monitors_changed, int region_w, int region_h, int x_offset, int y_offset) { static size_t num_monitors = 0; static Monitor monitors[MAX_WINDOWS]; if (monitors_changed) { num_monitors = GetMonitors(display, parent_window, monitors, MAX_WINDOWS); } if (single_auth_window) { Window unused_root, unused_child; int unused_root_x, unused_root_y, x, y; unsigned int unused_mask; XQueryPointer(display, parent_window, &unused_root, &unused_child, &unused_root_x, &unused_root_y, &x, &y, &unused_mask); for (size_t i = 0; i < num_monitors; ++i) { if (x >= monitors[i].x && x < monitors[i].x + monitors[i].width && y >= monitors[i].y && y < monitors[i].y + monitors[i].height) { CreateOrUpdatePerMonitorWindow(0, &monitors[i], region_w, region_h, x_offset, y_offset); return; } } if (num_monitors > 0) { CreateOrUpdatePerMonitorWindow(0, &monitors[0], region_w, region_h, x_offset, y_offset); DestroyPerMonitorWindows(1); } else { DestroyPerMonitorWindows(0); } return; } // 1 window per monitor. size_t new_num_windows = num_monitors; // Update or create everything. for (size_t i = 0; i < new_num_windows; ++i) { CreateOrUpdatePerMonitorWindow(i, &monitors[i], region_w, region_h, x_offset, y_offset); } // Kill all the old stuff. DestroyPerMonitorWindows(new_num_windows); if (num_windows != new_num_windows) { Log("Unreachable code - expected to get %d windows, got %d", (int)new_num_windows, (int)num_windows); } } int TextAscent(void) { #ifdef HAVE_XFT_EXT if (xft_font != NULL) { return xft_font->ascent; } #endif return core_font->max_bounds.ascent; } int TextDescent(void) { #ifdef HAVE_XFT_EXT if (xft_font != NULL) { return xft_font->descent; } #endif return core_font->max_bounds.descent; } #ifdef HAVE_XFT_EXT // Returns the amount of pixels to expand the logical box in extents so it // covers the visible box. int XGlyphInfoExpandAmount(XGlyphInfo *extents) { // Use whichever is larger - visible bounding box (bigger if font is italic) // or spacing to next character (bigger if last character is a space). // Best reference I could find: // https://keithp.com/~keithp/render/Xft.tutorial // Visible bounding box: [-x, -x + width[ // Logical bounding box: [0, xOff[ // For centering we should always use the logical bounding box, however for // erasing we should use the visible bounding box. Thus our goal is to // expand the _logical_ box to fully cover the _visible_ box: int expand_left = extents->x; int expand_right = -extents->x + extents->width - extents->xOff; int expand_max = expand_left > expand_right ? expand_left : expand_right; int expand_positive = expand_max > 0 ? expand_max : 0; return expand_positive; } #endif int TextWidth(const char *string, int len) { #ifdef HAVE_XFT_EXT if (xft_font != NULL) { XGlyphInfo extents; XftTextExtentsUtf8(display, xft_font, (const FcChar8 *)string, len, &extents); return extents.xOff + 2 * XGlyphInfoExpandAmount(&extents); } #endif return XTextWidth(core_font, string, len); } void DrawString(int monitor, int x, int y, int is_warning, const char *string, int len) { #ifdef HAVE_XFT_EXT if (xft_font != NULL) { // HACK: Query text extents here to make the text fit into the specified // box. For y this is covered by the usual ascent/descent behavior - for x // we however do have to work around font descents being drawn to the left // of the cursor. XGlyphInfo extents; XftTextExtentsUtf8(display, xft_font, (const FcChar8 *)string, len, &extents); XftDrawStringUtf8(xft_draws[monitor], is_warning ? &xft_color_warning : &xft_color_foreground, xft_font, x + XGlyphInfoExpandAmount(&extents), y, (const FcChar8 *)string, len); return; } #endif XDrawString(display, windows[monitor], is_warning ? gcs_warning[monitor] : gcs[monitor], x, y, string, len); } void StrAppend(char **output, size_t *output_size, const char *input, size_t input_size) { if (*output_size <= input_size) { // Cut the input off. Sorry. input_size = *output_size - 1; } memcpy(*output, input, input_size); *output += input_size; *output_size -= input_size; } void BuildTitle(char *output, size_t output_size, const char *input) { if (show_username) { size_t username_len = strlen(username); StrAppend(&output, &output_size, username, username_len); } if (show_username && show_hostname) { StrAppend(&output, &output_size, "@", 1); } if (show_hostname) { size_t hostname_len = show_hostname > 1 ? strlen(hostname) : strcspn(hostname, "."); StrAppend(&output, &output_size, hostname, hostname_len); } if (*input == 0) { *output = 0; return; } if (show_username || show_hostname) { StrAppend(&output, &output_size, " - ", 3); } strncpy(output, input, output_size - 1); output[output_size - 1] = 0; } /*! \brief Display a string in the window. * * The given title and message will be displayed on all screens. In case caps * lock is enabled, the string's case will be inverted. * * \param title The title of the message. * \param str The message itself. * \param is_warning Whether to use the warning style to display the message. */ void DisplayMessage(const char *title, const char *str, int is_warning) { char full_title[256]; BuildTitle(full_title, sizeof(full_title), title); int th = TextAscent() + TextDescent() + LINE_SPACING; int to = TextAscent() + LINE_SPACING / 2; // Text at to fits into 0 to th. int len_full_title = strlen(full_title); int tw_full_title = TextWidth(full_title, len_full_title); int len_str = strlen(str); int tw_str = TextWidth(str, len_str); int indicators_warning = 0; int have_multiple_layouts = 0; const char *indicators = GetIndicators(&indicators_warning, &have_multiple_layouts); int len_indicators = strlen(indicators); int tw_indicators = TextWidth(indicators, len_indicators); const char *switch_layout = have_multiple_layouts ? "Press Ctrl-Tab to switch keyboard layout" : ""; int len_switch_layout = strlen(switch_layout); int tw_switch_layout = TextWidth(switch_layout, len_switch_layout); const char *switch_user = have_switch_user_command ? "Press Ctrl-Alt-O or Win-O to switch user" : ""; int len_switch_user = strlen(switch_user); int tw_switch_user = TextWidth(switch_user, len_switch_user); char datetime[80] = ""; if (show_datetime) { time_t rawtime; struct tm timeinfo_buf; struct tm *timeinfo; time(&rawtime); timeinfo = localtime_r(&rawtime, &timeinfo_buf); if (timeinfo == NULL || strftime(datetime, sizeof(datetime), datetime_format, timeinfo) == 0) { // The datetime buffer was too small to fit the time format, and in this // case the buffer contents are undefined. Let's just make it a valid // empty string then so all else will go well. datetime[0] = 0; } } int len_datetime = strlen(datetime); int tw_datetime = TextWidth(datetime, len_datetime); // Compute the region we will be using, relative to cx and cy. int box_w = tw_full_title; if (box_w < tw_datetime) { box_w = tw_datetime; } if (box_w < tw_str) { box_w = tw_str; } if (box_w < tw_indicators) { box_w = tw_indicators; } if (box_w < tw_switch_layout) { box_w = tw_switch_layout; } if (box_w < tw_switch_user) { box_w = tw_switch_user; } int box_h = (4 + have_multiple_layouts + have_switch_user_command + show_datetime * 2) * th; int region_w = box_w + 2 * WINDOW_BORDER; int region_h = box_h + 2 * WINDOW_BORDER; if (burnin_mitigation_max_offset_change > 0) { x_offset += rand() % (2 * burnin_mitigation_max_offset_change + 1) - burnin_mitigation_max_offset_change; if (x_offset < -burnin_mitigation_max_offset) { x_offset = -burnin_mitigation_max_offset; } if (x_offset > burnin_mitigation_max_offset) { x_offset = burnin_mitigation_max_offset; } y_offset += rand() % (2 * burnin_mitigation_max_offset_change + 1) - burnin_mitigation_max_offset_change; if (y_offset < -burnin_mitigation_max_offset) { y_offset = -burnin_mitigation_max_offset; } if (y_offset > burnin_mitigation_max_offset) { y_offset = burnin_mitigation_max_offset; } } UpdatePerMonitorWindows(per_monitor_windows_dirty, region_w, region_h, x_offset, y_offset); per_monitor_windows_dirty = 0; for (size_t i = 0; i < num_windows; ++i) { int cx = region_w / 2; int cy = region_h / 2; int y = cy + to - box_h / 2; XClearWindow(display, windows[i]); #ifdef DRAW_BORDER XDrawRectangle(display, windows[i], gcs[i], // cx - box_w / 2, cy - box_h / 2, // box_w - 1, box_h - 1); #endif if (show_datetime) { DrawString(i, cx - tw_datetime / 2, y, 0, datetime, len_datetime); y += th * 2; } DrawString(i, cx - tw_full_title / 2, y, is_warning, full_title, len_full_title); y += th * 2; DrawString(i, cx - tw_str / 2, y, is_warning, str, len_str); y += th; DrawString(i, cx - tw_indicators / 2, y, indicators_warning, indicators, len_indicators); y += th; if (have_multiple_layouts) { DrawString(i, cx - tw_switch_layout / 2, y, 0, switch_layout, len_switch_layout); y += th; } if (have_switch_user_command) { DrawString(i, cx - tw_switch_user / 2, y, 0, switch_user, len_switch_user); // y += th; } } // Make the things just drawn appear on the screen as soon as possible. XFlush(display); } void WaitForKeypress(int seconds) { // Sleep for up to 1 second _or_ a key press. struct timeval timeout; timeout.tv_sec = seconds; timeout.tv_usec = 0; fd_set set; memset(&set, 0, sizeof(set)); // For clang-analyzer. FD_ZERO(&set); FD_SET(0, &set); select(1, &set, NULL, NULL, &timeout); } /*! \brief Bump the position for the password "cursor". * * If pwlen > 0: * Precondition: pos in 0..PARANOID_PASSWORD_LENGTH-1. * Postcondition: pos' in 1..PARANOID_PASSWORD_LENGTH-1. * Postcondition: abs(pos' - pos) >= PARANOID_PASSWORD_MIN_CHANGE. * Postcondition: pos' is uniformly distributed among all permitted choices. * If pwlen == 0: * Postcondition: pos' is 0. * * \param pwlen The current password length. * \param pos The initial cursor position; will get updated. * \param last_keystroke The time of last keystroke; will get updated. */ void BumpDisplayMarker(size_t pwlen, size_t *pos, struct timeval *last_keystroke) { gettimeofday(last_keystroke, NULL); // Empty password: always put at 0. if (pwlen == 0) { *pos = 0; return; } // Otherwise: put in the range and fulfill the constraints. for (;;) { size_t new_pos = 1 + rand() % (PARANOID_PASSWORD_LENGTH - 1); if (labs((ssize_t)new_pos - (ssize_t)*pos) >= PARANOID_PASSWORD_MIN_CHANGE) { *pos = new_pos; break; } } } //! The size of the buffer to store the password in. Not NUL terminated. #define PWBUF_SIZE 256 //! The size of the buffer to use for display, with space for cursor and NUL. #define DISPLAYBUF_SIZE (PWBUF_SIZE + 2) void ShowFromArray(const char **array, size_t displaymarker, char *displaybuf, size_t displaybufsize, size_t *displaylen) { const char *selection = array[displaymarker]; strncpy(displaybuf, selection, displaybufsize); displaybuf[displaybufsize - 1] = 0; *displaylen = strlen(selection); } /*! \brief Ask a question to the user. * * \param msg The message. * \param response The response will be stored in a newly allocated buffer here. * The caller is supposed to eventually free() it. * \param echo If true, the input will be shown; otherwise it will be hidden * (password entry). * \return 1 if successful, anything else otherwise. */ int Prompt(const char *msg, char **response, int echo) { // Ask something. Return strdup'd string. struct { // The received X11 event. XEvent ev; // Input buffer. Not NUL-terminated. char pwbuf[PWBUF_SIZE]; // Current input length. size_t pwlen; // Display buffer. If echo is 0, this will only contain asterisks, a // possible cursor, and be NUL-terminated. char displaybuf[DISPLAYBUF_SIZE]; // Display buffer length. size_t displaylen; // The display marker changes on every input action to a value from 0 to // PARANOID_PASSWORD-1. It indicates where to display the "cursor". size_t displaymarker; // Character read buffer. char inputbuf; // The time of last keystroke. struct timeval last_keystroke; // Temporary position variables that might leak properties about the // password and thus are in the private struct too. size_t prevpos; size_t pos; int len; } priv; int blink_state = 0; if (!echo && MLOCK_PAGE(&priv, sizeof(priv)) < 0) { LogErrno("mlock"); // We continue anyway, as the user being unable to unlock the screen is // worse. But let's alert the user. DisplayMessage("Error", "Password will not be stored securely.", 1); WaitForKeypress(1); } priv.pwlen = 0; priv.displaymarker = 0; time_t deadline = time(NULL) + prompt_timeout; // Unfortunately we may have to break out of multiple loops at once here but // still do common cleanup work. So we have to track the return value in a // variable. int status = 0; int done = 0; int played_sound = 0; while (!done) { if (echo) { if (priv.pwlen != 0) { memcpy(priv.displaybuf, priv.pwbuf, priv.pwlen); } priv.displaylen = priv.pwlen; // Note that priv.pwlen <= sizeof(priv.pwbuf) and thus // priv.pwlen + 2 <= sizeof(priv.displaybuf). priv.displaybuf[priv.displaylen] = blink_state ? ' ' : *cursor; priv.displaybuf[priv.displaylen + 1] = '\0'; } else { switch (password_prompt) { case PASSWORD_PROMPT_ASTERISKS: { mblen(NULL, 0); priv.pos = priv.displaylen = 0; while (priv.pos < priv.pwlen) { ++priv.displaylen; // Note: this won't read past priv.pwlen. priv.len = mblen(priv.pwbuf + priv.pos, priv.pwlen - priv.pos); if (priv.len <= 0) { // This guarantees to "eat" one byte each step. Therefore, // priv.displaylen <= priv.pwlen is ensured. break; } priv.pos += priv.len; } memset(priv.displaybuf, '*', priv.displaylen); // Note that priv.pwlen <= sizeof(priv.pwbuf) and thus // priv.pwlen + 2 <= sizeof(priv.displaybuf). priv.displaybuf[priv.displaylen] = blink_state ? ' ' : *cursor; priv.displaybuf[priv.displaylen + 1] = '\0'; break; } case PASSWORD_PROMPT_HIDDEN: { priv.displaylen = 0; priv.displaybuf[0] = '\0'; break; } case PASSWORD_PROMPT_DISCO: { size_t combiner_length = strlen(disco_combiner); size_t dancer_length = strlen(disco_dancers[0]); size_t stride = combiner_length + dancer_length; priv.displaylen = stride * DISCO_PASSWORD_DANCERS * strlen(disco_dancers[0]) + strlen(disco_combiner); for (size_t i = 0, bit = 1; i < DISCO_PASSWORD_DANCERS; ++i, bit <<= 1) { const char *dancer = disco_dancers[(priv.displaymarker & bit) ? 1 : 0]; memcpy(priv.displaybuf + i * stride, disco_combiner, combiner_length); memcpy(priv.displaybuf + i * stride + combiner_length, dancer, dancer_length); } memcpy(priv.displaybuf + DISCO_PASSWORD_DANCERS * stride, disco_combiner, combiner_length); priv.displaybuf[priv.displaylen] = '\0'; break; } case PASSWORD_PROMPT_EMOJI: { ShowFromArray(emoji, priv.displaymarker, priv.displaybuf, sizeof(priv.displaybuf), &priv.displaylen); break; } case PASSWORD_PROMPT_EMOTICON: { ShowFromArray(emoticons, priv.displaymarker, priv.displaybuf, sizeof(priv.displaybuf), &priv.displaylen); break; } case PASSWORD_PROMPT_KAOMOJI: { ShowFromArray(kaomoji, priv.displaymarker, priv.displaybuf, sizeof(priv.displaybuf), &priv.displaylen); break; } #if __STDC_VERSION__ >= 199901L case PASSWORD_PROMPT_TIME: case PASSWORD_PROMPT_TIME_HEX: { if (priv.pwlen == 0) { strncpy(priv.displaybuf, "----", DISPLAYBUF_SIZE - 1); priv.displaybuf[DISPLAYBUF_SIZE - 1] = 0; } else { if (password_prompt == PASSWORD_PROMPT_TIME) { snprintf(priv.displaybuf, DISPLAYBUF_SIZE, "%" PRId64 ".%06" PRId64, (int64_t)priv.last_keystroke.tv_sec, (int64_t)priv.last_keystroke.tv_usec); } else { snprintf(priv.displaybuf, DISPLAYBUF_SIZE, "%#" PRIx64, (int64_t)priv.last_keystroke.tv_sec * 1000000 + (int64_t)priv.last_keystroke.tv_usec); } priv.displaybuf[DISPLAYBUF_SIZE - 1] = 0; } break; } #endif default: case PASSWORD_PROMPT_CURSOR: { priv.displaylen = PARANOID_PASSWORD_LENGTH; memset(priv.displaybuf, '_', priv.displaylen); priv.displaybuf[priv.displaymarker] = blink_state ? '-' : '|'; priv.displaybuf[priv.displaylen] = '\0'; break; } } } DisplayMessage(msg, priv.displaybuf, 0); if (!played_sound) { PlaySound(SOUND_PROMPT); played_sound = 1; } // Blink the cursor. if (auth_cursor_blink) { blink_state = !blink_state; } struct timeval timeout; timeout.tv_sec = BLINK_INTERVAL / 1000000; timeout.tv_usec = BLINK_INTERVAL % 1000000; while (!done) { fd_set set; memset(&set, 0, sizeof(set)); // For clang-analyzer. FD_ZERO(&set); FD_SET(0, &set); int nfds = select(1, &set, NULL, NULL, &timeout); if (nfds < 0) { LogErrno("select"); done = 1; break; } time_t now = time(NULL); if (now > deadline) { Log("AUTH_TIMEOUT hit"); done = 1; break; } if (deadline > now + prompt_timeout) { // Guard against the system clock stepping back. deadline = now + prompt_timeout; } if (nfds == 0) { // Blink... break; } // From now on, only do nonblocking selects so we update the screen ASAP. timeout.tv_usec = 0; // Force the cursor to be in visible state while typing. blink_state = 0; // Reset the prompt timeout. deadline = now + prompt_timeout; ssize_t nread = read(0, &priv.inputbuf, 1); if (nread <= 0) { Log("EOF on password input - bailing out"); done = 1; break; } switch (priv.inputbuf) { case '\b': // Backspace. case '\177': { // Delete (note: i3lock does not handle this one). // Backwards skip with multibyte support. mblen(NULL, 0); priv.pos = priv.prevpos = 0; while (priv.pos < priv.pwlen) { priv.prevpos = priv.pos; // Note: this won't read past priv.pwlen. priv.len = mblen(priv.pwbuf + priv.pos, priv.pwlen - priv.pos); if (priv.len <= 0) { // This guarantees to "eat" one byte each step. Therefore, // this cannot loop endlessly. break; } priv.pos += priv.len; } priv.pwlen = priv.prevpos; BumpDisplayMarker(priv.pwlen, &priv.displaymarker, &priv.last_keystroke); break; } case '\001': // Ctrl-A. // Clearing input line on just Ctrl-A is odd - but commonly // requested. In most toolkits, Ctrl-A does not immediately erase but // almost every keypress other than arrow keys will erase afterwards. priv.pwlen = 0; BumpDisplayMarker(priv.pwlen, &priv.displaymarker, &priv.last_keystroke); break; case '\023': // Ctrl-S. SwitchKeyboardLayout(); break; case '\025': // Ctrl-U. // Delete the entire input line. // i3lock: supports Ctrl-U but not Ctrl-A. // xscreensaver: supports Ctrl-U and Ctrl-X but not Ctrl-A. priv.pwlen = 0; BumpDisplayMarker(priv.pwlen, &priv.displaymarker, &priv.last_keystroke); break; case 0: // Shouldn't happen. case '\033': // Escape. done = 1; break; case '\r': // Return. case '\n': // Return. *response = malloc(priv.pwlen + 1); if (!echo && MLOCK_PAGE(*response, priv.pwlen + 1) < 0) { LogErrno("mlock"); // We continue anyway, as the user being unable to unlock the screen // is worse. But let's alert the user of this. DisplayMessage("Error", "Password has not been stored securely.", 1); WaitForKeypress(1); } if (priv.pwlen != 0) { memcpy(*response, priv.pwbuf, priv.pwlen); } (*response)[priv.pwlen] = 0; status = 1; done = 1; break; default: if (priv.inputbuf >= '\000' && priv.inputbuf <= '\037') { // Other control character. We ignore them (and specifically do not // update the cursor on them) to "discourage" their use in // passwords, as most login screens do not support them anyway. break; } if (priv.pwlen < sizeof(priv.pwbuf)) { priv.pwbuf[priv.pwlen] = priv.inputbuf; ++priv.pwlen; BumpDisplayMarker(priv.pwlen, &priv.displaymarker, &priv.last_keystroke); } else { Log("Password entered is too long - bailing out"); done = 1; break; } break; } } // Handle X11 events that queued up. while (!done && XPending(display) && (XNextEvent(display, &priv.ev), 1)) { if (IsMonitorChangeEvent(display, priv.ev.type)) { per_monitor_windows_dirty = 1; } } } // priv contains password related data, so better clear it. memset(&priv, 0, sizeof(priv)); if (!done) { Log("Unreachable code - the loop above must set done"); } return status; } /*! \brief Perform authentication using a helper proxy. * * \return The authentication status (0 for OK, 1 otherwise). */ int Authenticate() { int requestfd[2], responsefd[2]; if (pipe(requestfd)) { LogErrno("pipe"); return 1; } if (pipe(responsefd)) { LogErrno("pipe"); return 1; } // Use authproto_pam. pid_t childpid = ForkWithoutSigHandlers(); if (childpid == -1) { LogErrno("fork"); return 1; } if (childpid == 0) { // Child process. Just run authproto_pam. // But first, move requestfd[1] to 1 and responsefd[0] to 0. close(requestfd[0]); close(responsefd[1]); if (requestfd[1] == 0) { // Tricky case. We don't _expect_ this to happen - after all, // initially our own fd 0 should be bound to xsecurelock's main // program - but nevertheless let's handle it. // At least this implies that no other fd is 0. int requestfd1 = dup(requestfd[1]); if (requestfd1 == -1) { LogErrno("dup"); _exit(EXIT_FAILURE); } close(requestfd[1]); if (dup2(responsefd[0], 0) == -1) { LogErrno("dup2"); _exit(EXIT_FAILURE); } close(responsefd[0]); if (requestfd1 != 1) { if (dup2(requestfd1, 1) == -1) { LogErrno("dup2"); _exit(EXIT_FAILURE); } close(requestfd1); } } else { if (responsefd[0] != 0) { if (dup2(responsefd[0], 0) == -1) { LogErrno("dup2"); _exit(EXIT_FAILURE); } close(responsefd[0]); } if (requestfd[1] != 1) { if (dup2(requestfd[1], 1) == -1) { LogErrno("dup2"); _exit(EXIT_FAILURE); } close(requestfd[1]); } } { const char *args[2] = {authproto_executable, NULL}; ExecvHelper(authproto_executable, args); sleep(2); // Reduce log spam or other effects from failed execv. _exit(EXIT_FAILURE); } } // Otherwise, we're in the parent process. close(requestfd[1]); close(responsefd[0]); for (;;) { char *message; char *response; char type = ReadPacket(requestfd[0], &message, 1); switch (type) { case PTYPE_INFO_MESSAGE: DisplayMessage("PAM says", message, 0); explicit_bzero(message, strlen(message)); free(message); PlaySound(SOUND_INFO); WaitForKeypress(1); break; case PTYPE_ERROR_MESSAGE: DisplayMessage("Error", message, 1); explicit_bzero(message, strlen(message)); free(message); PlaySound(SOUND_ERROR); WaitForKeypress(1); break; case PTYPE_PROMPT_LIKE_USERNAME: if (Prompt(message, &response, 1)) { WritePacket(responsefd[1], PTYPE_RESPONSE_LIKE_USERNAME, response); explicit_bzero(response, strlen(response)); free(response); } else { WritePacket(responsefd[1], PTYPE_RESPONSE_CANCELLED, ""); } explicit_bzero(message, strlen(message)); free(message); DisplayMessage("Processing...", "", 0); break; case PTYPE_PROMPT_LIKE_PASSWORD: if (Prompt(message, &response, 0)) { WritePacket(responsefd[1], PTYPE_RESPONSE_LIKE_PASSWORD, response); explicit_bzero(response, strlen(response)); free(response); } else { WritePacket(responsefd[1], PTYPE_RESPONSE_CANCELLED, ""); } explicit_bzero(message, strlen(message)); free(message); DisplayMessage("Processing...", "", 0); break; case 0: goto done; default: Log("Unknown message type %02x", (int)type); explicit_bzero(message, strlen(message)); free(message); goto done; } } done: close(requestfd[0]); close(responsefd[1]); int status; if (!WaitProc("authproto", &childpid, 1, 0, &status)) { Log("WaitPgrp returned false but we were blocking"); abort(); } if (status == 0) { PlaySound(SOUND_SUCCESS); } return status != 0; } enum PasswordPrompt GetPasswordPromptFromFlags( int paranoid_password_flag, const char *password_prompt_flag) { if (!*password_prompt_flag) { return paranoid_password_flag ? PASSWORD_PROMPT_CURSOR : PASSWORD_PROMPT_ASTERISKS; } for (enum PasswordPrompt prompt = 0; prompt < PASSWORD_PROMPT_COUNT; ++prompt) { if (strcmp(password_prompt_flag, PasswordPromptStrings[prompt]) == 0) { return prompt; } } Log("Invalid XSECURELOCK_PASSWORD_PROMPT value; defaulting to cursor"); return PASSWORD_PROMPT_CURSOR; } #ifdef HAVE_XFT_EXT XftFont *FixedXftFontOpenName(Display *display, int screen, const char *font_name) { XftFont *xft_font = XftFontOpenName(display, screen, font_name); #ifdef HAVE_FONTCONFIG // Workaround for Xft crashing the process when trying to render a colored // font. See https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 and // https://gitlab.freedesktop.org/xorg/lib/libxft/issues/6 among others. In // the long run this should be ported to a different font rendering library // than Xft. FcBool iscol; if (xft_font != NULL && FcPatternGetBool(xft_font->pattern, FC_COLOR, 0, &iscol) && iscol) { Log("Colored font %s is not supported by Xft", font_name); XftFontClose(display, xft_font); return NULL; } #else #warning "Xft enabled without fontconfig. May crash trying to use emoji fonts." Log("Xft enabled without fontconfig. May crash trying to use emoji fonts."); #endif return xft_font; } #endif /*! \brief The main program. * * Usage: XSCREENSAVER_WINDOW=window_id ./auth_x11; status=$? * * \return 0 if authentication successful, anything else otherwise. */ int main(int argc_local, char **argv_local) { argc = argc_local; argv = argv_local; setlocale(LC_CTYPE, ""); setlocale(LC_TIME, ""); // This is used by displaymarker only; there is slight security relevance here // as an attacker who has a screenshot and an exact startup time and PID can // guess the password length. Of course, an attacker who records the screen // as a video, or points a camera or a microphone at the keyboard, can too. struct timeval tv; gettimeofday(&tv, NULL); srand(tv.tv_sec ^ tv.tv_usec ^ getpid()); authproto_executable = GetExecutablePathSetting("XSECURELOCK_AUTHPROTO", AUTHPROTO_EXECUTABLE, 0); // Unless disabled, we shift the login prompt randomly around by a few // pixels. This should mostly mitigate burn-in effects from the prompt // being displayed all the time, e.g. because the user's mouse is "shivering" // and thus the auth prompt reappears soon after timeout. burnin_mitigation_max_offset = GetIntSetting("XSECURELOCK_BURNIN_MITIGATION", 16); if (burnin_mitigation_max_offset > 0) { x_offset = rand() % (2 * burnin_mitigation_max_offset + 1) - burnin_mitigation_max_offset; y_offset = rand() % (2 * burnin_mitigation_max_offset + 1) - burnin_mitigation_max_offset; } //! Deprecated flag for setting whether password display should hide the //! length. int paranoid_password_flag; //! Updated flag for password display choice const char *password_prompt_flag; // If requested, mitigate burn-in even more by moving the auth prompt while // displayed. I bet many will find this annoying though. burnin_mitigation_max_offset_change = GetIntSetting("XSECURELOCK_BURNIN_MITIGATION_DYNAMIC", 0); prompt_timeout = GetIntSetting("XSECURELOCK_AUTH_TIMEOUT", 5 * 60); show_username = GetIntSetting("XSECURELOCK_SHOW_USERNAME", 1); show_hostname = GetIntSetting("XSECURELOCK_SHOW_HOSTNAME", 1); paranoid_password_flag = GetIntSetting( "XSECURELOCK_" /* REMOVE IN v2 */ "PARANOID_PASSWORD", 1); password_prompt_flag = GetStringSetting("XSECURELOCK_PASSWORD_PROMPT", ""); show_datetime = GetIntSetting("XSECURELOCK_SHOW_DATETIME", 0); datetime_format = GetStringSetting("XSECURELOCK_DATETIME_FORMAT", "%c"); have_switch_user_command = !!*GetStringSetting("XSECURELOCK_SWITCH_USER_COMMAND", ""); auth_sounds = GetIntSetting("XSECURELOCK_AUTH_SOUNDS", 0); single_auth_window = GetIntSetting("XSECURELOCK_SINGLE_AUTH_WINDOW", 0); auth_cursor_blink = GetIntSetting("XSECURELOCK_AUTH_CURSOR_BLINK", 1); #ifdef HAVE_XKB_EXT show_keyboard_layout = GetIntSetting("XSECURELOCK_SHOW_KEYBOARD_LAYOUT", 1); show_locks_and_latches = GetIntSetting("XSECURELOCK_SHOW_LOCKS_AND_LATCHES", 0); #endif password_prompt = GetPasswordPromptFromFlags(paranoid_password_flag, password_prompt_flag); if ((display = XOpenDisplay(NULL)) == NULL) { Log("Could not connect to $DISPLAY"); return 1; } #ifdef HAVE_XKB_EXT int xkb_opcode, xkb_event_base, xkb_error_base; int xkb_major_version = XkbMajorVersion, xkb_minor_version = XkbMinorVersion; have_xkb_ext = XkbQueryExtension(display, &xkb_opcode, &xkb_event_base, &xkb_error_base, &xkb_major_version, &xkb_minor_version); #endif if (!GetHostName(hostname, sizeof(hostname))) { return 1; } if (!GetUserName(username, sizeof(username))) { return 1; } main_window = ReadWindowID(); if (main_window == None) { Log("Invalid/no window ID in XSCREENSAVER_WINDOW"); return 1; } Window unused_root; Window *unused_children = NULL; unsigned int unused_nchildren; XQueryTree(display, main_window, &unused_root, &parent_window, &unused_children, &unused_nchildren); XFree(unused_children); Colormap colormap = DefaultColormap(display, DefaultScreen(display)); XColor dummy; XAllocNamedColor( display, DefaultColormap(display, DefaultScreen(display)), GetStringSetting("XSECURELOCK_AUTH_BACKGROUND_COLOR", "black"), &xcolor_background, &dummy); XAllocNamedColor( display, DefaultColormap(display, DefaultScreen(display)), GetStringSetting("XSECURELOCK_AUTH_FOREGROUND_COLOR", "white"), &xcolor_foreground, &dummy); XAllocNamedColor(display, DefaultColormap(display, DefaultScreen(display)), GetStringSetting("XSECURELOCK_AUTH_WARNING_COLOR", "red"), &xcolor_warning, &dummy); core_font = NULL; #ifdef HAVE_XFT_EXT xft_font = NULL; #endif const char *font_name = GetStringSetting("XSECURELOCK_FONT", ""); // First try parsing the font name as an X11 core font. We're trying these // first as their font name format is more restrictive (usually starts with a // dash), except for when font aliases are used. int have_font = 0; if (font_name[0] != 0) { core_font = XLoadQueryFont(display, font_name); have_font = (core_font != NULL); #ifdef HAVE_XFT_EXT if (!have_font) { xft_font = FixedXftFontOpenName(display, DefaultScreen(display), font_name); have_font = (xft_font != NULL); } #endif } if (!have_font) { if (font_name[0] != 0) { Log("Could not load the specified font %s - trying a default font", font_name); } #ifdef HAVE_XFT_EXT xft_font = FixedXftFontOpenName(display, DefaultScreen(display), "monospace"); have_font = (xft_font != NULL); #endif } if (!have_font) { core_font = XLoadQueryFont(display, "fixed"); have_font = (core_font != NULL); } if (!have_font) { Log("Could not load a mind-bogglingly stupid font"); return 1; } #ifdef HAVE_XFT_EXT if (xft_font != NULL) { XRenderColor xrcolor; xrcolor.alpha = 65535; // Translate the X11 colors to XRender colors. xrcolor.red = xcolor_foreground.red; xrcolor.green = xcolor_foreground.green; xrcolor.blue = xcolor_foreground.blue; XftColorAllocValue(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), &xrcolor, &xft_color_foreground); xrcolor.red = xcolor_warning.red; xrcolor.green = xcolor_warning.green; xrcolor.blue = xcolor_warning.blue; XftColorAllocValue(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), &xrcolor, &xft_color_warning); } #endif SelectMonitorChangeEvents(display, main_window); InitWaitPgrp(); int status = Authenticate(); // Clear any possible processing message by closing our windows. DestroyPerMonitorWindows(0); #ifdef HAVE_XFT_EXT if (xft_font != NULL) { XftColorFree(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), &xft_color_warning); XftColorFree(display, DefaultVisual(display, DefaultScreen(display)), DefaultColormap(display, DefaultScreen(display)), &xft_color_foreground); XftFontClose(display, xft_font); } #endif XFreeColors(display, colormap, &xcolor_warning.pixel, 1, 0); XFreeColors(display, colormap, &xcolor_foreground.pixel, 1, 0); XFreeColors(display, colormap, &xcolor_background.pixel, 1, 0); return status; } xsecurelock-1.9.0/helpers/authproto_pamtester.in0000644000175000017500000000225213757234024017104 00000000000000#!/bin/sh # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. echo "P 15" echo "Enter password:" read -r ptype len case "$ptype" in p) # Trick: reading len+1 characters get the password and the terminating # newline - just what we need here. if head -c "$((len+1))" |\ @path_to_pamtester@ @pam_service_name@ "$USER" authenticate \ >/dev/null 2>&1; then echo "i 11" echo "I know you." exit 0 else echo "e 17" echo "Invalid password." exit 1 fi ;; x) exit 1 ;; *) echo >&2 "Unexpected packet type." exit 1 ;; esac # Shouldn't get here. exit 42 xsecurelock-1.9.0/saver_child.h0000644000175000017500000000271413757234024013440 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef SAVER_CHILD_H #define SAVER_CHILD_H #include // for Window #include // for Display #define MAX_SAVERS 16 /*! \brief Kill all saver children. * * This can be used from a signal handler. */ void KillAllSaverChildrenSigHandler(int signo); /*! \brief Starts or stops the screen saver child process. * * \param dpy The X11 display. * \param w The screen saver window. Will get cleared after saver child * execution. * \param index The index of the saver to maintain (0 <= index < MAX_SAVERS). * \param executable What binary to spawn for screen saving. No arguments will * be passed. * \param should_be_running If true, the saver child is started if not running * yet; if alse, the saver child will be terminated. */ void WatchSaverChild(Display* dpy, Window w, int index, const char* executable, int should_be_running); #endif xsecurelock-1.9.0/depcomp0000755000175000017500000005602014077246515012364 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2020 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program 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 General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: xsecurelock-1.9.0/autogen.sh0000755000175000017500000000117213757234024013002 00000000000000#!/bin/bash # # Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. set -ex autoreconf -i xsecurelock-1.9.0/Makefile.am0000644000175000017500000001465013757234024013042 00000000000000# Copyright 2014 Google Inc. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. CLEANFILES = macros = \ -DHELPER_PATH=\"$(pkglibexecdir)\" \ -DDOCS_PATH=\"$(docdir)\" \ -DAUTH_EXECUTABLE=\"@auth_executable@\" \ -DAUTHPROTO_EXECUTABLE=\"@authproto_executable@\" \ -DGLOBAL_SAVER_EXECUTABLE=\"@global_saver_executable@\" \ -DSAVER_EXECUTABLE=\"@saver_executable@\" \ -DPAM_SERVICE_NAME=\"@pam_service_name@\" if HAVE_LIBBSD macros += -DHAVE_LIBBSD endif if PAM_CHECK_ACCOUNT_TYPE macros += -DPAM_CHECK_ACCOUNT_TYPE endif if HAVE_DPMS_EXT macros += -DHAVE_DPMS_EXT endif if HAVE_FONTCONFIG macros += -DHAVE_FONTCONFIG endif if HAVE_XSCREENSAVER_EXT macros += -DHAVE_XSCREENSAVER_EXT endif if HAVE_XSYNC_EXT macros += -DHAVE_XSYNC_EXT endif if HAVE_XCOMPOSITE_EXT macros += -DHAVE_XCOMPOSITE_EXT endif if HAVE_XF86MISC_EXT macros += -DHAVE_XF86MISC_EXT endif if HAVE_XFIXES_EXT macros += -DHAVE_XFIXES_EXT endif if HAVE_XFT_EXT macros += -DHAVE_XFT_EXT endif if HAVE_XRANDR_EXT macros += -DHAVE_XRANDR_EXT endif if HAVE_XKB_EXT macros += -DHAVE_XKB_EXT endif bin_PROGRAMS = \ xsecurelock xsecurelock_SOURCES = \ auth_child.c auth_child.h \ env_settings.c env_settings.h \ logging.c logging.h \ mlock_page.h \ main.c \ saver_child.c saver_child.h \ unmap_all.c unmap_all.h \ util.c util.h \ version.c version.h \ wait_pgrp.c wait_pgrp.h \ wm_properties.c wm_properties.h \ xscreensaver_api.c xscreensaver_api.h \ incompatible_compositor.xbm nodist_xsecurelock_SOURCES = \ env_helpstr.inc xsecurelock_CPPFLAGS = $(macros) $(LIBBSD_CFLAGS) xsecurelock_LDADD = $(LIBBSD_LIBS) helpersdir = $(pkglibexecdir) helpers_SCRIPTS = \ helpers/saver_blank if HAVE_HTPASSWD helpers_SCRIPTS += \ helpers/authproto_htpasswd endif if HAVE_MPLAYER helpers_SCRIPTS += \ helpers/saver_mplayer endif if HAVE_MPV helpers_SCRIPTS += \ helpers/saver_mpv endif if HAVE_PAMTESTER helpers_SCRIPTS += \ helpers/authproto_pamtester endif if HAVE_XSCREENSAVER helpers_SCRIPTS += \ helpers/saver_xscreensaver endif helpers_PROGRAMS = \ pgrp_placeholder pgrp_placeholder_SOURCES = \ helpers/pgrp_placeholder.c helpers_PROGRAMS += \ saver_multiplex saver_multiplex_SOURCES = \ env_settings.c env_settings.h \ helpers/monitors.c helpers/monitors.h \ helpers/saver_multiplex.c \ logging.c logging.h \ saver_child.c saver_child.h \ wait_pgrp.c wait_pgrp.h \ wm_properties.c wm_properties.h \ xscreensaver_api.c xscreensaver_api.h saver_multiplex_CPPFLAGS = $(macros) helpers_PROGRAMS += \ dimmer dimmer_SOURCES = \ env_settings.c env_settings.h \ helpers/dimmer.c \ logging.c logging.h \ wm_properties.c wm_properties.h dimmer_CPPFLAGS = $(macros) if HAVE_IDLE_TIMER helpers_PROGRAMS += \ until_nonidle until_nonidle_SOURCES = \ env_settings.c env_settings.h \ helpers/until_nonidle.c \ logging.c logging.h \ wait_pgrp.c wait_pgrp.h until_nonidle_CPPFLAGS = $(macros) endif helpers_PROGRAMS += \ auth_x11 auth_x11_SOURCES = \ env_info.c env_info.h \ env_settings.c env_settings.h \ helpers/authproto.c helpers/authproto.h \ helpers/auth_x11.c \ helpers/monitors.c helpers/monitors.h \ logging.c logging.h \ mlock_page.h \ util.c util.h \ wait_pgrp.c wait_pgrp.h \ wm_properties.c wm_properties.h \ xscreensaver_api.c xscreensaver_api.h auth_x11_CPPFLAGS = $(macros) $(FONTCONFIG_CFLAGS) $(XFT_CFLAGS) $(LIBBSD_CFLAGS) auth_x11_LDADD = $(FONTCONFIG_LIBS) $(XFT_LIBS) $(LIBBSD_LIBS) if HAVE_PAM helpers_PROGRAMS += \ authproto_pam authproto_pam_SOURCES = \ env_info.c env_info.h \ env_settings.c env_settings.h \ helpers/authproto.c helpers/authproto.h \ helpers/authproto_pam.c \ logging.c logging.h \ mlock_page.h \ util.c util.h authproto_pam_CPPFLAGS = $(macros) $(LIBBSD_CFLAGS) authproto_pam_LDADD = $(LIBBSD_LIBS) endif doc_DATA = \ CONTRIBUTING \ LICENSE \ README.md if HAVE_PANDOC man1_MANS = xsecurelock.1 xsecurelock.1.md: doc/xsecurelock.1.md README.md { \ grep -B 9999 'ENV VARIABLES HERE' doc/xsecurelock.1.md; \ grep -A 9999 'ENV VARIABLES START' README.md |\ grep -B 9999 'ENV VARIABLES END'; \ grep -A 9999 'ENV VARIABLES HERE' doc/xsecurelock.1.md; \ } > xsecurelock.1.md xsecurelock.1: xsecurelock.1.md $(path_to_pandoc) -s -f markdown -t man -o $@ $< CLEANFILES += xsecurelock.1.md xsecurelock.1 endif # Some tools that we sure don't wan to install noinst_PROGRAMS = cat_authproto nvidia_break_compositor get_compositor remap_all cat_authproto_SOURCES = \ logging.c logging.h \ helpers/authproto.c helpers/authproto.h \ test/cat_authproto.c nvidia_break_compositor_SOURCES = \ test/nvidia_break_compositor.c nvidia_break_compositor_CPPFLAGS = $(macros) get_compositor_SOURCES = \ test/get_compositor.c get_compositor_CPPFLAGS = $(macros) remap_all_SOURCES= \ test/remap_all.c \ unmap_all.c unmap_all.h remap_all_CPPFLAGS = $(macros) FORCE: version.c: FORCE if [ -n "$(GIT_VERSION)" ]; then \ echo "const char *const git_version = \"$(GIT_VERSION)\";" \ > version.c; \ elif git describe --always --dirty >/dev/null 2>&1; then \ echo "const char *const git_version = \"` \ git describe --always --dirty \ `\";" > version.c; \ else \ : version.c must exist in non-git builds.; \ cat version.c; \ fi .PRECIOUS: version.c # Old one is better than none. env_helpstr.inc: README.md # Autogenerate for --help. grep -A 9999 'ENV VARIABLES START' "$<" |\ grep -B 9999 'ENV VARIABLES END' |\ grep '^[ *]' |\ sed -e 's,\\\|",\\&,g; s,$$,\\n",; s,^,",;' > "$@" xsecurelock-main.$(OBJEXT): env_helpstr.inc EXTRA_DIST = \ CONTRIBUTING \ LICENSE \ README.md \ autogen.sh \ doc/xsecurelock.1.md \ ensure-documented-settings.sh \ helpers/saver_blank \ incompatible_compositor.xbm.sh \ run-iwyu.sh \ run-linters.sh \ test/*.c \ test/*.sh \ test/*.xdo \ version.c examplesdir = $(docdir)/examples dist_examples_DATA = \ doc/examples/saver_livestreams if HAVE_DOXYGEN doxyfile.stamp: $(DOXYGEN) Doxyfile echo Timestamp > doxyfile.stamp CLEANFILES += doxyfile.stamp all-local: doxyfile.stamp clean-local: $(RM) -r $(top_srcdir)/doxy endif xsecurelock-1.9.0/incompatible_compositor.xbm.sh0000755000175000017500000000035313757234024017051 00000000000000#!/bin/sh LF=' ' convert -size 256x128 -gravity center -pointsize 28 \ -font /usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf \ caption:"INCOMPATIBLE COMPOSITOR,${LF}PLEASE FIX!" \ xbm:incompatible_compositor.xbm xsecurelock-1.9.0/unmap_all.h0000644000175000017500000000472113757234024013125 00000000000000/* Copyright 2018 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #ifndef UNMAP_ALL_H #define UNMAP_ALL_H #include // for Window #include // for Display typedef struct { Display *display; Window root_window; // The window list; None windows should be skipped when iterating. Window *windows; unsigned int n_windows; unsigned int first_unmapped_window; } UnmapAllWindowsState; /*! \brief Stores the list of all mapped application windows in the state. * * Note that windows might be created after this has been called, so you * typically want to grab the server first. * * \return true if all is fine, false if a non-ignored window matching my own * window class was found, which should indicate that another instance is * already running. */ int InitUnmapAllWindowsState(UnmapAllWindowsState *state, Display *display, Window root_window, const Window *ignored_windows, unsigned int n_ignored_windows, const char *my_res_class, const char *my_res_name, int include_frame); /*! \brief Unmaps all windows, and stores them in the state. * * After each unmapping it calls just_unmapped_can_we_stop on the window; if * that returns a non-zero value, unmapping stops and we return that value. * * Must be used on the state filled by ListAllWindows. * * \return Nonzero return value of just_unmapped_can_we_stop, or zero if we * unmapped all. */ int UnmapAllWindows(UnmapAllWindowsState *state, int (*just_unmapped_can_we_stop)(Window w, void *arg), void *arg); /*! \brief Remaps all windows from the state. * * Must be used on the state filled by ListAllWindows. */ void RemapAllWindows(UnmapAllWindowsState *state); /*! \brief Clears the UnmapAllWindowsState when done, and returns resources to * X11. */ void ClearUnmapAllWindowsState(UnmapAllWindowsState *state); #endif xsecurelock-1.9.0/main.c0000644000175000017500000016351614503371750012102 00000000000000/* Copyright 2014 Google Inc. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ /*! *\brief XSecureLock. * *XSecureLock is an X11 screen lock utility designed with the primary goal of *security. */ #include // for Window, None, CopyFromParent #include // for XA_CARDINAL, XA_ATOM #include // for XEvent, XMapRaised, XSelectInput #include // for XcmsFailure #include // for XLookupString #include // for XC_arrow #include // for XK_BackSpace, XK_Tab, XK_o #include // for fcntl, FD_CLOEXEC, F_GETFD #include // for NULL, setlocale, LC_CTYPE #include // for sigaction, raise, sa_handler #include // for printf, size_t, snprintf #include // for exit, system, EXIT_FAILURE #include // for memset, strcmp, strncmp #include // for select, timeval, fd_set, FD_SET #include // for gettimeofday #include // for nanosleep, timespec #include // for _exit, chdir, close, execvp #ifdef HAVE_DPMS_EXT #include // for BOOL, CARD16 #include #include // for DPMSModeOff, DPMSModeStandby #endif #ifdef HAVE_XCOMPOSITE_EXT #include // for XCompositeGetOverlayWindow #include "incompatible_compositor.xbm" // for incompatible_compositor_bits #endif #ifdef HAVE_XSCREENSAVER_EXT #include // for ScreenSaverNotify, ScreenSave... #include // for XScreenSaverQueryExtension #endif #ifdef HAVE_XF86MISC_EXT #include // for XF86MiscSetGrabKeysState #endif #ifdef HAVE_XFIXES_EXT #include // for XFixesQueryExtension, XFixesS... #include // for ShapeBounding #endif #include "auth_child.h" // for KillAuthChildSigHandler, Want... #include "env_settings.h" // for GetIntSetting, GetExecutableP... #include "logging.h" // for Log, LogErrno #include "mlock_page.h" // for MLOCK_PAGE #include "saver_child.h" // for WatchSaverChild, KillAllSaver... #include "unmap_all.h" // for ClearUnmapAllWindowsState #include "util.h" // for explicit_bzero #include "version.h" // for git_version #include "wait_pgrp.h" // for WaitPgrp #include "wm_properties.h" // for SetWMProperties /*! \brief How often (in times per second) to watch child processes. * * This defines the minimum frequency to call WatchChildren(). */ #define WATCH_CHILDREN_HZ 10 /*! \brief Try to reinstate grabs in regular intervals. * * This will reinstate the grabs WATCH_CHILDREN_HZ times per second. This * appears to be required with some XScreenSaver hacks that cause XSecureLock to * lose MotionNotify events, but nothing else. */ #undef ALWAYS_REINSTATE_GRABS /*! \brief Try to bring the grab window to foreground in regular intervals. * * Some desktop environments have transparent OverrideRedirect notifications. * These do not send a VisibilityNotify to this window, as some part still * shines through. As a workaround, this enables raising the grab window * periodically. */ #undef AUTO_RAISE /*! \brief Show cursor during auth. * * If enabled, a mouse cursor is shown whenever the auth_window is mapped. * * Note that proper use of this also would require a way to forward click * events to the auth helper, which doesn't exist yet. */ #undef SHOW_CURSOR_DURING_AUTH /*! \brief Exhaustive list of all mouse related X11 events. * * These will be selected for grab. It is important that this contains all * pointer event types, to not let any through to other applications. */ #define ALL_POINTER_EVENTS \ (ButtonPressMask | ButtonReleaseMask | EnterWindowMask | LeaveWindowMask | \ PointerMotionMask | Button1MotionMask | Button2MotionMask | \ Button3MotionMask | Button4MotionMask | Button5MotionMask | \ ButtonMotionMask) //! Private (possibly containing information about the user's password) data. // This data is locked to RAM using mlock() to avoid leakage to disk via swap. struct { // The received X event. XEvent ev; // The decoded key press. char buf[16]; KeySym keysym; // The length of the data in buf. int len; } priv; //! The name of the auth child to execute, relative to HELPER_PATH. const char *auth_executable; //! The name of the saver child to execute, relative to HELPER_PATH. const char *saver_executable; //! The command to run once screen locking is complete. char *const *notify_command = NULL; #ifdef HAVE_XCOMPOSITE_EXT //! Do not use XComposite to cover transparent notifications. int no_composite = 0; //! Create an almost-fullscreen sized "obscurer window" against bad compositors. int composite_obscurer = 0; #endif //! If set, we can start a new login session. int have_switch_user_command = 0; //! If set, we try to force grabbing by "evil" means. int force_grab = 0; //! If set, print window info about any "conflicting" windows to stderr. int debug_window_info = 0; //! If nonnegative, the time in seconds till we blank the screen explicitly. int blank_timeout = -1; //! The DPMS state to switch the screen to when blanking. const char *blank_dpms_state = "off"; //! Whether to reset the saver module when auth closes. int saver_reset_on_auth_close = 0; //! Delay we should wait before starting mapping windows to let children run. int saver_delay_ms = 0; //! Whetever stopping saver when screen is blanked. int saver_stop_on_blank = 0; //! The PID of a currently running notify command, or 0 if none is running. pid_t notify_command_pid = 0; //! The time when we will blank the screen. struct timeval time_to_blank; //! Whether the screen is currently blanked by us. int blanked = 0; #ifdef HAVE_DPMS_EXT //! Whether DPMS needs to be disabled when unblanking. Set when blanking. int must_disable_dpms = 0; #endif //! If set by signal handler we should wake up and prompt for auth. static volatile sig_atomic_t signal_wakeup = 0; void ResetBlankScreenTimer(void) { if (blank_timeout < 0) { return; } gettimeofday(&time_to_blank, NULL); time_to_blank.tv_sec += blank_timeout; } void InitBlankScreen(void) { if (blank_timeout < 0) { return; } blanked = 0; ResetBlankScreenTimer(); } void MaybeBlankScreen(Display *display) { if (blank_timeout < 0 || blanked) { return; } struct timeval now; gettimeofday(&now, NULL); if (now.tv_sec < time_to_blank.tv_sec || (now.tv_sec == time_to_blank.tv_sec && now.tv_usec < time_to_blank.tv_usec)) { return; } // Blank timer expired - blank the screen. blanked = 1; XForceScreenSaver(display, ScreenSaverActive); if (!strcmp(blank_dpms_state, "on")) { // Just X11 blanking. goto done; } #ifdef HAVE_DPMS_EXT // If we get here, we want to do DPMS blanking. int dummy; if (!DPMSQueryExtension(display, &dummy, &dummy)) { Log("DPMS is unavailable and XSECURELOCK_BLANK_DPMS_STATE not on"); goto done; } CARD16 state; BOOL onoff; DPMSInfo(display, &state, &onoff); if (!onoff) { // DPMS not active by user - so we gotta force it. must_disable_dpms = 1; DPMSEnable(display); } if (!strcmp(blank_dpms_state, "standby")) { DPMSForceLevel(display, DPMSModeStandby); } else if (!strcmp(blank_dpms_state, "suspend")) { DPMSForceLevel(display, DPMSModeSuspend); } else if (!strcmp(blank_dpms_state, "off")) { DPMSForceLevel(display, DPMSModeOff); } else { Log("XSECURELOCK_BLANK_DPMS_STATE not in standby/suspend/off/on"); } #else Log("DPMS is not compiled in and XSECURELOCK_BLANK_DPMS_STATE not on"); #endif done: // Flush the output buffer so we turn off the display now and not a few ms // later. XFlush(display); } void ScreenNoLongerBlanked(Display *display) { #ifdef HAVE_DPMS_EXT if (must_disable_dpms) { DPMSDisable(display); must_disable_dpms = 0; // Flush the output buffer so we turn on the display now and not a // few ms later. Makes our and X11's idle timer more consistent. XFlush(display); } #endif blanked = 0; } void UnblankScreen(Display *display) { if (blanked) { XForceScreenSaver(display, ScreenSaverReset); ScreenNoLongerBlanked(display); } ResetBlankScreenTimer(); } static void HandleSIGTERM(int signo) { KillAllSaverChildrenSigHandler(signo); // Dirty, but quick. KillAuthChildSigHandler(signo); // More dirty. explicit_bzero(&priv, sizeof(priv)); raise(signo); } static void HandleSIGUSR2(int unused_signo) { (void)unused_signo; signal_wakeup = 1; } enum WatchChildrenState { //! Request saver child. WATCH_CHILDREN_NORMAL, //! Request no saver to run (DPMS!). WATCH_CHILDREN_SAVER_DISABLED, //! Request auth child. WATCH_CHILDREN_FORCE_AUTH }; /*! \brief Watch the child processes, and bring them into the desired state. * * If the requested state is WATCH_CHILDREN_NORMAL and neither auth nor saver * child are running, the saver child will be spawned. * * If the requested state is WATCH_CHILDREN_SAVER_DISABLED, a possibly running * saver child will be killed. * * If the requested state is WATCH_CHILDREN_FORCE_AUTH, a possibly running saver * child will be killed, and an auth child will be spawned. * * If the auth child was already running, the stdinbuf is sent to the auth child * on standard input. * * \param state The request to perform. * \param stdinbuf Key presses to send to the auth child, if set. * \return If true, authentication was successful and the program should exit. */ int WatchChildren(Display *dpy, Window auth_win, Window saver_win, enum WatchChildrenState state, const char *stdinbuf) { int want_auth = WantAuthChild(state == WATCH_CHILDREN_FORCE_AUTH); int auth_running = 0; // Note: want_auth is true whenever we WANT to run authentication, or it is // already running. It may have recently terminated, which we will notice // later. if (want_auth) { // Actually start the auth child, or notice termination. if (WatchAuthChild(auth_win, auth_executable, state == WATCH_CHILDREN_FORCE_AUTH, stdinbuf, &auth_running)) { // Auth performed successfully. Terminate the other children. WatchSaverChild(dpy, saver_win, 0, saver_executable, 0); // Now terminate the screen lock. return 1; } // If we wanted auth, but it's not running, auth just terminated. Unmap the // auth window and poke the screensaver so that it can reset any timeouts. if (!auth_running) { XUnmapWindow(dpy, auth_win); if (saver_reset_on_auth_close) { KillAllSaverChildrenSigHandler(SIGUSR1); } } } // Show the screen saver. WatchSaverChild(dpy, saver_win, 0, saver_executable, state != WATCH_CHILDREN_SAVER_DISABLED); if (auth_running) { // While auth is running, we never blank. UnblankScreen(dpy); } else { // If no auth is running, permit blanking as per timer. MaybeBlankScreen(dpy); } // Do not terminate the screen lock. return 0; } /*! \brief Wake up the screen saver in response to a keyboard or mouse event. * * \return If true, authentication was successful, and the program should exit. */ int WakeUp(Display *dpy, Window auth_win, Window saver_win, const char *stdinbuf) { return WatchChildren(dpy, auth_win, saver_win, WATCH_CHILDREN_FORCE_AUTH, stdinbuf); } /*! \brief An X11 error handler that merely logs errors to stderr. * * This is used to prevent X11 errors from terminating XSecureLock. */ int JustLogErrorsHandler(Display *display, XErrorEvent *error) { char buf[128]; XGetErrorText(display, error->error_code, buf, sizeof(buf)); buf[sizeof(buf) - 1] = 0; Log("Got non-fatal X11 error: %s", buf); return 0; } /*! \brief An X11 error handler that does nothing at all. * * This is used for calls where we expect errors to happen. */ int SilentlyIgnoreErrorsHandler(Display *display, XErrorEvent *error) { (void)display; (void)error; return 0; } /*! \brief Print a version message. */ void Version(void) { printf("XSecureLock - X11 screen lock utility designed for security.\n"); if (*git_version) { printf("Version: %s\n", git_version); } else { printf("Version unknown.\n"); } } /*! \brief Print an usage message. * * A message is shown explaining how to use XSecureLock. * * \param me The name this executable was invoked as. */ void Usage(const char *me) { Version(); printf( "\n" "Usage:\n" " env [variables...] %s [-- command to run when locked]\n" "\n" "Environment variables you may set for XSecureLock and its modules:\n" "\n" #include "env_helpstr.inc" // IWYU pragma: keep "\n" "Configured default auth module: " AUTH_EXECUTABLE "\n" "Configured default authproto module: " AUTHPROTO_EXECUTABLE "\n" "Configured default global saver module: " GLOBAL_SAVER_EXECUTABLE "\n" "Configured default per-screen saver module: " SAVER_EXECUTABLE "\n" "\n" "This software is licensed under the Apache 2.0 License. Details are\n" "available at the following location:\n" " " DOCS_PATH "/COPYING\n", me, "%s", // For XSECURELOCK_KEY_%s_COMMAND. "%s"); // For XSECURELOCK_KEY_%s_COMMAND's description. } /*! \brief Load default settings from environment variables. * * These settings override what was figured out at ./configure time. */ void LoadDefaults() { auth_executable = GetExecutablePathSetting("XSECURELOCK_AUTH", AUTH_EXECUTABLE, 1); saver_executable = GetExecutablePathSetting("XSECURELOCK_GLOBAL_SAVER", GLOBAL_SAVER_EXECUTABLE, 0); #ifdef HAVE_XCOMPOSITE_EXT no_composite = GetIntSetting("XSECURELOCK_NO_COMPOSITE", 0); composite_obscurer = GetIntSetting("XSECURELOCK_COMPOSITE_OBSCURER", 1); #endif have_switch_user_command = *GetStringSetting("XSECURELOCK_SWITCH_USER_COMMAND", ""); force_grab = GetIntSetting("XSECURELOCK_FORCE_GRAB", 0); debug_window_info = GetIntSetting("XSECURELOCK_DEBUG_WINDOW_INFO", 0); blank_timeout = GetIntSetting("XSECURELOCK_BLANK_TIMEOUT", 600); blank_dpms_state = GetStringSetting("XSECURELOCK_BLANK_DPMS_STATE", "off"); saver_reset_on_auth_close = GetIntSetting("XSECURELOCK_SAVER_RESET_ON_AUTH_CLOSE", 0); saver_delay_ms = GetIntSetting("XSECURELOCK_SAVER_DELAY_MS", 0); saver_stop_on_blank = GetIntSetting("XSECURELOCK_SAVER_STOP_ON_BLANK", 1); } /*! \brief Parse the command line arguments, or exit in case of failure. * * This accepts saver_* or auth_* arguments, and puts them in their respective * global variable. * * This is DEPRECATED - use the XSECURELOCK_SAVER and XSECURELOCK_AUTH * environment variables instead! * * Possible errors will be printed on stderr. */ void ParseArgumentsOrExit(int argc, char **argv) { for (int i = 1; i < argc; ++i) { if (!strncmp(argv[i], "auth_", 5)) { Log("Setting auth child name from command line is DEPRECATED. Use " "the XSECURELOCK_AUTH environment variable instead"); auth_executable = argv[i]; continue; } if (!strncmp(argv[i], "saver_", 6)) { Log("Setting saver child name from command line is DEPRECATED. Use " "the XSECURELOCK_SAVER environment variable instead"); saver_executable = argv[i]; continue; } if (!strcmp(argv[i], "--")) { notify_command = argv + i + 1; break; } if (!strcmp(argv[i], "--help")) { Usage(argv[0]); exit(0); } if (!strcmp(argv[i], "--version")) { Version(); exit(0); } // If we get here, the argument is unrecognized. Exit, then. Log("Unrecognized argument: %s", argv[i]); Usage(argv[0]); exit(0); } } /*! \brief Check the settings. * * This tests whether the selected auth and saver executables are valid and * actually executable. Failure of this could lead to an un-unlockable screen, * and we sure don't want that. * * Possible errors will be printed on stderr. * * \return true if everything is OK, false otherwise. */ int CheckSettings() { // Flawfinder note: the access() calls here are not security relevant and just // prevent accidentally running with a nonexisting saver or auth executable as // that could make the system un-unlockable. if (auth_executable == NULL) { Log("Auth module has not been specified in any way"); return 0; } if (saver_executable == NULL) { Log("Saver module has not been specified in any way"); return 0; } return 1; } /*! \brief Print some debug info about a window. * * Only enabled if debug_window_info is set. * * Spammy. */ void DebugDumpWindowInfo(Window w) { if (!debug_window_info) { return; } char buf[128]; // Note: process has to be backgrounded (&) because we may be within // XGrabServer. int buflen = snprintf(buf, sizeof(buf), "{ xwininfo -all -id %lu; xprop -id %lu; } >&2 &", w, w); if (buflen <= 0 || (size_t)buflen >= sizeof(buf)) { Log("Wow, pretty large integers you got there"); return; } system(buf); } /*! \brief Raise a window if necessary. * * Does not cause any events if the window is already on the top. * \param display The X11 display. * \param w The window to raise. * \param silent Whether to output something if we can't detect what is wrong. * \param force Whether to always raise our window, even if we can't find what * covers us. Set this only if confident that there is something overlapping * us, like in response to a negative VisibilityNotify. */ void MaybeRaiseWindow(Display *display, Window w, int silent, int force) { int need_raise = force; Window root, parent; Window *children, *siblings; unsigned int nchildren, nsiblings; if (XQueryTree(display, w, &root, &parent, &children, &nchildren)) { XFree(children); Window grandparent; if (!XQueryTree(display, parent, &root, &grandparent, &siblings, &nsiblings)) { Log("XQueryTree failed on the parent"); siblings = NULL; nsiblings = 0; } } else { Log("XQueryTree failed on self"); siblings = NULL; nsiblings = 0; } if (nsiblings == 0) { Log("No siblings found"); } else { if (w == siblings[nsiblings - 1]) { // But we _are_ on top...? if (force && !silent) { // We have evidence of something covering us, but cannot locate it. Log("MaybeRaiseWindow miss: something obscured my window %lu but I " "can't find it", w); } } else { // We found what's covering us. Log("MaybeRaiseWindow hit: window %lu was above my window %lu", siblings[nsiblings - 1], w); DebugDumpWindowInfo(siblings[nsiblings - 1]); need_raise = 1; } } XFree(siblings); if (need_raise) { XRaiseWindow(display, w); } } typedef struct { Display *display; Window root_window; Cursor cursor; int silent; } AcquireGrabsState; int TryAcquireGrabs(Window w, void *state_voidp) { AcquireGrabsState *state = state_voidp; int ok = 1; if (XGrabPointer(state->display, state->root_window, False, ALL_POINTER_EVENTS, GrabModeAsync, GrabModeAsync, None, state->cursor, CurrentTime) != GrabSuccess) { if (!state->silent) { Log("Critical: cannot grab pointer"); } ok = 0; } if (XGrabKeyboard(state->display, state->root_window, False, GrabModeAsync, GrabModeAsync, CurrentTime) != GrabSuccess) { if (!state->silent) { Log("Critical: cannot grab keyboard"); } ok = 0; } if (w != None) { Log("Unmapped window %lu to force grabbing, which %s", w, ok ? "succeeded" : "didn't help"); if (ok) { DebugDumpWindowInfo(w); } } return ok; } /*! \brief Acquire all necessary grabs to lock the screen. * * \param display The X11 display. * \param root_window The root window. * \param silent Do not log errors. * \param force Try extra hard (1), or even harder (2). The latter mode will * very likely interfere strongly with window managers. \return true if grabbing * succeeded, false otherwise. */ int AcquireGrabs(Display *display, Window root_window, Window *ignored_windows, unsigned int n_ignored_windows, Cursor cursor, int silent, int force) { AcquireGrabsState grab_state; grab_state.display = display; grab_state.root_window = root_window; grab_state.cursor = cursor; grab_state.silent = silent; if (!force) { // Easy case. return TryAcquireGrabs(None, &grab_state); } XGrabServer(display); // Critical section. UnmapAllWindowsState unmap_state; int ok; if (InitUnmapAllWindowsState(&unmap_state, display, root_window, ignored_windows, n_ignored_windows, "xsecurelock", NULL, force > 1)) { Log("Trying to force grabbing by unmapping all windows. BAD HACK"); ok = UnmapAllWindows(&unmap_state, TryAcquireGrabs, &grab_state); RemapAllWindows(&unmap_state); } else { Log("Found XSecureLock to be already running, not forcing"); ok = TryAcquireGrabs(None, &grab_state); } ClearUnmapAllWindowsState(&unmap_state); XUngrabServer(display); // Always flush the display after this to ensure the server is only // grabbed for as long as needed, and to make absolutely sure that // remapping did happen. XFlush(display); return ok; } /*! \brief Tell xss-lock or others that we're done locking. * * This enables xss-lock to delay going to sleep until the screen is actually * locked - useful to prevent information leaks after wakeup. * * \param fd The file descriptor of the X11 connection that we shouldn't close. */ void NotifyOfLock(int xss_sleep_lock_fd) { if (xss_sleep_lock_fd != -1) { if (close(xss_sleep_lock_fd) != 0) { LogErrno("close(XSS_SLEEP_LOCK_FD)"); } } if (notify_command != NULL && *notify_command != NULL) { pid_t pid = ForkWithoutSigHandlers(); if (pid == -1) { LogErrno("fork"); } else if (pid == 0) { // Child process. execvp(notify_command[0], notify_command); LogErrno("execvp"); _exit(EXIT_FAILURE); } else { // Parent process after successful fork. notify_command_pid = pid; } } } int CheckLockingEffectiveness() { // When this variable is set, all checks in here are still evaluated but we // try locking anyway. int error_status = 0; const char *error_string = "Will not lock"; if (GetIntSetting("XSECURELOCK_DEBUG_ALLOW_LOCKING_IF_INEFFECTIVE", 0)) { error_status = 1; error_string = "Locking anyway"; } // Do not try "locking" a Wayland session. Although everything we do appears // to work on XWayland, our grab will only affect X11 and not Wayland // clients, and therefore the lock will not be effective. If you need to get // around this check for testing, just unset the WAYLAND_DISPLAY environment // variable before starting XSecureLock. But really, this won't be secure in // any way... if (*GetStringSetting("WAYLAND_DISPLAY", "")) { Log("Wayland detected. This would only lock the X11 part of your session. " "%s", error_string); return error_status; } // Inside a VNC session, we better don't lock, as users might think it locked // their client when it actually only locked the remote. if (*GetStringSetting("VNCDESKTOP", "")) { Log("VNC detected. This would only lock your remote session. %s", error_string); return error_status; } // Inside a Chrome Remote Desktop session, we better don't lock, as users // might think it locked their client when it actually only locked the remote. if (*GetStringSetting("CHROME_REMOTE_DESKTOP_SESSION", "")) { Log("Chrome Remote Desktop detected. This would only lock your remote " "session. %s", error_string); return error_status; } return 1; } /*! \brief The main program. * * Usage: see Usage(). */ int main(int argc, char **argv) { setlocale(LC_CTYPE, ""); int xss_sleep_lock_fd = GetIntSetting("XSS_SLEEP_LOCK_FD", -1); if (xss_sleep_lock_fd != -1) { // Children processes should not inherit the sleep lock // Failures are not critical, systemd will ignore the lock // when InhibitDelayMaxSec is reached int flags = fcntl(xss_sleep_lock_fd, F_GETFD); if (flags == -1) { LogErrno("fcntl(XSS_SLEEP_LOCK_FD, F_GETFD)"); } else { flags |= FD_CLOEXEC; int status = fcntl(xss_sleep_lock_fd, F_SETFD, flags); if (status == -1) { LogErrno("fcntl(XSS_SLEEP_LOCK_FD, F_SETFD, %#x)", flags); } } } // Switch to the root directory to not hold on to any directory descriptors // (just in case you started xsecurelock from a directory you want to unmount // later). if (chdir("/")) { Log("Could not switch to the root directory"); return 1; } // Test if HELPER_PATH is accessible; if not, we will likely have a problem. if (access(HELPER_PATH "/", X_OK)) { Log("Could not access directory %s", HELPER_PATH); return 1; } // Parse and verify arguments. LoadDefaults(); ParseArgumentsOrExit(argc, argv); if (!CheckSettings()) { Usage(argv[0]); return 1; } // Check if we are in a lockable session. if (!CheckLockingEffectiveness()) { return 1; } // Connect to X11. Display *display = XOpenDisplay(NULL); if (display == NULL) { Log("Could not connect to $DISPLAY"); return 1; } // TODO(divVerent): Support that? if (ScreenCount(display) != 1) { Log("Warning: 'Zaphod' configurations are not supported at this point. " "Only locking the default screen.\n"); } // My windows. Window my_windows[4]; unsigned int n_my_windows = 0; // Who's the root? Window root_window = DefaultRootWindow(display); // Query the initial screen size, and get notified on updates. Also we're // going to grab on the root window, so FocusOut events about losing the grab // will appear there. XSelectInput(display, root_window, StructureNotifyMask | FocusChangeMask); int w = DisplayWidth(display, DefaultScreen(display)); int h = DisplayHeight(display, DefaultScreen(display)); #ifdef DEBUG_EVENTS Log("DisplayWidthHeight %d %d", w, h); #endif // Prepare some nice window attributes for a screen saver window. XColor black; black.pixel = BlackPixel(display, DefaultScreen(display)); XQueryColor(display, DefaultColormap(display, DefaultScreen(display)), &black); XColor xcolor_background; XColor dummy; int status = XAllocNamedColor( display, DefaultColormap(display, DefaultScreen(display)), GetStringSetting("XSECURELOCK_BACKGROUND_COLOR", "black"), &xcolor_background, &dummy); unsigned long background_pixel = black.pixel; if (status != XcmsFailure) { background_pixel = xcolor_background.pixel; } Pixmap bg = XCreateBitmapFromData(display, root_window, "\0", 1, 1); Cursor default_cursor = XCreateFontCursor(display, XC_arrow); Cursor transparent_cursor = XCreatePixmapCursor(display, bg, bg, &black, &black, 0, 0); XSetWindowAttributes coverattrs = {0}; coverattrs.background_pixel = background_pixel; coverattrs.save_under = 1; coverattrs.override_redirect = 1; coverattrs.cursor = transparent_cursor; Window parent_window = root_window; #ifdef HAVE_XCOMPOSITE_EXT int composite_event_base, composite_error_base, composite_major_version = 0, composite_minor_version = 0; int have_xcomposite_ext = XCompositeQueryExtension(display, &composite_event_base, &composite_error_base) && // Require at least XComposite 0.3. XCompositeQueryVersion(display, &composite_major_version, &composite_minor_version) && (composite_major_version >= 1 || composite_minor_version >= 3); if (!have_xcomposite_ext) { Log("XComposite extension not detected"); } if (have_xcomposite_ext && no_composite) { Log("XComposite extension detected but disabled by user"); have_xcomposite_ext = 0; } Window composite_window = None, obscurer_window = None; if (have_xcomposite_ext) { composite_window = XCompositeGetOverlayWindow(display, root_window); // Some compositers may unmap or shape the overlay window - undo that, just // in case. XMapRaised(display, composite_window); #ifdef HAVE_XFIXES_EXT int xfixes_event_base, xfixes_error_base; if (XFixesQueryExtension(display, &xfixes_event_base, &xfixes_error_base)) { XFixesSetWindowShapeRegion(display, composite_window, ShapeBounding, // 0, 0, 0); } #endif parent_window = composite_window; if (composite_obscurer) { // Also create an "obscurer window" that we don't actually use but that // covers almost everything in case the composite window temporarily does // not work (e.g. in case the compositor hides the COW). We are making // the obscurer window actually white, so issues like this become visible // but harmless. The window isn't full-sized to avoid compositors turning // off themselves in response to a full-screen window, but nevertheless // this is kept opt-in for now until shown reliable. XSetWindowAttributes obscurerattrs = coverattrs; obscurerattrs.background_pixmap = XCreatePixmapFromBitmapData( display, root_window, incompatible_compositor_bits, incompatible_compositor_width, incompatible_compositor_height, BlackPixel(display, DefaultScreen(display)), WhitePixel(display, DefaultScreen(display)), DefaultDepth(display, DefaultScreen(display))); obscurer_window = XCreateWindow( display, root_window, 1, 1, w - 2, h - 2, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixmap | CWSaveUnder | CWOverrideRedirect | CWCursor, &obscurerattrs); SetWMProperties(display, obscurer_window, "xsecurelock", "obscurer", argc, argv); my_windows[n_my_windows++] = obscurer_window; } } #endif // Create the windows. // background_window is the outer window which exists for security reasons (in // case a subprocess may turn its window transparent or something). // saver_window is the "visible" window that the saver child will draw on. // auth_window is a window exclusively used by the auth child. It will only be // mapped during auth, and hidden otherwise. These windows are separated // because XScreenSaver's savers might XUngrabKeyboard on their window. Window background_window = XCreateWindow( display, parent_window, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel | CWSaveUnder | CWOverrideRedirect | CWCursor, &coverattrs); SetWMProperties(display, background_window, "xsecurelock", "background", argc, argv); my_windows[n_my_windows++] = background_window; Window saver_window = XCreateWindow(display, background_window, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel, &coverattrs); SetWMProperties(display, saver_window, "xsecurelock", "saver", argc, argv); my_windows[n_my_windows++] = saver_window; Window auth_window = XCreateWindow(display, background_window, 0, 0, w, h, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel, &coverattrs); SetWMProperties(display, auth_window, "xsecurelock", "auth", argc, argv); my_windows[n_my_windows++] = auth_window; // Let's get notified if we lose visibility, so we can self-raise. #ifdef HAVE_XCOMPOSITE_EXT if (composite_window != None) { XSelectInput(display, composite_window, StructureNotifyMask | VisibilityChangeMask); } if (obscurer_window != None) { XSelectInput(display, obscurer_window, StructureNotifyMask | VisibilityChangeMask); } #endif XSelectInput(display, background_window, StructureNotifyMask | VisibilityChangeMask); XSelectInput(display, saver_window, StructureNotifyMask); XSelectInput(display, auth_window, StructureNotifyMask | VisibilityChangeMask); // Make sure we stay always on top. XWindowChanges coverchanges; coverchanges.stack_mode = Above; XConfigureWindow(display, background_window, CWStackMode, &coverchanges); XConfigureWindow(display, auth_window, CWStackMode, &coverchanges); // We're OverrideRedirect anyway, but setting this hint may help compositors // leave our window alone. Atom state_atom = XInternAtom(display, "_NET_WM_STATE", False); Atom fullscreen_atom = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", False); XChangeProperty(display, background_window, state_atom, XA_ATOM, 32, PropModeReplace, (const unsigned char *)&fullscreen_atom, 1); // Bypass compositing, just in case. Atom dont_composite_atom = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); long dont_composite = 1; XChangeProperty(display, background_window, dont_composite_atom, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *)&dont_composite, 1); #ifdef HAVE_XCOMPOSITE_EXT if (composite_window != None) { // Also set this property on the Composite Overlay Window, just in case a // compositor were to try compositing it (xcompmgr does, but doesn't know // this property anyway). XChangeProperty(display, composite_window, dont_composite_atom, XA_CARDINAL, 32, PropModeReplace, (const unsigned char *)&dont_composite, 1); } // Note: NOT setting this on the obscurer window, as this is a fallback and // actually should be composited to make sure the compositor never draws // anything "interesting". #endif // Initialize XInput so we can get multibyte key events. XIM xim = XOpenIM(display, NULL, NULL, NULL); if (xim == NULL) { Log("XOpenIM failed. Assuming Latin-1 encoding"); } XIC xic = NULL; if (xim != NULL) { // As we're OverrideRedirect and grabbing input, we can't use any fancy IMs. // Therefore, if we can't get a requirement-less IM, we won't use XIM at // all. int input_styles[4] = { XIMPreeditNothing | XIMStatusNothing, // Status might be invisible. XIMPreeditNothing | XIMStatusNone, // Maybe a compose key. XIMPreeditNone | XIMStatusNothing, // Status might be invisible. XIMPreeditNone | XIMStatusNone // Standard handling. }; for (size_t i = 0; i < sizeof(input_styles) / sizeof(input_styles[0]); ++i) { // Note: we draw XIM stuff in auth_window so it's above the saver/auth // child. However, we receive events for the grab window. xic = XCreateIC(xim, XNInputStyle, input_styles[i], XNClientWindow, auth_window, NULL); if (xic != NULL) { break; } } if (xic == NULL) { Log("XCreateIC failed. Assuming Latin-1 encoding"); } } #ifdef HAVE_XSCREENSAVER_EXT // If we support the screen saver extension, that'd be good. int scrnsaver_event_base, scrnsaver_error_base; if (!XScreenSaverQueryExtension(display, &scrnsaver_event_base, &scrnsaver_error_base)) { scrnsaver_event_base = 0; scrnsaver_error_base = 0; } XScreenSaverSelectInput(display, background_window, ScreenSaverNotifyMask); #endif #ifdef HAVE_XF86MISC_EXT // In case keys to disable grabs are available, turn them off for the duration // of the lock. if (XF86MiscSetGrabKeysState(display, False) != MiscExtGrabStateSuccess) { Log("Could not set grab keys state"); return EXIT_FAILURE; } #endif // Acquire all grabs we need. Retry in case the window manager is still // holding some grabs while starting XSecureLock. int last_normal_attempt = force_grab ? 1 : 0; Window previous_focused_window = None; int previous_revert_focus_to = RevertToNone; int retries = 10; for (; retries >= 0; --retries) { if (AcquireGrabs(display, root_window, my_windows, n_my_windows, transparent_cursor, /*silent=*/retries > last_normal_attempt, /*force=*/retries < last_normal_attempt)) { break; } if (previous_focused_window == None) { // When retrying for the first time, try to change the X11 input focus. // This may close context menus and thereby allow us to grab. XGetInputFocus(display, &previous_focused_window, &previous_revert_focus_to); // We don't have any window mapped yet, but it should be a safe bet to set // focus to the root window (most WMs allow for an easy way out from that // predicament in case our restore logic fails). XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime); // Make this happen instantly. XFlush(display); } nanosleep(&(const struct timespec){0, 100000000L}, NULL); } if (retries < 0) { Log("Failed to grab. Giving up."); return EXIT_FAILURE; } if (MLOCK_PAGE(&priv, sizeof(priv)) < 0) { LogErrno("mlock"); return EXIT_FAILURE; } struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = SIG_IGN; // Don't die if auth child closes stdin. if (sigaction(SIGPIPE, &sa, NULL) != 0) { LogErrno("sigaction(SIGPIPE)"); } sa.sa_handler = HandleSIGUSR2; // For remote wakeups by system events. if (sigaction(SIGUSR2, &sa, NULL) != 0) { LogErrno("sigaction(SIGUSR2)"); } sa.sa_flags = SA_RESETHAND; // It re-raises to suicide. sa.sa_handler = HandleSIGTERM; // To kill children. if (sigaction(SIGTERM, &sa, NULL) != 0) { LogErrno("sigaction(SIGTERM)"); } InitWaitPgrp(); // Need to flush the display so savers sure can access the window. XFlush(display); // Figure out the initial Xss saver state. This gets updated by event. enum WatchChildrenState xss_requested_saver_state = WATCH_CHILDREN_NORMAL; #ifdef HAVE_XSCREENSAVER_EXT if (scrnsaver_event_base != 0) { XScreenSaverInfo *info = XScreenSaverAllocInfo(); XScreenSaverQueryInfo(display, root_window, info); if (info->state == ScreenSaverOn && info->kind == ScreenSaverBlanked && saver_stop_on_blank) { xss_requested_saver_state = WATCH_CHILDREN_SAVER_DISABLED; } XFree(info); } #endif InitBlankScreen(); XFlush(display); if (WatchChildren(display, auth_window, saver_window, xss_requested_saver_state, NULL)) { goto done; } // Wait for children to initialize. struct timespec sleep_ts; sleep_ts.tv_sec = saver_delay_ms / 1000; sleep_ts.tv_nsec = (saver_delay_ms % 1000) * 1000000L; nanosleep(&sleep_ts, NULL); // Map our windows. // This is done after grabbing so failure to grab does not blank the screen // yet, thereby "confirming" the screen lock. XMapRaised(display, saver_window); XMapRaised(display, background_window); XClearWindow(display, background_window); // Workaround for bad drivers. XRaiseWindow(display, auth_window); // Don't map here. #ifdef HAVE_XCOMPOSITE_EXT if (obscurer_window != None) { // Map the obscurer window last so it should never become visible. XMapRaised(display, obscurer_window); } #endif XFlush(display); // Prevent X11 errors from killing XSecureLock. Instead, just keep going. XSetErrorHandler(JustLogErrorsHandler); int x11_fd = ConnectionNumber(display); if (x11_fd == xss_sleep_lock_fd && xss_sleep_lock_fd != -1) { Log("XSS_SLEEP_LOCK_FD matches DISPLAY - what?!? We're probably " "inhibiting sleep now"); xss_sleep_lock_fd = -1; } int background_window_mapped = 0, background_window_visible = 0, auth_window_mapped = 0, saver_window_mapped = 0, need_to_reinstate_grabs = 0, xss_lock_notified = 0; for (;;) { // Watch children WATCH_CHILDREN_HZ times per second. fd_set in_fds; memset(&in_fds, 0, sizeof(in_fds)); // For clang-analyzer. FD_ZERO(&in_fds); FD_SET(x11_fd, &in_fds); struct timeval tv; tv.tv_usec = 1000000 / WATCH_CHILDREN_HZ; tv.tv_sec = 0; select(x11_fd + 1, &in_fds, 0, 0, &tv); // Make sure to shut down the saver when blanked. Saves power. enum WatchChildrenState requested_saver_state = (saver_stop_on_blank && blanked) ? WATCH_CHILDREN_SAVER_DISABLED : xss_requested_saver_state; // Now check status of our children. if (WatchChildren(display, auth_window, saver_window, requested_saver_state, NULL)) { goto done; } // If something changed our cursor, change it back. XUndefineCursor(display, saver_window); #ifdef ALWAYS_REINSTATE_GRABS // This really should never be needed... (void)need_to_reinstate_grabs; need_to_reinstate_grabs = 1; #endif if (need_to_reinstate_grabs) { need_to_reinstate_grabs = 0; if (!AcquireGrabs(display, root_window, my_windows, n_my_windows, transparent_cursor, 0, 0)) { Log("Critical: could not reacquire grabs. The screen is now UNLOCKED! " "Trying again next frame."); need_to_reinstate_grabs = 1; } } #ifdef AUTO_RAISE if (auth_window_mapped) { MaybeRaiseWindow(display, auth_window, 0, 0); } MaybeRaiseWindow(display, background_window, 0, 0); #ifdef HAVE_XCOMPOSITE_EXT if (obscurer_window != None) { MaybeRaiseWindow(display, obscurer_window, 1, 0); } #endif #endif // Take care of zombies. if (notify_command_pid != 0) { int status; WaitProc("notify", ¬ify_command_pid, 0, 0, &status); // Otherwise, we're still alive. Re-check next time. } if (signal_wakeup) { // A signal was received to request a wakeup. Clear the flag // and proceed to auth. Technically this check involves a race // condition between check and set since we don't block signals, // but this should not make a difference since screen wakeup is // idempotent. signal_wakeup = 0; #ifdef DEBUG_EVENTS Log("WakeUp on signal"); #endif UnblankScreen(display); if (WakeUp(display, auth_window, saver_window, NULL)) { goto done; } } // Handle all events. while (XPending(display) && (XNextEvent(display, &priv.ev), 1)) { if (XFilterEvent(&priv.ev, None)) { // If an input method ate the event, ignore it. continue; } switch (priv.ev.type) { case ConfigureNotify: #ifdef DEBUG_EVENTS Log("ConfigureNotify %lu %d %d", (unsigned long)priv.ev.xconfigure.window, priv.ev.xconfigure.width, priv.ev.xconfigure.height); #endif if (priv.ev.xconfigure.window == root_window) { // Root window size changed. Adjust the saver_window window too! w = priv.ev.xconfigure.width; h = priv.ev.xconfigure.height; #ifdef DEBUG_EVENTS Log("DisplayWidthHeight %d %d", w, h); #endif #ifdef HAVE_XCOMPOSITE_EXT if (obscurer_window != None) { XMoveResizeWindow(display, obscurer_window, 1, 1, w - 2, h - 2); } #endif XMoveResizeWindow(display, background_window, 0, 0, w, h); XClearWindow(display, background_window); // Workaround for bad drivers. XMoveResizeWindow(display, saver_window, 0, 0, w, h); // Just in case - ConfigureNotify might also be sent for raising } // Also, whatever window has been reconfigured, should also be raised // to make sure. if (auth_window_mapped && priv.ev.xconfigure.window == auth_window) { MaybeRaiseWindow(display, auth_window, 0, 0); } else if (priv.ev.xconfigure.window == background_window) { MaybeRaiseWindow(display, background_window, 0, 0); XClearWindow(display, background_window); // Workaround for bad drivers. #ifdef HAVE_XCOMPOSITE_EXT } else if (obscurer_window != None && priv.ev.xconfigure.window == obscurer_window) { MaybeRaiseWindow(display, obscurer_window, 1, 0); #endif } break; case VisibilityNotify: #ifdef DEBUG_EVENTS Log("VisibilityNotify %lu %d", (unsigned long)priv.ev.xvisibility.window, priv.ev.xvisibility.state); #endif if (priv.ev.xvisibility.state == VisibilityUnobscured) { if (priv.ev.xvisibility.window == background_window) { background_window_visible = 1; } } else { // If something else shows an OverrideRedirect window, we want to // stay on top. if (auth_window_mapped && priv.ev.xvisibility.window == auth_window) { Log("Someone overlapped the auth window. Undoing that"); MaybeRaiseWindow(display, auth_window, 0, 1); } else if (priv.ev.xvisibility.window == background_window) { background_window_visible = 0; Log("Someone overlapped the background window. Undoing that"); MaybeRaiseWindow(display, background_window, 0, 1); XClearWindow(display, background_window); // Workaround for bad drivers. #ifdef HAVE_XCOMPOSITE_EXT } else if (obscurer_window != None && priv.ev.xvisibility.window == obscurer_window) { // Not logging this as our own composite overlay window causes // this to happen too; keeping this there anyway so we self-raise // if something is wrong with the COW and something else overlaps // us. MaybeRaiseWindow(display, obscurer_window, 1, 1); } else if (composite_window != None && priv.ev.xvisibility.window == composite_window) { Log("Someone overlapped the composite overlay window window. " "Undoing that"); // Note: MaybeRaiseWindow isn't valid here, as the COW has the // root as parent without being a proper child of it. Let's just // raise the COW unconditionally. XRaiseWindow(display, composite_window); #endif } else { Log("Received unexpected VisibilityNotify for window %lu", priv.ev.xvisibility.window); } } break; case MotionNotify: case ButtonPress: // Mouse events launch the auth child. ScreenNoLongerBlanked(display); if (WakeUp(display, auth_window, saver_window, NULL)) { goto done; } break; case KeyPress: { // Keyboard events launch the auth child. ScreenNoLongerBlanked(display); Status status = XLookupNone; int have_key = 1; int do_wake_up = 1; priv.keysym = NoSymbol; if (xic) { // This uses the current locale. priv.len = XmbLookupString(xic, &priv.ev.xkey, priv.buf, sizeof(priv.buf) - 1, &priv.keysym, &status); if (priv.len <= 0) { // Error or no output. Fine. have_key = 0; } else if (status != XLookupChars && status != XLookupBoth) { // Got nothing new. have_key = 0; } } else { // This is always Latin-1. Sorry. priv.len = XLookupString(&priv.ev.xkey, priv.buf, sizeof(priv.buf) - 1, &priv.keysym, NULL); if (priv.len <= 0) { // Error or no output. Fine. have_key = 0; } } if (have_key && (size_t)priv.len >= sizeof(priv.buf)) { // Detect possible overruns. This should be unreachable. Log("Received invalid length from XLookupString: %d", priv.len); have_key = 0; } if (priv.keysym == XK_Tab && (priv.ev.xkey.state & ControlMask)) { // Map Ctrl-Tab to Ctrl-S (switch layout). We remap this one // because not all layouts have a key for S. priv.buf[0] = '\023'; priv.buf[1] = 0; } else if (priv.keysym == XK_BackSpace && (priv.ev.xkey.state & ControlMask)) { // Map Ctrl-Backspace to Ctrl-U (clear entry line). priv.buf[0] = '\025'; priv.buf[1] = 0; } else if (have_switch_user_command && (priv.keysym == XK_o || priv.keysym == XK_0) && (((priv.ev.xkey.state & ControlMask) && (priv.ev.xkey.state & Mod1Mask)) || (priv.ev.xkey.state & Mod4Mask))) { // Switch to greeter on Ctrl-Alt-O or Win-O. system("eval \"$XSECURELOCK_SWITCH_USER_COMMAND\" &"); // And send a Ctrl-U (clear entry line). priv.buf[0] = '\025'; priv.buf[1] = 0; } else if (have_key) { // Map all newline-like things to newlines. if (priv.len == 1 && priv.buf[0] == '\r') { priv.buf[0] = '\n'; } priv.buf[priv.len] = 0; } else { // No new bytes. Fine. priv.buf[0] = 0; // We do check if something external wants to handle this key, // though. const char *keyname = XKeysymToString(priv.keysym); if (keyname != NULL) { char buf[64]; int buflen = snprintf(buf, sizeof(buf), "XSECURELOCK_KEY_%s_COMMAND", keyname); if (buflen <= 0 || (size_t)buflen >= sizeof(buf)) { Log("Wow, pretty long keysym names you got there"); } else { const char *command = GetStringSetting(buf, ""); if (*command) { buflen = snprintf(buf, sizeof(buf), "eval \"$XSECURELOCK_KEY_%s_COMMAND\" &", keyname); if (buflen <= 0 || (size_t)buflen >= sizeof(buf)) { Log("Wow, pretty long keysym names you got there"); } else { system(buf); do_wake_up = 0; } } } } } // Now if so desired, wake up the login prompt, and check its // status. int authenticated = do_wake_up ? WakeUp(display, auth_window, saver_window, priv.buf) : 0; // Clear out keypress data immediately. explicit_bzero(&priv, sizeof(priv)); if (authenticated) { goto done; } } break; case KeyRelease: case ButtonRelease: // Known to wake up screen blanking. ScreenNoLongerBlanked(display); break; case MappingNotify: case EnterNotify: case LeaveNotify: // Ignored. break; case MapNotify: #ifdef DEBUG_EVENTS Log("MapNotify %lu", (unsigned long)priv.ev.xmap.window); #endif if (priv.ev.xmap.window == auth_window) { auth_window_mapped = 1; #ifdef SHOW_CURSOR_DURING_AUTH // Actually ShowCursor... XGrabPointer(display, root_window, False, ALL_POINTER_EVENTS, GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime); #endif } else if (priv.ev.xmap.window == saver_window) { saver_window_mapped = 1; } else if (priv.ev.xmap.window == background_window) { background_window_mapped = 1; } break; case UnmapNotify: #ifdef DEBUG_EVENTS Log("UnmapNotify %lu", (unsigned long)priv.ev.xmap.window); #endif if (priv.ev.xmap.window == auth_window) { auth_window_mapped = 0; #ifdef SHOW_CURSOR_DURING_AUTH // Actually HideCursor... XGrabPointer(display, root_window, False, ALL_POINTER_EVENTS, GrabModeAsync, GrabModeAsync, None, transparent_cursor, CurrentTime); #endif } else if (priv.ev.xmap.window == saver_window) { // This should never happen, but let's handle it anyway. Log("Someone unmapped the saver window. Undoing that"); saver_window_mapped = 0; XMapWindow(display, saver_window); } else if (priv.ev.xmap.window == background_window) { // This should never happen, but let's handle it anyway. Log("Someone unmapped the background window. Undoing that"); background_window_mapped = 0; XMapRaised(display, background_window); XClearWindow(display, background_window); // Workaround for bad drivers. #ifdef HAVE_XCOMPOSITE_EXT } else if (obscurer_window != None && priv.ev.xmap.window == obscurer_window) { // This should never happen, but let's handle it anyway. Log("Someone unmapped the obscurer window. Undoing that"); XMapRaised(display, obscurer_window); } else if (composite_window != None && priv.ev.xmap.window == composite_window) { // This should never happen, but let's handle it anyway. // Compton might do this when --unredir-if-possible is set and a // fullscreen game launches while the screen is locked. Log("Someone unmapped the composite overlay window. Undoing " "that"); XMapRaised(display, composite_window); #endif } else if (priv.ev.xmap.window == root_window) { // This should never happen, but let's handle it anyway. Log("Someone unmapped the root window?!? Undoing that"); XMapRaised(display, root_window); } break; case FocusIn: case FocusOut: #ifdef DEBUG_EVENTS Log("Focus%d %lu", priv.ev.xfocus.mode, (unsigned long)priv.ev.xfocus.window); #endif if (priv.ev.xfocus.window == root_window && priv.ev.xfocus.mode == NotifyUngrab) { // Not logging this - this is a normal occurrence if invoking the // screen lock from a key combination, as the press event may // launch xsecurelock while the release event releases a passive // grab. We still immediately try to reacquire grabs here, though. if (!AcquireGrabs(display, root_window, my_windows, n_my_windows, transparent_cursor, 0, 0)) { Log("Critical: could not reacquire grabs after NotifyUngrab. " "The screen is now UNLOCKED! Trying again next frame."); need_to_reinstate_grabs = 1; } } break; case ClientMessage: { if (priv.ev.xclient.window == root_window) { // ClientMessage on root window is used by the EWMH spec. No need to // spam about those. As we want to watch the root window size, // we must keep selecting StructureNotifyMask there. break; } // Those cause spam below, so let's log them separately to get some // details. const char *message_type = XGetAtomName(display, priv.ev.xclient.message_type); Log("Received unexpected ClientMessage event %s on window %lu", message_type == NULL ? "(null)" : message_type, priv.ev.xclient.window); break; } default: #ifdef DEBUG_EVENTS Log("Event%d %lu", priv.ev.type, (unsigned long)priv.ev.xany.window); #endif #ifdef HAVE_XSCREENSAVER_EXT // Handle screen saver notifications. If the screen is blanked // anyway, turn off the saver child. if (scrnsaver_event_base != 0 && priv.ev.type == scrnsaver_event_base + ScreenSaverNotify) { XScreenSaverNotifyEvent *xss_ev = (XScreenSaverNotifyEvent *)&priv.ev; if (xss_ev->state == ScreenSaverOn) { xss_requested_saver_state = WATCH_CHILDREN_SAVER_DISABLED; } else { xss_requested_saver_state = WATCH_CHILDREN_NORMAL; } break; } #endif Log("Received unexpected event %d", priv.ev.type); break; } if (background_window_mapped && background_window_visible && saver_window_mapped && !xss_lock_notified) { NotifyOfLock(xss_sleep_lock_fd); xss_lock_notified = 1; } } } done: // Make sure no DPMS changes persist. UnblankScreen(display); if (previous_focused_window != None) { XSetErrorHandler(SilentlyIgnoreErrorsHandler); XSetInputFocus(display, previous_focused_window, previous_revert_focus_to, CurrentTime); XSetErrorHandler(JustLogErrorsHandler); } // Wipe the password. explicit_bzero(&priv, sizeof(priv)); // Free our resources, and exit. #ifdef HAVE_XCOMPOSITE_EXT if (obscurer_window != None) { // Destroy the obscurer window first so it should never become visible. XDestroyWindow(display, obscurer_window); } if (composite_window != None) { XCompositeReleaseOverlayWindow(display, composite_window); } #endif XDestroyWindow(display, auth_window); XDestroyWindow(display, saver_window); XDestroyWindow(display, background_window); XFreeCursor(display, transparent_cursor); XFreeCursor(display, default_cursor); XFreePixmap(display, bg); XCloseDisplay(display); return EXIT_SUCCESS; } xsecurelock-1.9.0/Doxyfile.in0000644000175000017500000030374613757234024013130 00000000000000# Doxyfile 1.8.6 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = @PACKAGE_NAME@ # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = @PACKAGE_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "XSecureLock is an X11 screen lock utility." # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels # and the maximum width should not exceed 200 pixels. Doxygen will copy the logo # to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = doxy # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce a # new page for each member. If set to NO, the documentation of a member will be # part of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. # # Note For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by by putting a % sign in front of the word # or globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = YES # If the EXTRACT_PACKAGE tag is set to YES all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = YES # If the EXTRACT_STATIC tag is set to YES all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined # locally in source files will be included in the documentation. If set to NO # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = YES # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = YES # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO these classes will be included in the various overviews. This option has # no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable ( YES) or disable ( NO) the # todo list. This list is created by putting \todo commands in the # documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable ( YES) or disable ( NO) the # test list. This list is created by putting \test commands in the # documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable ( YES) or disable ( NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable ( YES) or disable ( NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES the list # will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. Do not use file names with spaces, bibtex cannot handle them. See # also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES, then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO doxygen will only warn about wrong or incomplete parameter # documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. # Note: If this tag is empty the current directory is searched. INPUT = @top_srcdir@ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank the # following patterns are tested:*.c, *.cc, *.cxx, *.cpp, *.c++, *.java, *.ii, # *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, # *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, # *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf, # *.qsf, *.as and *.js. FILE_PATTERNS = # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER ) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = README.md #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify an additional user- # defined cascading style sheet that is included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefor more robust against future updates. # Doxygen will copy the style sheet file to the output directory. For an example # see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the stylesheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to NO can help when comparing the output of multiple runs. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler ( hhc.exe). If non-empty # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated ( # YES) or that it should be included in the master .chm file ( NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index ( hhk), content ( hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated ( # YES) or a normal table of contents ( NO) in the .chm file. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom stylesheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using prerendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /