diff --git a/README.md b/README.md index 109ae2f..0544bab 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ This was designed to save me some sanity in maintaining it as well as easily int - dwmblocks-async - slock - nsxiv +- slim ### Terminal - dvtm @@ -25,6 +26,12 @@ This was designed to save me some sanity in maintaining it as well as easily int ## Notes +### slim notes + +To reload the preset theme (located in `/usr/share/themes/slim/suckless`), then run `.cache/wal/slim-reload` **after** running pywal. The script must also be ran as sudo. + +This is, if you wanted a display manager added. The scripts also works a workaround unless if slim could handle loading themes from other dirs. + ### eww notes The weather widget is pretty barebones as of now. Report bugs if found. diff --git a/config/wal/templates/slim-reload b/config/wal/templates/slim-reload new file mode 100755 index 0000000..53c9a2c --- /dev/null +++ b/config/wal/templates/slim-reload @@ -0,0 +1,5 @@ +#!/bin/sh +# Reloads slim (needs sudo to ask the passswoes) + +sudo cp .cache/wal/slim.theme /usr/share/slim/themes/suckless +sudo cp {wallpaper} /usr/share/slim/themes/suckless/background.png diff --git a/config/wal/templates/slim.theme b/config/wal/templates/slim.theme new file mode 100644 index 0000000..f51d547 --- /dev/null +++ b/config/wal/templates/slim.theme @@ -0,0 +1,52 @@ +# Green glass theme for SLiM +# by Rob Pearce + +# Messages (e.g. shutdown) +msg_color {color15} +msg_font Hack Nerd Font:size=18:bold:dpi=75 +msg_x 50% +msg_y 40% +msg_shadow_color #702342 +msg_shadow_xoffset 1 +msg_shadow_yoffset 1 + +# valid values: stretch, tile +background_style stretch +background_color {color0} + +# Input controls +input_panel_x 50% +input_panel_y 45% +input_name_x 394 +input_name_y 142 +input_pass_x 394 +input_pass_y 178 +input_font Hack Nerd Font:size=12:dpi=75 +input_color {color0} + +# Username / password request +username_font Hack Nerd Font:size=18:dpi=75 +username_color {color15} +username_x 270 +username_y 144 +password_x 270 +password_y 180 +username_shadow_color {color0} +username_shadow_xoffset 1 +username_shadow_yoffset 1 + +username_msg Username: +password_msg Password: + +# Welcome message +welcome_font Hack Nerd Font:size=28:bold:dpi=75 +welcome_color {color1} +welcome_x 50% +welcome_y 40 +welcome_msg Login to %host +welcome_shadow_xoffset 2 +welcome_shadow_yoffset 2 +welcome_shadow_color {color0} + +passwd_feedback_x 50% +passwd_feedback_y 80% diff --git a/slim/CMakeLists.txt b/slim/CMakeLists.txt new file mode 100644 index 0000000..628c86e --- /dev/null +++ b/slim/CMakeLists.txt @@ -0,0 +1,252 @@ +cmake_minimum_required(VERSION 2.8.12 FATAL_ERROR) + +set(PROJECT_NAME slim) +project(${PROJECT_NAME}) + +#Pretty colors +set(CMAKE_COLOR_MAKEFILE ON) +#Dont force verbose +set(CMAKE_VERBOSE_MAKEFILE ON) +#Include current dir +set(CMAKE_INCLUDE_CURRENT_DIR TRUE) + +INCLUDE(CheckIncludeFile) +INCLUDE(CheckCCompilerFlag) +INCLUDE(CheckCXXCompilerFlag) +INCLUDE(CheckTypeSize) +INCLUDE(GNUInstallDirs) + +# Version +set(SLIM_VERSION_MAJOR "1") +set(SLIM_VERSION_MINOR "4") +set(SLIM_VERSION_PATCH "0") +set(SLIM_VERSION "${SLIM_VERSION_MAJOR}.${SLIM_VERSION_MINOR}.${SLIM_VERSION_PATCH}") + +set(PKGDATADIR "${CMAKE_INSTALL_FULL_DATADIR}/slim") +set(SYSCONFDIR "/etc") + +set(SLIM_DEFINITIONS) +if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR + ${CMAKE_SYSTEM_NAME} MATCHES "NetBSD" OR + ${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD" + ) + set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DNEEDS_BASENAME") +else() + set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DHAVE_SHADOW") +endif() + +set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DAPPNAME=\"${PROJECT_NAME}\"") +set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DVERSION=\"${SLIM_VERSION}\"") +set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DPKGDATADIR=\"${PKGDATADIR}\"") +set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DSYSCONFDIR=\"${SYSCONFDIR}\"") + +# source +set(slim_srcs + main.cpp + app.cpp + numlock.cpp + switchuser.cpp + png.c + jpeg.c +) + +set(slimlock_srcs + slimlock.cpp +) + +set(common_srcs + cfg.cpp + image.cpp + log.cpp + panel.cpp + util.cpp +) +if(USE_PAM) + set(common_srcs ${common_srcs} PAM.cpp) + # used to always build slimlock if we are using PAM. Removed as per Devuan + # set(BUILD_SLIMLOCK 1) +endif(USE_PAM) + +# Build common library +set(BUILD_SHARED_LIBS ON CACHE BOOL "Build shared libraries") + +if (BUILD_SHARED_LIBS) + message(STATUS "Enable shared library building") + add_library(libslim ${common_srcs}) +else(BUILD_SHARED_LIBS) + message(STATUS "Disable shared library building") + add_library(libslim STATIC ${common_srcs}) +endif(BUILD_SHARED_LIBS) + +if(USE_CONSOLEKIT) + set(slim_srcs ${slim_srcs} Ck.cpp) +endif(USE_CONSOLEKIT) + +add_executable(slim ${slim_srcs}) +if(BUILD_SLIMLOCK) + add_executable(slimlock ${slimlock_srcs}) +endif(BUILD_SLIMLOCK) + +#Set the custom CMake module directory where our include/lib finders are +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") + +find_package(X11 REQUIRED) +find_package(Freetype REQUIRED) +find_package(JPEG REQUIRED) +find_package(PNG REQUIRED) + +# Fontconfig +set(FONTCONFIG_DIR ${CMAKE_MODULE_PATH}) +find_package(FONTCONFIG REQUIRED) +if(FONTCONFIG_FOUND) + message("\tFontConfig Found") + target_link_libraries(slim ${FONTCONFIG_LIBRARY}) + include_directories(${FONTCONFIG_INCLUDE_DIR}) +endif(FONTCONFIG_FOUND) + +# PAM +if(USE_PAM) + message("\tPAM Enabled") + find_package(PAM) + if(PAM_FOUND) + message("\tPAM Found") + set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DUSE_PAM") + target_link_libraries(slim ${PAM_LIBRARY}) + target_link_libraries(libslim ${PAM_LIBRARY}) + if(BUILD_SLIMLOCK) + target_link_libraries(slimlock ${PAM_LIBRARY}) + endif(BUILD_SLIMLOCK) + include_directories(${PAM_INCLUDE_DIR}) + else(PAM_FOUND) + message("\tPAM Not Found") + endif(PAM_FOUND) +else(USE_PAM) + message("\tPAM disabled") +endif(USE_PAM) + +# ConsoleKit +if(USE_CONSOLEKIT) + find_package(CkConnector) + message("\tConsoleKit Enabled") + if(CKCONNECTOR_FOUND) + message("\tConsoleKit Found") + # DBus check + find_package(DBus REQUIRED) + if(DBUS_FOUND) + message("\tDBus Found") + target_link_libraries(slim ${DBUS_LIBRARIES}) + include_directories(${DBUS_ARCH_INCLUDE_DIR}) + include_directories(${DBUS_INCLUDE_DIR}) + set(SLIM_DEFINITIONS ${SLIM_DEFINITIONS} "-DUSE_CONSOLEKIT") + target_link_libraries(slim ${CKCONNECTOR_LIBRARIES}) + include_directories(${CKCONNECTOR_INCLUDE_DIR}) + else(DBUS_FOUND) + message("\tDBus Not Found") + endif(DBUS_FOUND) + else(CKCONNECTOR_FOUND) + message("\tConsoleKit Not Found") + message("\tConsoleKit disabled") + endif(CKCONNECTOR_FOUND) +else(USE_CONSOLEKIT) + message("\tConsoleKit disabled") +endif(USE_CONSOLEKIT) + +# system librarys +find_library(M_LIB m) +find_library(RT_LIB rt) +find_library(CRYPTO_LIB crypt) +find_package(Threads) + +add_definitions(${SLIM_DEFINITIONS}) + +#Set up include dirs with all found packages +include_directories( + ${X11_INCLUDE_DIR} + ${X11_Xft_INCLUDE_PATH} + ${X11_Xrender_INCLUDE_PATH} + ${X11_Xrandr_INCLUDE_PATH} + ${FREETYPE_INCLUDE_DIRS} + ${X11_Xmu_INCLUDE_PATH} + ${ZLIB_INCLUDE_DIR} + ${JPEG_INCLUDE_DIR} + ${PNG_INCLUDE_DIR} +) + +target_link_libraries(libslim + ${RT_LIB} + ${X11_Xft_LIB} + ${X11_Xrandr_LIB} + ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} +) + +#Set up library with all found packages for slim +target_link_libraries(slim + ${M_LIB} + ${RT_LIB} + ${CRYPTO_LIB} + ${X11_X11_LIB} + ${X11_Xft_LIB} + ${X11_Xrender_LIB} + ${X11_Xrandr_LIB} + ${X11_Xmu_LIB} + ${FREETYPE_LIBRARY} + ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} + libslim +) + +if(BUILD_SLIMLOCK) + #Set up library with all found packages for slimlock + target_link_libraries(slimlock + ${M_LIB} + ${RT_LIB} + ${CRYPTO_LIB} + ${X11_X11_LIB} + ${X11_Xft_LIB} + ${X11_Xrender_LIB} + ${X11_Xrandr_LIB} + ${X11_Xmu_LIB} + ${X11_Xext_LIB} + ${FREETYPE_LIBRARY} + ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} + libslim + ) +endif(BUILD_SLIMLOCK) + +####### install +# slim +install(TARGETS slim RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + +if (BUILD_SHARED_LIBS) + set_target_properties(libslim PROPERTIES + OUTPUT_NAME slim + SOVERSION ${SLIM_VERSION}) + + install(TARGETS libslim + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) +endif (BUILD_SHARED_LIBS) + +# man file +install(FILES slim.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + +# configure - in theory we should use CMAKE_INSTALL_SYSCONFDIR but that doesn't work +install(FILES slim.conf DESTINATION ${SYSCONFDIR}) + +# systemd service file - commented out as broken - not all Linux uses systemd! +#if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") +# install(FILES slim.service DESTINATION ${LIBDIR}/systemd/system) +#endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux") + +#slimlock +if(BUILD_SLIMLOCK) + install(TARGETS slimlock RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE SETUID) + install(FILES slimlock.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) +endif(BUILD_SLIMLOCK) + +# themes directory +subdirs(themes) diff --git a/slim/COPYING b/slim/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/slim/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/slim/ChangeLog b/slim/ChangeLog new file mode 100644 index 0000000..095ba44 --- /dev/null +++ b/slim/ChangeLog @@ -0,0 +1,196 @@ +1.4.0 - 2023.01.21 + * - BREAKING CONFIG CHANGE - the position of the passwd_feedback in slim + is now relative to the screen, not the panel. This is now consistent + with slimlock and with the session and other messages. + * The selected session is now preserved over failed logins + * When testing themes with the -p option, the size of the window can now + be specified, and the theme is previewed correctly for that size. + * On exit from theme test, the "test message" now reports user and session + * Failed login feedback is now disabled by default or if position negative + * Applied some Devuan patches + - Set the XDG_SESSION_CLASS. This fixes Gentoo bug #727544 - failure to + register sessions with elogind in some instances. The (incorrect) bug + note in the man page has therefore been removed again. + - Use $DISPLAY instead of hardcoding :0.0 in sessionstart/stop commands + - No longer always builds slimlock if using PAM - it must be explicitly + enabled + - Fixed formatting in slimlock man page + - A couple of typos fixed in strings + * Updated the README and encoded in UTF-8 + * Corrected the handling of the -n / -nodaemon option so that it doesn't + swallow the next option if given as -n + * Bug fixes in slimlock + - The active entry is for password, so show the password prompt, not the + username one + - Don't react to F1, as it isn't appropriate and used to blank the + screen + - Keep it all on one screen even when the WM hasn't set a "primary" for + RandR purposes + * Fix ticket #4 - the config parser now works properly. Previously it got + confused by option names that were substrings of other option names + * Themes with "background_style color" no longer need a redundant image + file to make them work. + - This needed a bit of a re-write of the image handling, which also + improves efficiency + * New default theme - the old one is retained as "original" + * Some general documentation improvements (man pages, comments in example + files) + +1.3.9 - 2022.11.18 + * Changed the handling of the "auth failed" message in DM mode so that it + remains on display until a key is pressed. + * Added a command line option to specify the configuration file + * Allow the logfile to be specified as "stderr" to write to stderr + Also now writes all log messages to stderr if they are printed before + the log file is open, including when using the -p option. + * Added a configuration option to retain the user name if authentication + fails (which is probably more helpful if you just mistype your password) + * Applied a modified version of the "wait for the X server" patch from guix + * No longer unnecessarily re-opens an already open X display interface. + Similarly removed a redundant "get root window" call + * Deleted some unused member variables and methods. Various other internal + clean-up + * Reverted the install location definition for the configuration file, as + the CMAKE_INSTALL_SYSCONFDIR wasn't right + * Added a "bug" note to the man page, for the reliance on sessreg, which + causes occasional misbehaviour for some people. + +1.3.8 - 2022.03.01 + * Fixed some bugs listed on bugs.gentoo.org: + 832303 - failed to build with clang, Invalid suffix on string literal + 580458 - open fd on /var/log/slim.log passed to session + * Fixed handling of log stream so that all the code uses the same instance + * Handle return values from calls, to clean up warn-unused-result warnings + * Fixed "sessions" config option (the earlier patch was incomplete) + * Several QA improvements + * Updated cmake config - use standard install paths, don't force options + +1.3.7 - 2022.01.30 + * Imported several bug fixes from the Gentoo package: + libslim-cmake-fixes + disable-ck-for-systemd + reinstate sessions config option as an alternative to sessiondir + systemd session compatibility + remove zlib dependency + envcpy-bad-pointer-arithmetic patch + add-missing-libgen_h + wrong comparison for XCreateGC error return (GCC11 needs it fixed!) + * Fixed a bug in handling expired user accounts with PAM + * Show a message on login failure (using the same method as slimlock) + + + --- The releases below were the original SLiM project --- + +1.3.6 - 2013.10.01 + * Merge slimlock. + * Add support ^H (like backspace). + * And fix some bugs. + +1.3.5 - 2012.12.31 + * Support UTF8 string. + * Add systemd service. + * And fix some bugs. + +1.3.4 - 2012.06.26 + * Replaced stderr writes function. + * Fix numlock control. + * Fix build with GLUT. + * Fix PAM authentication. + +1.3.3 - 2012.02.22 + * Change build system to CMake. + * Add support ConsoleKit. + * Fix some bugs.... + +1.3.2 - 2010.07.08 + * Add support xauth secret. + * Add xnest_debug mode. + +1.3.1 - 2008.09.26 + * Added focus_password config option for focusing password + automatically when default_user is enabled + * Added auto_login option + * Fixed uninitialized daemonmode, see + http://www.freebsd.org/cgi/query-pr.cgi?pr=114366 + * Fixed maximum length for password + * Introduced customization options for session text: + font, colors, position, shadows. + +1.3.0 - 2006.07.14 + * Added PAM support by Martin Parm + * Fixed segfault on exit when testing themes. Thanks + to Darren Salt & Mike Massonnet + * Fixed vt argument detection, thanks to Henrik Brix Andersen + * Corrected reference to input_color in the default theme + * Fixed default shell setting + * Fix segfault when calling XCloseDisplay(NULL); thanks Uli Schlachter + +1.2.6 - 2006.09.15 + * Bug #008167: Update pid when in daemon mode + * Fixed warnings when compiling with -Wall. Thanks to + KIMURA Masaru + * Fixed major memory leaks with repeated login (bug #007535) + +1.2.5 - 2006.07.24 + * hiding of the cursor is now an option (disabled + by default) since some WMs does not re-initialize + the root window cursor. + * The X server is restarted when the user logs out. + This fixes potential security issues with user-launched + apps staying attached to the root window after logout. + * Bug #7432 : Added proper Xauth authentication: the X server + is started with the -auth option and the user who logs + in has his .Xauthority file initializated. + +1.2.4 - 2006.01.18 + * Added commands for session start and stop + (i.e. for session registering) + * Added automatic numlock on/off option + * Support for numpad Enter key + * Restored support for daemon option in the config + file. + * Lock file now uses process id, no more false + locking (thanks to Tobias Roth) + +1.2.3 - 2005.09.11 + * Added FreeBSD, NetBSD, OpenBSD support + * Replaced autotools with plain makefile(s) + * Added 'suspend' command (untested, we don't use it) + * Added support for %theme variable in login command + +1.2.2 - 2005.05.21 + * fix panel drawing on screens <= 1024x768 + * Don't start X server unless valid theme found + * revert to 'default' of invalid theme specified + * try all themes from a set if one doesn't work + +1.2.1 - 2005.05.17 + * draw input directly on panel + +1.2.0 - 2005.05.16 + * added theme preview (slim -p /path/to/theme) + * added JPEG support for panel image + * added 'center' background type and 'background_color' option + * added text shadow + * added warning when execution of login command fails + * Fix login failure when no shell specified in /etc/passwd + * Print error when login command execution fails + * add XNEST_DEBUG ifdef's to allow for easy debugging + * Add support for Ctrl-u and Ctrl-w + * Add 'vt07' to server arguments if not already specified + * Removes daemon option from the config file. Use slim -d + * Allow 'current_theme' to be a set of themes, choose randomly + * Change default theme + +1.1.0 - 2004.12.09 + * error messages for X11 apps are no longer redirected + to the log file + * fixed text position for default theme + * added configurable shutdown and reboot messages + * separated 'Enter username' and 'Enter password' messages + position. + * due to the previous two points, the theme format has + slightly changed + +1.0.0 - 2004.12.07 + * First public SLiM release diff --git a/slim/Ck.cpp b/slim/Ck.cpp new file mode 100644 index 0000000..dad4f05 --- /dev/null +++ b/slim/Ck.cpp @@ -0,0 +1,152 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 2011 David Hauweele + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include + +#include +#include +#include +#include + +#include "Ck.h" + +namespace Ck +{ + Exception::Exception(const std::string &func, const std::string &errstr) + : func(func), errstr(errstr) + { + } + + dbus_bool_t Session::ck_connector_open_graphic_session( + const std::string &display, + uid_t uid) + { + dbus_bool_t local = true; + const char *session_type = "x11"; + const char *x11_display = display.c_str(); + const char *x11_device = get_x11_device(display); + const char *remote_host = ""; + const char *display_dev = ""; + + return ck_connector_open_session_with_parameters(ckc, &error, + "unix-user", &uid, + "session-type", &session_type, + "x11-display", &x11_display, + "x11-display-device", &x11_device, + "display-device", &display_dev, + "remote-host-name", &remote_host, + "is-local", &local, + NULL); + } + + const char * Session::get_x11_device(const std::string &display) + { + static char device[32]; + + Display *xdisplay = XOpenDisplay(display.c_str()); + + if(!xdisplay) + throw Exception(__func__, "cannot open display"); + + Window root; + Atom xfree86_vt_atom; + Atom return_type_atom; + int return_format; + unsigned long return_count; + unsigned long bytes_left; + unsigned char *return_value; + long vt; + + xfree86_vt_atom = XInternAtom(xdisplay, "XFree86_VT", true); + + if(xfree86_vt_atom == None) + throw Exception(__func__, "cannot get XFree86_VT"); + + root = DefaultRootWindow(xdisplay); + + if(XGetWindowProperty(xdisplay, root, xfree86_vt_atom, + 0L, 1L, false, XA_INTEGER, + &return_type_atom, &return_format, + &return_count, &bytes_left, + &return_value) != Success) + throw Exception(__func__, "cannot get root window property"); + + if(return_type_atom != XA_INTEGER) + throw Exception(__func__, "bad atom type"); + + if(return_format != 32) + throw Exception(__func__, "invalid return format"); + + if(return_count != 1) + throw Exception(__func__, "invalid count"); + + if(bytes_left != 0) + throw Exception(__func__, "invalid bytes left"); + + vt = *((long *)return_value); + + std::sprintf(device, "/dev/tty%ld", vt); + + if(return_value) + XFree(return_value); + + return device; + } + + void Session::open_session(const std::string &display, uid_t uid) + { + ckc = ck_connector_new(); + + if(!ckc) + throw Exception(__func__, "error setting up connection to ConsoleKit"); + + if (!ck_connector_open_graphic_session(display, uid)) { + if(dbus_error_is_set(&error)) + throw Exception(__func__, error.message); + else + throw Exception(__func__, "cannot open ConsoleKit session: OOM," + " DBus system bus not available or insufficient" + " privileges"); + } + } + + const char * Session::get_xdg_session_cookie() + { + return ck_connector_get_cookie(ckc); + } + + void Session::close_session() + { + if(!ck_connector_close_session(ckc, &error)) { + if(dbus_error_is_set(&error)) + throw Exception(__func__, error.message); + else + throw Exception(__func__, "cannot close ConsoleKit session: OOM," + " DBus system bus not available or insufficient" + " privileges"); + } + } + + Session::Session() + { + dbus_error_init(&error); + } + + Session::~Session() + { + dbus_error_free(&error); + } +} + +std::ostream& operator<<( std::ostream& os, const Ck::Exception& e) +{ + os << e.func << ": " << e.errstr; + return os; +} diff --git a/slim/Ck.h b/slim/Ck.h new file mode 100644 index 0000000..a3f8486 --- /dev/null +++ b/slim/Ck.h @@ -0,0 +1,49 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 2007 Martin Parm + * + * 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 of the License, or + *(at your option) any later version. + */ + +#ifndef _CK_H_ +#define _CK_H_ + +#include + +#include +#include + +namespace Ck +{ + class Exception + { + public: + std::string func; + std::string errstr; + Exception(const std::string &func, const std::string &errstr); + }; + + class Session + { + private: + CkConnector *ckc; + DBusError error; + + const char *get_x11_device(const std::string &display); + dbus_bool_t ck_connector_open_graphic_session(const std::string &display, + uid_t uid); + public: + const char *get_xdg_session_cookie(); + void open_session(const std::string &display, uid_t uid); + void close_session(); + + Session(); + ~Session(); + }; +} + +std::ostream &operator<<(std::ostream &os, const Ck::Exception &e); + +#endif /* _CK_H_ */ diff --git a/slim/INSTALL b/slim/INSTALL new file mode 100644 index 0000000..3e41238 --- /dev/null +++ b/slim/INSTALL @@ -0,0 +1,39 @@ +INSTALL file for SLiM + +0. Prerequisites: + - cmake + - X.org or XFree86 + - libxmu + - libpng + - libjpeg + +1. to build and install the program: + o Using the ccmake interactive tool: + - cd into a clean directory, e.g. "mkdir slim; cd slim" + - unpack the code, e.g. "tar zxf ../slim-1.4.0.tar.gz" + - create a build directory and change to it, e.g. "mkdir build; cd build" + - run ccmake to configure for your OS, e.g. "ccmake ../slim-1.4.0" + . press 'c' to configure and 'e' to ignore warnings + . set the CMAKE_INSTALL_PREFIX and other variables as needed + . continue pressing 'c' until the 'g' option is available + . press 'g' to generate the files needed + - run "make" + - run "make install" + + o Using command line only: + - cd into a clean directory + - unpack the code + - create a build directory and change to it + - run cmake to configure for your OS and options + - build and install as above + e.g. + mkdir slim; cd slim + tar zxf ../slim-1.4.0.tar.gz + mkdir build; cd build + cmake ../slim-1.4.0 -DCMAKE_INSTALL_PREFIX=/opt -DUSE_PAM=yes -DUSE_CONSOLEKIT=yes + make + make install + +2. automatic startup + Edit the init scripts according to your OS/Distribution. An example for + systemd is provided diff --git a/slim/PAM.cpp b/slim/PAM.cpp new file mode 100644 index 0000000..088ce05 --- /dev/null +++ b/slim/PAM.cpp @@ -0,0 +1,309 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 2007 Martin Parm + * Copyright (C) 2022 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "PAM.h" + +namespace PAM +{ + Exception::Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum) + : errnum(_errnum), errstr(pam_strerror(_pam_handle, _errnum)), + func_name(_func_name) + {} + + Exception::~Exception(void) {} + + Auth_Exception::Auth_Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum) + : Exception(_pam_handle, _func_name, _errnum) + {} + + Cred_Exception::Cred_Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum) + : Exception(_pam_handle, _func_name, _errnum) + {} + + int Authenticator::_end (void) + { + int result=pam_end(pam_handle, last_result); + pam_handle=0; + return result; + } + + Authenticator::Authenticator(conversation* conv, void* data) + : pam_handle(0), last_result(PAM_SUCCESS) + { + pam_conversation.conv=conv; + pam_conversation.appdata_ptr=data; + } + + Authenticator::~Authenticator(void) + { + if (pam_handle) + _end(); + } + + void Authenticator::start(const std::string& service) + { + switch ((last_result=pam_start(service.c_str(), NULL, &pam_conversation, &pam_handle))) { + default: + throw Exception(pam_handle, "pam_start()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::end(void) + { + switch ((last_result=_end())) { + default: + throw Exception(pam_handle, "pam_end()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::set_item(const Authenticator::ItemType item, const void* value) + { + switch ((last_result=pam_set_item(pam_handle, item, value))) { + default: + _end(); + throw Exception(pam_handle, "pam_set_item()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + const void* Authenticator::get_item(const Authenticator::ItemType item) + { + const void* data; + switch ((last_result=pam_get_item(pam_handle, item, &data))) { + default: + case PAM_SYSTEM_ERR: +#ifdef __LIBPAM_VERSION + case PAM_BAD_ITEM: +#endif + _end(); + throw Exception(pam_handle, "pam_get_item()", last_result); + + case PAM_PERM_DENIED: /* The value of item was NULL */ + case PAM_SUCCESS: + break; + } + return data; + } + +#ifdef __LIBPAM_VERSION + void Authenticator::fail_delay(const unsigned int micro_sec) + { + switch ((last_result=pam_fail_delay(pam_handle, micro_sec))) { + default: + _end(); + throw Exception(pam_handle, "fail_delay()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } +#endif + + void Authenticator::authenticate(void) + { + switch ((last_result=pam_authenticate(pam_handle, 0))) { + default: + case PAM_ABORT: + case PAM_AUTHINFO_UNAVAIL: +// _end(); +// throw Exception(pam_handle, "pam_authenticate()", last_result); + + case PAM_USER_UNKNOWN: + case PAM_MAXTRIES: + case PAM_CRED_INSUFFICIENT: + case PAM_ACCT_EXPIRED: + case PAM_PERM_DENIED: + case PAM_AUTH_ERR: + throw Auth_Exception(pam_handle, "pam_authentication()", last_result); + + case PAM_SUCCESS: + break; + } + + switch ((last_result=pam_acct_mgmt(pam_handle, PAM_SILENT))) { + /* The documentation and implementation of Linux PAM differs: + PAM_NEW_AUTHTOKEN_REQD is described in the documentation but + don't exists in the actual implementation. This issue needs + to be fixes at some point. */ + + default: + /* case PAM_NEW_AUTHTOKEN_REQD: */ + case PAM_ACCT_EXPIRED: + case PAM_USER_UNKNOWN: + case PAM_AUTH_ERR: + case PAM_PERM_DENIED: + throw Auth_Exception(pam_handle, "pam_acct_mgmt()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::check_acct(void) + { + switch((last_result=pam_acct_mgmt(pam_handle, PAM_SILENT))){ + case PAM_ACCT_EXPIRED: + case PAM_USER_UNKNOWN: + case PAM_PERM_DENIED: + throw Auth_Exception(pam_handle, "pam_acct_mgmt()", last_result); + + default: + //case PAM_NEW_AUTHTOKEN_REQD: + case PAM_AUTH_ERR: + case PAM_SUCCESS: + break; + }; + } + + void Authenticator::open_session(void) + { + switch ((last_result=pam_setcred(pam_handle, PAM_ESTABLISH_CRED))) { + default: + case PAM_CRED_ERR: + case PAM_CRED_UNAVAIL: + _end(); + throw Exception(pam_handle, "pam_setcred()", last_result); + + case PAM_ACCT_EXPIRED: + case PAM_PERM_DENIED: + case PAM_CRED_EXPIRED: + case PAM_USER_UNKNOWN: + throw Cred_Exception(pam_handle, "pam_setcred()", last_result); + + case PAM_SUCCESS: + break; + } + + switch ((last_result=pam_open_session(pam_handle, 0))) { + /* The documentation and implementation of Linux PAM differs: + PAM_SESSION_ERROR is described in the documentation but + don't exists in the actual implementation. This issue needs + to be fixes at some point. */ + + default: + /* case PAM_SESSION_ERROR: */ + pam_setcred(pam_handle, PAM_DELETE_CRED); + _end(); + throw Exception(pam_handle, "pam_open_session()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::close_session(void) + { + switch ((last_result=pam_close_session(pam_handle, 0))) { + /* The documentation and implementation of Linux PAM differs: + PAM_SESSION_ERROR is described in the documentation but + don't exists in the actual implementation. This issue needs + to be fixes at some point. */ + + default: + /* case PAM_SESSION_ERROR: */ + pam_setcred(pam_handle, PAM_DELETE_CRED); + _end(); + throw Exception(pam_handle, "pam_close_session", last_result); + + case PAM_SUCCESS: + break; + } + switch ((last_result=pam_setcred(pam_handle, PAM_DELETE_CRED))) { + default: + case PAM_CRED_ERR: + case PAM_CRED_UNAVAIL: + case PAM_CRED_EXPIRED: + case PAM_USER_UNKNOWN: + _end(); + throw Exception(pam_handle, "pam_setcred()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::setenv(const std::string& key, const std::string& value) + { + std::string name_value = key+"="+value; + switch ((last_result = pam_putenv(pam_handle, name_value.c_str()))) { + default: + case PAM_PERM_DENIED: + case PAM_ABORT: + case PAM_BUF_ERR: +#ifdef __LIBPAM_VERSION + case PAM_BAD_ITEM: +#endif + _end(); + throw Exception(pam_handle, "pam_putenv()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + void Authenticator::delenv(const std::string& key) + { + switch ((last_result = pam_putenv(pam_handle, key.c_str()))) { + default: + case PAM_PERM_DENIED: + case PAM_ABORT: + case PAM_BUF_ERR: +#ifdef __LIBPAM_VERSION + case PAM_BAD_ITEM: +#endif + _end(); + throw Exception(pam_handle, "pam_putenv()", last_result); + + case PAM_SUCCESS: + break; + } + return; + } + + const char* Authenticator::getenv(const std::string& key) + { + return pam_getenv(pam_handle, key.c_str()); + } + + char** Authenticator::getenvlist(void) + { + return pam_getenvlist(pam_handle); + } +} + +std::ostream& operator<<( std::ostream& os, const PAM::Exception& e) +{ + os << e.func_name << ": " << e.errstr; + return os; +} diff --git a/slim/PAM.h b/slim/PAM.h new file mode 100644 index 0000000..925ea62 --- /dev/null +++ b/slim/PAM.h @@ -0,0 +1,106 @@ +/* SLiM - Simple Login Manager + Copyright (C) 2007 Martin Parm + + 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 of the License, or + (at your option) any later version. +*/ + +#ifndef _PAM_H_ +#define _PAM_H_ +#include +#include + +#ifdef __LIBPAM_VERSION +#include +#endif + +namespace PAM +{ + class Exception + { + public: + int errnum; + std::string errstr; + std::string func_name; + Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum); + virtual ~Exception(void); + }; + + class Auth_Exception: public Exception + { + public: + Auth_Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum); + }; + + class Cred_Exception: public Exception + { + public: + Cred_Exception(pam_handle_t* _pam_handle, + const std::string& _func_name, + int _errnum); + }; + + + class Authenticator + { + private: + struct pam_conv pam_conversation; + pam_handle_t* pam_handle; + int last_result; + + int _end(void); + + public: + typedef int (conversation)(int num_msg, + const struct pam_message **msg, + struct pam_response **resp, + void *appdata_ptr); + + enum ItemType { + Service = PAM_SERVICE, + User = PAM_USER, + User_Prompt = PAM_USER_PROMPT, + TTY = PAM_TTY, + Requestor = PAM_RUSER, + Host = PAM_RHOST, + Conv = PAM_CONV, +#ifdef __LIBPAM_VERSION + /* Fail_Delay = PAM_FAIL_DELAY */ +#endif + }; + + public: + Authenticator(conversation* conv, void* data=0); + ~Authenticator(void); + + void start(const std::string& service); + void end(void); + void set_item(const ItemType item, const void* value); + const void* get_item(const ItemType item); +#ifdef __LIBPAM_VERSION + void fail_delay(const unsigned int micro_sec); +#endif + void authenticate(void); + void check_acct(void); + void open_session(void); + void close_session(void); + void setenv(const std::string& key, const std::string& value); + void delenv(const std::string& key); + const char* getenv(const std::string& key); + char** getenvlist(void); + + private: + /* Explicitly disable copy constructor and copy assignment */ + Authenticator(const PAM::Authenticator&); + Authenticator& operator=(const PAM::Authenticator&); + }; +} + +std::ostream& operator<<( std::ostream& os, const PAM::Exception& e); +#endif /* _PAM_H_ */ diff --git a/slim/README b/slim/README new file mode 100644 index 0000000..c6c1598 --- /dev/null +++ b/slim/README @@ -0,0 +1,69 @@ +README file for SLiM + +Rob Pearce + +INTRODUCTION + SLiM (Simple Login Manager) is a graphical login manager for X11. + It aims to be simple, fast and independent from the various + desktop environments. + SLiM was originally based on the last stable release of Login.app + by Per Lidén. + + Features: + - External themes and configuration + - PNG support with alpha transparency for panel + - PNG / JPEG support for backgrounds + - XFT / freetype support + - Double or single (GDM-style) inputbox support + - PAM support for authentication + - Compatible with ConsoleKit or logind, OpenRC or systemd, etc. + - CMake build procedure + +INSTALLATION + see the INSTALL file + +USAGE + SLiM is intended to be run automatically as part of the system + startup. Get your SysVInint, OpenRC or systemd to run the slim + executable, with the -d option if you want it to run as a daemon + in the background (recommended for OpenRC, systemd may work + better without) + + As you would expect of a login manager, you enter your username + and password to login. By default, the ~/.xinitrc file is run, + so be sure to have a working .xinitrc file in your home. This + behaviour is configurable, and may be set up differently by the + distro package. + + Special usernames can be entered to run commands that are + configurable in the config file: + - console: start a console login + - exit: exit SLiM (this may not have desirable results) + - halt: shut down the system + - reboot: reboot the system + + Pressing the F11 key executes a user-specified command (see the + configuration file). The default is to take a screenshot, if the + 'import' program is available. + +CONFIGURATION + /etc/slim.conf is the main configuration file. Alternative files + can be specified on the command line. + Options are explained in the file itself + +THEMES + The appearance (and some parts of the behaviour) of SLiM depends + on the "theme" specified by the configuration file. See the file + THEMES for details. + +COPYRIGHT + SLiM is copyright (c) 2004-13 by Simone Rota, Johannes Winkelmann, + Nobuhiro Iwamatsu and 2022-23 by Rob Pearce. It is available under the + GNU General Public License. + See the COPYING file for the complete license. + + Image handling code adapted and extended from xplanet 1.0.1, + copyright (c) 2002-04 by Hari Nair + + Login.app is copyright (c) 1997, 1998 by Per Liden and is + licensed through the GNU General Public License. diff --git a/slim/THEMES b/slim/THEMES new file mode 100644 index 0000000..935e708 --- /dev/null +++ b/slim/THEMES @@ -0,0 +1,136 @@ +Quick THEME howto for SLiM + +Some basic information regarding the slim theme format. +Read this file if you plan to make some theme for +the program, and of course have a look at the included themes + +GENERAL CONCEPT + A SLiM theme essentially consists of: + - a background image (background.png or background.jpg) + - a panel image (panel.png or panel.jpg) + - input box(es) and messages and their placement + and properties (slim.theme) + + The panel and background images can be a PNG or JPEG file. + The panel is blended into the background image, + taking care of alpha transparency. + +SUPPORTED FORMATS + - fonts: use the xft font specs, ie: Verdana:size=16:bold + - colors: use html hex format, ie #0066CC + - positions: can be either absolute in pixels, ie 350 + or relative to the container, ie 50% is in the middle + of the screen. + +OPTIONS + The following is an example slim.theme file + ---------------------------------------------------------------------- + # Color, font, position for the messages (ie: shutting down) + msg_color #FFFFFF + msg_font Verdana:size=16:bold + msg_x 50% + msg_y 30 + + # Color, font, position for the session list + session_color #FFFFFF + session_font Verdana:size=16:bold + session_x 50% + session_y 90% + + # style of background ('stretch', 'tile', 'center') and color + background_style stretch + background_color #FF0033 + + # Horizonatal and vertical position for the panel. + input_panel_x 50% + input_panel_y 40% + + # Input controls horizontal and vertical positions relative to + # the panel. + # Set input_pass_x and input_pass_y to -1 to use a single input + # box for username/password (GDM Style). + # Note that these fields only accept absolute values. + input_name_x 40 + input_name_y 100 + input_pass_x 40 + input_pass_y 120 + + # Input controls font and color + input_font Verdana:size=12 + input_color #000000 + + # Welcome message position. (relative to the panel) + # use -1 for both values or comment the options to disable + # the welcome message + welcome_x 50% + welcome_y 38 + + # Font and color for the welcome message + welcome_font Verdana:size=16:bold:slant=italic + welcome_color #d7dde8 + + # 'Enter username' font and foreground/background color + username_font Verdana:size=12 + username_color #d7dde8 + + # 'Enter username' and 'Enter password' position (relative to the panel) + # use -1 for both values to disable the message + # note that in case of single inputbox the password values are ignored. + username_x 50% + username_y 146 + password_x 50% + password_y 146 + + # The message to be displayed. Leave blank if no message + # is needed (ie, when already present in the panel image) + username_msg Please enter your username + password_msg Please enter your password + + # Optional message to show on a failed login. Similar set of values + # to the above. Use -1 for the x & y to disable (default). + password_feedback_x 50% + password_feedback_y 165 + password_feedback_msg Authentication failed + # The font and color are inherited from msg_xxx + ---------------------------------------------------------------------- + +SHADOWS + + The 'msg', 'input', 'welcome', 'session' and 'username' sections + support shadows; three values can be configured: + - color: the shadow color + - x offset: the offset in x direction, relative to the normal text + - y offset: the offset in y direction, relative to the normal text + + So to add a text shadow to the welcome message, add the following + to slim.conf: + ---------------------------------------------------------------------- + welcome_shadow_xoffset -2 + welcome_shadow_yoffset 2 + welcome_shadow_color #ff0000 + ---------------------------------------------------------------------- + + The other keys are analogous: + ---------------------------------------------------------------------- + # for username and password label + username_shadow_xoffset 2 + username_shadow_yoffset -2 + username_shadow_color #ff0000 + + # for the input fields + input_shadow_xoffset 1 + input_shadow_yoffset 1 + input_shadow_color #0000ff + + # for the messages: + msg_shadow_xoffset 1 + msg_shadow_yoffset 1 + msg_shadow_color #ff00ff + + # For the session: + session_shadow_xoffset 1 + session_shadow_yoffset 1 + session_shadow_color #ff00ff + ---------------------------------------------------------------------- + + diff --git a/slim/TODO b/slim/TODO new file mode 100644 index 0000000..f0130c6 --- /dev/null +++ b/slim/TODO @@ -0,0 +1,17 @@ +Important things +---------------- +- Make it work better with multi-screen setups + +Improvements suggested long ago +------------------------------- +- Don't start X server if theme's not found +- i18n +- FreeBSD fixes +- restart X server on ctrl-alt-backspace + +Other thoughts for improvement +------------------------------ +- position text to fit screen, e.g. when at 90% position +- allow nested config +- make slimlock handle themes the same as slim +- option to run X server as non-root diff --git a/slim/app.cpp b/slim/app.cpp new file mode 100644 index 0000000..237477d --- /dev/null +++ b/slim/app.cpp @@ -0,0 +1,1455 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2022 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include // for getpwnam etc. +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "const.h" +#include "log.h" +#include "numlock.h" +#include "switchuser.h" +#include "util.h" +#include "app.h" + +#ifdef HAVE_SHADOW +#include +#endif + +using namespace std; + +#ifdef USE_PAM +#include + +int conv(int num_msg, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + *resp = (struct pam_response *) calloc(num_msg, sizeof(struct pam_response)); + Panel* panel = *static_cast(appdata_ptr); + int result = PAM_SUCCESS; + int i; + + for (i = 0; i < num_msg; i++) + { + (*resp)[i].resp = 0; + (*resp)[i].resp_retcode = 0; + switch (msg[i]->msg_style) + { + case PAM_PROMPT_ECHO_ON: + /* We assume PAM is asking for the username */ + panel->EventHandler(Panel::Get_Name); + switch (panel->getAction()) + { + case Panel::Suspend: + case Panel::Halt: + case Panel::Reboot: + (*resp)[i].resp=strdup("root"); + break; + + case Panel::Console: + case Panel::Exit: + case Panel::Login: + (*resp)[i].resp=strdup(panel->GetName().c_str()); + break; + default: + break; + } + break; + + case PAM_PROMPT_ECHO_OFF: + /* We assume PAM is asking for the password */ + switch (panel->getAction()) + { + case Panel::Console: + case Panel::Exit: + /* We should leave now! */ + result = PAM_CONV_ERR; + break; + + default: + panel->EventHandler(Panel::Get_Passwd); + (*resp)[i].resp=strdup(panel->GetPasswd().c_str()); + break; + } + break; + + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + /* We simply write these to the log + TODO: Maybe we should show them. In particular, if you + have a fingerprint reader, PAM passes instructions + in PAM_TEXT_INFO messages */ + logStream << APPNAME << ": " << msg[i]->msg << endl; + break; + } + if (result != PAM_SUCCESS) + break; + } + + if (result != PAM_SUCCESS) + { + for (i = 0; i < num_msg; i++) + { + if ((*resp)[i].resp == 0) + continue; + free((*resp)[i].resp); + (*resp)[i].resp = 0; + } + free(*resp); + *resp = 0; + } + + return result; +} +#endif + +extern App* LoginApp; + +int xioerror(Display *disp) +{ + LoginApp->RestartServer(); + return 0; +} + +void CatchSignal(int sig) +{ + logStream << APPNAME << ": unexpected signal " << sig << endl; + + if (LoginApp->isServerStarted()) + LoginApp->StopServer(); + + LoginApp->RemoveLock(); + exit(ERR_EXIT); +} + +static volatile bool got_sigusr1 = false; +void User1Signal(int sig) +{ + got_sigusr1 = true; + signal(sig, User1Signal); +} + +App::App(int argc, char** argv) + : Dpy(NULL), ServerPID(-1), serverStarted(false), +#ifdef USE_PAM + pam(conv, static_cast(&LoginPanel)), +#endif + cfg(0), + firstlogin(true), daemonmode(false), force_nodaemon(false), + testing(false), tww(1280), twh(1024), +#ifdef USE_CONSOLEKIT + consolekit_support_enabled(true), +#endif + mcookiesize(32) /* Must be divisible by 4 */ +{ + int tmp; + mcookie = string(App::mcookiesize, 'a'); + char * win_size = 0; + + /* Parse command line + Note: we allow an arg for the -n option to handle "-nodaemon" as + originally quoted in the docs. However, the parser has never + checked the arg, so "-noddy" works exactly the same */ + while ((tmp = getopt(argc, argv, "c:vhsp:w:n::d")) != EOF) + { + switch (tmp) + { + case 'c': /* Config */ + if (optarg == NULL) + { + cerr << "The -c option requires an argument" << endl; + exit(ERR_EXIT); + } + if ( cfg != 0 ) + { + cerr << "The -c option can only be given once" << endl; + exit(ERR_EXIT); + } + cfg = new Cfg; + cfg->readConf(optarg); + break; + + case 'p': /* Test theme */ + testtheme = optarg; + testing = true; + if (testtheme == NULL) + { + cerr << "The -p option requires an argument" << endl; + exit(ERR_EXIT); + } + break; + + case 'w': /* Window size for theme test mode */ + if ( !testing ) + { + cerr << "The -w option is only valid after -p" << endl; + exit(ERR_EXIT); + } + win_size = optarg; + // Test for valid syntax later + break; + + case 'd': /* Daemon mode */ + daemonmode = true; + break; + + case 'n': /* Daemon mode */ + daemonmode = false; + force_nodaemon = true; + break; + + case 'v': /* Version */ + std::cout << APPNAME << " version " << VERSION << endl; + exit(OK_EXIT); + break; + +#ifdef USE_CONSOLEKIT + case 's': /* Disable consolekit support */ + consolekit_support_enabled = false; + break; +#endif + + case '?': /* Illegal option - getopt will have printed an error */ + std::cout << endl; + case 'h': /* Help */ + std::cout << "usage: " << APPNAME << " [option ...]" << endl + << "options:" << endl + << " -c /path/to/config select configuration file" << endl + << " -d daemon mode" << endl + << " -n no-daemon mode" << endl +#ifdef USE_CONSOLEKIT + << " -s start for systemd, disable consolekit support" << endl +#endif + << " -p /path/to/themedir preview theme" << endl + << " -w x size of window for preview" << endl + << " -h show this help" << endl + << " -v show version" << endl; + exit(OK_EXIT); + break; + } + } +#ifndef XNEST_DEBUG + if (getuid() != 0 && !testing) + { + logStream << APPNAME << ": only root can run this program" << endl; + exit(ERR_EXIT); + } +#endif /* XNEST_DEBUG */ + if ( win_size ) + { + char* sep = 0; + tww = (short)strtol ( win_size, &sep, 10 ); + if ( ( sep == 0 ) || ( *sep++ != 'x' ) ) + { + cerr << "Malformed argument to -w option" << endl; + exit(ERR_EXIT); + } + twh = (short)strtol ( sep, &sep, 10 ); + } +} + + +void App::Run() +{ + DisplayName = DISPLAY; + +#ifdef XNEST_DEBUG + char* p = getenv("DISPLAY"); + if (p && p[0]) + { + DisplayName = p; + cout << "Using display name " << DisplayName << endl; + } +#endif + + /* Read configuration and theme */ + if ( cfg == 0 ) + { + cfg = new Cfg; + cfg->readConf(CFGFILE); + } + string themebase = ""; + string themefile = ""; + string themedir = ""; + + if (testing) + { + themeName = testtheme; + } + else + { + themebase = string(THEMESDIR) + "/"; + themeName = cfg->getOption("current_theme"); + string::size_type pos; + if ((pos = themeName.find(",")) != string::npos) + { + /* input is a set */ + themeName = cfg->findValidRandomTheme(themeName); + if (themeName == "") + { + themeName = "default"; + } + } + } + +#ifdef USE_PAM + try { + pam.start("slim"); + pam.set_item(PAM::Authenticator::TTY, DisplayName); + pam.set_item(PAM::Authenticator::Requestor, "root"); + pam.setenv("XDG_SESSION_CLASS", "greeter"); // so eLogind works right + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + exit(ERR_EXIT); + } +#endif + + bool loaded = false; + while (!loaded) + { + themedir = themebase + themeName; + themefile = themedir + THEMESFILE; + if (!cfg->readConf(themefile)) + { + if (themeName == "default") + { + logStream << APPNAME << ": Failed to open default theme file " + << themefile << endl; + exit(ERR_EXIT); + } + else + { + logStream << APPNAME << ": Invalid theme in config: " + << themeName << endl; + themeName = "default"; + } + } + else + { + loaded = true; + } + } + + if (!testing) + { + /* Create lock file */ + LoginApp->GetLock(); + + /* Start x-server */ + setenv("DISPLAY", DisplayName, 1); + signal(SIGQUIT, CatchSignal); + signal(SIGTERM, CatchSignal); + signal(SIGKILL, CatchSignal); + signal(SIGINT, CatchSignal); + signal(SIGHUP, CatchSignal); + signal(SIGPIPE, CatchSignal); + signal(SIGUSR1, User1Signal); + +#ifndef XNEST_DEBUG + if ( !force_nodaemon && cfg->getOption("daemon") == "yes" ) + { + daemonmode = true; + } + + /* Daemonize */ + if (daemonmode) + { + if (daemon(0, 0) == -1) + { + logStream << APPNAME << ": " << strerror(errno) << endl; + exit(ERR_EXIT); + } + } + + OpenLog(); + + if (daemonmode) + UpdatePid(); + + CreateServerAuth(); + StartServer(); +#endif + } + + /* Open display if we haven't already (e.g. testing) */ + if ( Dpy == 0 ) + Dpy = XOpenDisplay(DisplayName); + /* Now check that it succeeded */ + if ( Dpy == 0 ) + { + logStream << APPNAME << ": could not open display '" + << DisplayName << "'" << endl; +#ifndef XNEST_DEBUG + if (!testing) + StopServer(); +#endif + exit(ERR_EXIT); + } + + /* Get screen and root window */ + Scr = DefaultScreen(Dpy); + Root = RootWindow(Dpy, Scr); + + /* for tests we use a standard window */ + if (testing) + { + Window RealRoot = Root; // already done RootWindow(Dpy, Scr); + Root = XCreateSimpleWindow(Dpy, RealRoot, 0, 0, tww, twh, 0, 0, 0); + XMapWindow(Dpy, Root); + XFlush(Dpy); + } + else + { + blankScreen(); + } + + /* Create panel */ + LoginPanel = new Panel ( Dpy, Scr, Root, cfg, themedir, + ( testing ? Panel::Mode_Test : Panel::Mode_DM ) ); + LoginPanel->HideCursor(); + + bool firstloop = true; /* 1st time panel is shown (for automatic username) */ + bool focuspass = cfg->getOption("focus_password")=="yes"; + bool autologin = cfg->getOption("auto_login")=="yes"; + + if ( firstlogin && ( cfg->getOption("default_user") != "" ) ) + { + LoginPanel->SetName ( cfg->getOption("default_user") ); + firstlogin = false; +#ifdef USE_PAM + pam.set_item(PAM::Authenticator::User, cfg->getOption("default_user").c_str()); +#endif + if (autologin) + { +#ifdef USE_PAM + try { + pam.check_acct(); +#endif + Login(); +#ifdef USE_PAM + } + catch(PAM::Auth_Exception& e){ + // The default user is invalid + } +#endif + } + } + + /* Set NumLock */ + string numlock = cfg->getOption("numlock"); + if (numlock == "on") + NumLock::setOn(Dpy); + else if (numlock == "off") + NumLock::setOff(Dpy); + + + /* Start looping */ + int panelclosed = 1; + Panel::ActionType Action; + + while (1) + { + if (panelclosed) + { + /* Init root */ + LoginPanel->setBackground(); + + /* Close all clients */ + if (!testing) + { + KillAllClients(False); + KillAllClients(True); + } + + /* Show panel */ + LoginPanel->OpenPanel(); + } + + if ( firstloop ) + { + LoginPanel->Reset(); + + if ( cfg->getOption("default_user") != "" ) + LoginPanel->SetName(cfg->getOption("default_user")); + + // Removed by Gentoo "session-chooser" patch + //LoginPanel->SwitchSession(); + } + + if ( !AuthenticateUser(focuspass && firstloop) ) + { + unsigned int cfg_passwd_timeout; + cfg_passwd_timeout = Cfg::string2int(cfg->getOption("wrong_passwd_timeout").c_str()); + if ( cfg_passwd_timeout > 60 ) + cfg_passwd_timeout = 60; + panelclosed = 0; + firstloop = false; + LoginPanel->WrongPassword(cfg_passwd_timeout); + XBell(Dpy, 100); + continue; + } + + firstloop = false; + + Action = LoginPanel->getAction(); + /* for themes test we just quit */ + if ( testing ) + { + Action = Panel::Exit; + } + + panelclosed = 1; + LoginPanel->ClosePanel(); + + switch (Action) + { + case Panel::Login: + Login(); + break; + case Panel::Console: + Console(); + break; + case Panel::Reboot: + Reboot(); + break; + case Panel::Halt: + Halt(); + break; + case Panel::Suspend: + Suspend(); + break; + case Panel::Exit: + Exit(); + break; + default: + break; + } + } +} + +#ifdef USE_PAM +bool App::AuthenticateUser(bool focuspass) +{ + /* Reset the username */ + try{ + if (!focuspass) + pam.set_item(PAM::Authenticator::User, 0); + pam.authenticate(); + } + catch(PAM::Auth_Exception& e){ + switch (LoginPanel->getAction()) + { + case Panel::Exit: + case Panel::Console: + return true; /* <--- This is simply fake! */ + + default: + break; + } + logStream << APPNAME << ": " << e << endl; + return false; + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + exit(ERR_EXIT); + } + return true; +} +#else +bool App::AuthenticateUser(bool focuspass) +{ + if (!focuspass) + { + LoginPanel->EventHandler(Panel::Get_Name); + switch (LoginPanel->getAction()) + { + case Panel::Exit: + case Panel::Console: + logStream << APPNAME << ": Got a special command (" + << LoginPanel->GetName() << ")" << endl; + return true; /* <--- This is simply fake! */ + + default: + break; + } + } + LoginPanel->EventHandler(Panel::Get_Passwd); + + char *encrypted, *correct; + struct passwd *pw; + + switch (LoginPanel->getAction()) + { + case Panel::Suspend: + case Panel::Halt: + case Panel::Reboot: + pw = getpwnam("root"); + break; + + case Panel::Console: + case Panel::Exit: + case Panel::Login: + pw = getpwnam(LoginPanel->GetName().c_str()); + break; + } + endpwent(); + if (pw == 0) + return false; + +#ifdef HAVE_SHADOW + struct spwd *sp = getspnam(pw->pw_name); + endspent(); + if (sp) + correct = sp->sp_pwdp; + else +#endif /* HAVE_SHADOW */ + correct = pw->pw_passwd; + + if (correct == 0 || correct[0] == '\0') + return true; + + encrypted = crypt(LoginPanel->GetPasswd().c_str(), correct); + return ((encrypted && strcmp(encrypted, correct) == 0) ? true : false); +} +#endif + +int App::GetServerPID() +{ + return ServerPID; +} + + +void App::Login() +{ + struct passwd *pw; + pid_t pid; + +#ifdef USE_PAM + try{ + pam.open_session(); + pw = getpwnam(static_cast(pam.get_item(PAM::Authenticator::User))); + } + catch(PAM::Cred_Exception& e){ + /* Credentials couldn't be established */ + logStream << APPNAME << ": " << e << endl; + return; + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + exit(ERR_EXIT); + } +#else + pw = getpwnam(LoginPanel->GetName().c_str()); +#endif + endpwent(); + if (pw == 0) + return; + if (pw->pw_shell[0] == '\0') { + setusershell(); + strcpy(pw->pw_shell, getusershell()); + endusershell(); + } + + /* Setup the environment */ + char* term = getenv("TERM"); + string maildir = _PATH_MAILDIR; + maildir.append("/"); + maildir.append(pw->pw_name); + string xauthority = pw->pw_dir; + xauthority.append("/.Xauthority"); + +#ifdef USE_PAM + /* Setup the PAM environment */ + try{ + if (term) + pam.setenv("TERM", term); + pam.setenv("HOME", pw->pw_dir); + pam.setenv("PWD", pw->pw_dir); + pam.setenv("SHELL", pw->pw_shell); + pam.setenv("USER", pw->pw_name); + pam.setenv("LOGNAME", pw->pw_name); + pam.setenv("PATH", cfg->getOption("default_path").c_str()); + pam.setenv("DISPLAY", DisplayName); + pam.setenv("MAIL", maildir.c_str()); + pam.setenv("XAUTHORITY", xauthority.c_str()); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + exit(ERR_EXIT); + } +#endif + +#ifdef USE_CONSOLEKIT + if (consolekit_support_enabled) + { + /* Setup the ConsoleKit session */ + try { + ck.open_session(DisplayName, pw->pw_uid); + } + catch(Ck::Exception &e) { + logStream << APPNAME << ": " << e << endl; + exit(ERR_EXIT); + } + } +#endif + + /* Create new process */ + pid = fork(); + if (pid == 0) + { +#ifdef USE_PAM + /* Get a copy of the environment and close the child's copy */ + /* of the PAM-handle. */ + char** child_env = pam.getenvlist(); + +# ifdef USE_CONSOLEKIT + if (consolekit_support_enabled) + { + char** old_env = child_env; + + /* Grow the copy of the environment for the session cookie */ + int n; + for (n = 0; child_env[n] != NULL ; n++) + ; + + n++; + + child_env = static_cast(malloc(sizeof(char*)*(n+1))); + memcpy(child_env, old_env, sizeof(char*)*n); + child_env[n - 1] = StrConcat("XDG_SESSION_COOKIE=", ck.get_xdg_session_cookie()); + child_env[n] = NULL; + } +# endif /* USE_CONSOLEKIT */ +#else + +# ifdef USE_CONSOLEKIT + const int Num_Of_Variables = 12; /* Number of env. variables + 1 */ +# else + const int Num_Of_Variables = 11; /* Number of env. variables + 1 */ +# endif /* USE_CONSOLEKIT */ + char** child_env = static_cast(malloc(sizeof(char*)*Num_Of_Variables)); + int n = 0; + if (term) + child_env[n++]=StrConcat("TERM=", term); + child_env[n++]=StrConcat("HOME=", pw->pw_dir); + child_env[n++]=StrConcat("PWD=", pw->pw_dir); + child_env[n++]=StrConcat("SHELL=", pw->pw_shell); + child_env[n++]=StrConcat("USER=", pw->pw_name); + child_env[n++]=StrConcat("LOGNAME=", pw->pw_name); + child_env[n++]=StrConcat("PATH=", cfg->getOption("default_path").c_str()); + child_env[n++]=StrConcat("DISPLAY=", DisplayName); + child_env[n++]=StrConcat("MAIL=", maildir.c_str()); + child_env[n++]=StrConcat("XAUTHORITY=", xauthority.c_str()); +# ifdef USE_CONSOLEKIT + if (consolekit_support_enabled) + child_env[n++]=StrConcat("XDG_SESSION_COOKIE=", ck.get_xdg_session_cookie()); +# endif /* USE_CONSOLEKIT */ + child_env[n++]=0; + +#endif + + /* Login process starts here */ + SwitchUser Su(pw, cfg, DisplayName, child_env); + string session = LoginPanel->getSession(); + string loginCommand = cfg->getOption("login_cmd"); + replaceVariables(loginCommand, SESSION_VAR, session); + replaceVariables(loginCommand, THEME_VAR, themeName); + string sessStart = cfg->getOption("sessionstart_cmd"); + if (sessStart != "") + { + replaceVariables(sessStart, USER_VAR, pw->pw_name); + if ( system(sessStart.c_str()) < 0 ) + logStream << APPNAME << ": Failed to run sessionstart_cmd" << endl; + } + Su.Login(loginCommand.c_str(), mcookie.c_str()); + _exit(OK_EXIT); + } + +#ifndef XNEST_DEBUG + CloseLog(); +#endif + + /* Wait until user is logging out (login process terminates) */ + pid_t wpid = -1; + int status; + while (wpid != pid) + { + wpid = wait(&status); + if (wpid == ServerPID) + xioerror(Dpy); /* Server died, simulate IO error */ + } +#ifndef XNEST_DEBUG + /* Re-activate log file */ + OpenLog(); +#endif + if (WIFEXITED(status) && WEXITSTATUS(status)) + { + LoginPanel->Message("Failed to execute login command"); + sleep(3); + } + else + { + string sessStop = cfg->getOption("sessionstop_cmd"); + if ( sessStop != "" ) + { + replaceVariables ( sessStop, USER_VAR, pw->pw_name ); + if ( system(sessStop.c_str()) < 0 ) + logStream << APPNAME << "Session stop command failed" << endl; + } + } + +#ifdef USE_CONSOLEKIT + if (consolekit_support_enabled) + { + try { + ck.close_session(); + } + catch(Ck::Exception &e) { + logStream << APPNAME << ": " << e << endl; + } + } +#endif + +#ifdef USE_PAM + try { + pam.close_session(); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + } +#endif + +/* Close all clients */ + KillAllClients(False); + KillAllClients(True); + + /* Send HUP signal to clientgroup */ + killpg(pid, SIGHUP); + + /* Send TERM signal to clientgroup, if error send KILL */ + if (killpg(pid, SIGTERM)) + killpg(pid, SIGKILL); + + LoginPanel->HideCursor(); + +#ifndef XNEST_DEBUG + RestartServer(); /// @bug recursive call! +#endif +} + + +void App::Reboot() +{ +#ifdef USE_PAM + try { + pam.end(); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + } +#endif + + /* Write message */ + LoginPanel->Message((char*)cfg->getOption("reboot_msg").c_str()); + sleep(3); + + /* Stop server and reboot */ + StopServer(); + RemoveLock(); + if ( system(cfg->getOption("reboot_cmd").c_str()) < 0 ) + logStream << APPNAME << ": Failed to execute reboot command" << endl; + exit(OK_EXIT); +} + + +void App::Halt() +{ +#ifdef USE_PAM + try { + pam.end(); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + } +#endif + + /* Write message */ + LoginPanel->Message((char*)cfg->getOption("shutdown_msg").c_str()); + sleep(3); + + /* Stop server and halt */ + StopServer(); + RemoveLock(); + if ( system(cfg->getOption("halt_cmd").c_str()) < 0 ) + logStream << APPNAME << ": Failed to execute halt command" << endl; + exit(OK_EXIT); +} + + +void App::Suspend() +{ + sleep(1); + if ( system(cfg->getOption("suspend_cmd").c_str() ) < 0 ) + logStream << APPNAME << ": Failed to execute suspend command" << endl; +} + + +void App::Console() +{ + int posx = 40; + int posy = 40; + int fontx = 9; + int fonty = 15; + int width = (XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posx * 2)) / fontx; + int height = (XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)) - (posy * 2)) / fonty; + + /* Execute console */ + const char* cmd = cfg->getOption("console_cmd").c_str(); + char *tmp = new char[strlen(cmd) + 60]; + sprintf(tmp, cmd, width, height, posx, posy, fontx, fonty); + if ( system(tmp) < 0 ) + logStream << APPNAME << ": Failed to fork console app '" << cmd << "'" << endl; + delete [] tmp; +} + +void App::Exit() +{ +#ifdef USE_PAM + try { + pam.end(); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + } +#endif + + if (testing) + { + std::string testmsg = "User "; + testmsg += LoginPanel->GetName(); + testmsg += " auth OK, session="; + testmsg += LoginPanel->getSession(); + LoginPanel->Message(testmsg); + sleep(3); + delete LoginPanel; + XCloseDisplay(Dpy); + } + else + { + delete LoginPanel; + StopServer(); + RemoveLock(); + } + delete cfg; + exit(OK_EXIT); +} + +int CatchErrors(Display *dpy, XErrorEvent *ev) +{ + return 0; +} + +void App::RestartServer() +{ +#ifdef USE_PAM + try { + pam.end(); + } + catch(PAM::Exception& e){ + logStream << APPNAME << ": " << e << endl; + } +#endif + + StopServer(); + RemoveLock(); + if (force_nodaemon) + { + delete LoginPanel; + exit(ERR_EXIT); /* use ERR_EXIT so that systemd's RESTART=on-failure works */ + } + else + { + while (waitpid(-1, NULL, WNOHANG) > 0); /* Collects all dead children */ + Run(); + } +} + + +/* + * Iterates over the list of all windows declared as children of Root and + * kills the clients. Since Root is the root window of the screen, all + * running applications (of the logged-in session) should be caught by this + */ +void App::KillAllClients(Bool top) +{ + Window dummywindow; + Window *children; + unsigned int nchildren; + unsigned int i; + XWindowAttributes attr; + + XSync(Dpy, 0); + XSetErrorHandler(CatchErrors); + + nchildren = 0; + XQueryTree(Dpy, Root, &dummywindow, &dummywindow, &children, &nchildren); + if (!top) + { + for (i=0; i timeout) + break; + } + + if (i > 0) + logStream << endl; + lasttext = text; + + return (ServerPID != pidfound); +} + + +int App::WaitForServer() +{ + int ncycles = 120; + int cycles; + + /* The X server should send us a USR1 signal when it's ready. We trap */ + /* that signal and set a flag. If that's not already happened, wait for */ + /* a good time. The incoming signal should terminate the sleep() call */ + /* with a non-zero return value. Otherwise, time out and try anyway but */ + /* log the oddity. */ + if ( !got_sigusr1 && ( sleep(5)==0 ) ) + logStream << "WaitForServer: Not seen SigUSR1 from Xserver" << endl; + + for (cycles = 0; cycles < ncycles; cycles++) + { + Dpy = XOpenDisplay(DisplayName); + if ( Dpy ) + { + XSetIOErrorHandler(xioerror); + return 1; + } + else + { + if (!ServerTimeout(1, (char *) "X server to begin accepting connections")) + break; + } + } + + logStream << "Giving up." << endl; + + return 0; +} + +int App::StartServer() +{ + got_sigusr1 = false; // We're about to start the X server so clear the semaphore + + ServerPID = fork(); /// @bug why do this so early? Just before the switch makes more sense + + int argc = 1, pos = 0, i; + static const int MAX_XSERVER_ARGS = 256; + static char* server[MAX_XSERVER_ARGS+2] = { NULL }; + server[0] = (char *)cfg->getOption("default_xserver").c_str(); + string argOption = cfg->getOption("xserver_arguments"); + /* Add mandatory -xauth option */ + argOption = argOption + " -auth " + cfg->getOption("authfile"); + char* args = new char[argOption.length()+2]; /* NULL plus vt */ + strcpy(args, argOption.c_str()); + + serverStarted = false; + + bool hasVtSet = false; + while (args[pos] != '\0') + { + if (args[pos] == ' ' || args[pos] == '\t') + { + *(args+pos) = '\0'; + server[argc++] = args+pos+1; + } + else if (pos == 0) + { + server[argc++] = args+pos; + } + ++pos; + + if (argc+1 >= MAX_XSERVER_ARGS) + { + /* ignore _all_ arguments to make sure the server starts at */ + /* all */ + argc = 1; + break; + } + } + + for (i = 0; i < argc; i++) + { + if (server[i][0] == 'v' && server[i][1] == 't') + { + bool ok = false; + Cfg::string2int(server[i]+2, &ok); + if (ok) + { + hasVtSet = true; + } + } + } + + if (!hasVtSet && daemonmode) + { + server[argc++] = (char*)"vt07"; + } + server[argc] = NULL; + + switch (ServerPID) { + case 0: + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + setpgid(0,getpid()); + + execvp(server[0], server); + logStream << APPNAME << ": X server could not be started" << endl; + exit(ERR_EXIT); + break; + + case -1: + break; + + default: + errno = 0; + // Prime the server timeout function and check for an immediate crash + if (!ServerTimeout(0, (char *)"")) + { + ServerPID = -1; + break; + } + + /* Wait for server to start up */ + if (WaitForServer() == 0) + { + logStream << APPNAME << ": unable to connect to X server" << endl; + StopServer(); + ServerPID = -1; + exit(ERR_EXIT); + } + break; + } + + delete [] args; + + serverStarted = true; ///< @bug not true if ServerPID is -1 + + return ServerPID; +} + + +jmp_buf CloseEnv; +int IgnoreXIO(Display *d) +{ + logStream << APPNAME << ": connection to X server lost." << endl; + longjmp(CloseEnv, 1); +} + +void App::StopServer() +{ + signal(SIGQUIT, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGPIPE, SIG_IGN); + signal(SIGTERM, SIG_DFL); + signal(SIGKILL, SIG_DFL); + + /* Catch X error */ + XSetIOErrorHandler(IgnoreXIO); + if (!setjmp(CloseEnv) && Dpy) + XCloseDisplay(Dpy); + + /* Send HUP to process group */ + errno = 0; + if ((killpg(getpid(), SIGHUP) != 0) && (errno != ESRCH)) + logStream << APPNAME << ": can't send HUP to process group " << getpid() << endl; + + /* Send TERM to server */ + if (ServerPID < 0) + return; + + errno = 0; + + if (killpg(ServerPID, SIGTERM) < 0) + { + if (errno == EPERM) + { + logStream << APPNAME << ": can't kill X server" << endl; + exit(ERR_EXIT); + } + if (errno == ESRCH) + return; + } + + /* Wait for server to shut down */ + if (!ServerTimeout(10, (char *)"X server to shut down")) + { + logStream << endl; + return; + } + + logStream << endl << APPNAME << + ": X server slow to shut down, sending KILL signal." << endl; + + /* Send KILL to server */ + errno = 0; + if (killpg(ServerPID, SIGKILL) < 0) + { + if (errno == ESRCH) + return; + } + + /* Wait for server to die */ + if (ServerTimeout(3, (char*)"server to die")) + { + logStream << endl << APPNAME << ": can't kill server" << endl; + exit(ERR_EXIT); + } + logStream << endl; +} + +void App::blankScreen() +{ + GC gc = XCreateGC(Dpy, Root, 0, 0); + XSetForeground(Dpy, gc, BlackPixel(Dpy, Scr)); + XFillRectangle(Dpy, Root, gc, 0, 0, + XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)), + XHeightOfScreen(ScreenOfDisplay(Dpy, Scr))); + XFlush(Dpy); + XFreeGC(Dpy, gc); +} + + +/* Check if there is a lockfile and a corresponding process */ +void App::GetLock() +{ + std::ifstream lockfile(cfg->getOption("lockfile").c_str()); + if (!lockfile) + { + /* no lockfile present, create one */ + std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); + if (!lockfile) + { + logStream << APPNAME << ": Could not create lock file: " << + cfg->getOption("lockfile").c_str() << std::endl; + exit(ERR_EXIT); + } + lockfile << getpid() << std::endl; + lockfile.close(); + } + else + { + /* lockfile present, read pid from it */ + int pid = 0; + lockfile >> pid; + lockfile.close(); + if (pid > 0) + { + /* see if process with this pid exists */ + int ret = kill(pid, 0); + if (ret == 0 || (ret == -1 && errno == EPERM) ) + { + logStream << APPNAME << + ": Another instance of the program is already running with PID " + << pid << std::endl; + exit(0); + } + else + { + logStream << APPNAME << ": Stale lockfile found, removing it" << std::endl; + std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); + if (!lockfile) + { + logStream << APPNAME << + ": Could not create new lock file: " << cfg->getOption("lockfile") + << std::endl; + exit(ERR_EXIT); + } + lockfile << getpid() << std::endl; + lockfile.close(); + } + } + } +} + + +/* Remove lockfile */ +void App::RemoveLock() +{ + remove(cfg->getOption("lockfile").c_str()); +} + + +/* Get server start check flag. */ +bool App::isServerStarted() +{ + return serverStarted; +} + + +/* Open a log file for error reporting */ +void App::OpenLog() +{ + if ( !logStream.openLog( cfg->getOption("logfile").c_str() ) ) + { + cerr << APPNAME << ": Could not access log file: " << cfg->getOption("logfile") << endl; + RemoveLock(); + exit(ERR_EXIT); + } + /* I should set the buffers to imediate write, but I just flush on every << operation. */ +} + + +/* Close the logging stream - just a wrapper round the real method */ +void App::CloseLog() +{ + /* Simply closing the log */ + logStream.closeLog(); +} + + +/* + * Populate any matching fields in a markup string with the value of + * a variable. The markup is %name but that is not enforced here - instead + * the caller must pass the full markup tag - %name, not just name + */ +void App::replaceVariables(string& input, + const string& var, + const string& value) +{ + string::size_type pos = 0; + int len = var.size(); + while ((pos = input.find(var, pos)) != string::npos) { + input = input.substr(0, pos) + value + input.substr(pos+len); + } +} + + +/* + * Set the required server authority parameters in the environment + * and file system. + */ +void App::CreateServerAuth() +{ + /* create mit cookie */ + uint16_t word; + uint8_t hi, lo; + int i; + string authfile; + const char *digits = "0123456789abcdef"; + Util::srandom(Util::makeseed()); + for (i = 0; i < App::mcookiesize; i+=4) + { + /* We rely on the fact that all bits generated by Util::random() + * are usable, so we are taking full words from its output. */ + word = Util::random() & 0xffff; + lo = word & 0xff; + hi = word >> 8; + mcookie[i] = digits[lo & 0x0f]; + mcookie[i+1] = digits[lo >> 4]; + mcookie[i+2] = digits[hi & 0x0f]; + mcookie[i+3] = digits[hi >> 4]; + } + /* reinitialize auth file */ + authfile = cfg->getOption("authfile"); + remove(authfile.c_str()); + putenv(StrConcat("XAUTHORITY=", authfile.c_str())); + Util::add_mcookie(mcookie, ":0", cfg->getOption("xauth_path"), + authfile); +} + + +/* + * Concatenate two C-style strings into a newly alloc'd char array of the + * right size. + */ +char* App::StrConcat(const char* str1, const char* str2) +{ + char* tmp = new char[strlen(str1) + strlen(str2) + 1]; + strcpy(tmp, str1); + strcat(tmp, str2); + return tmp; +} + + +/* + * Write our process ID to the lock file specified in the config + */ +void App::UpdatePid() +{ + std::ofstream lockfile(cfg->getOption("lockfile").c_str(), ios_base::out); + if (!lockfile) { + logStream << APPNAME << ": Could not update lock file: " << + cfg->getOption("lockfile").c_str() << endl; + exit(ERR_EXIT); + } + lockfile << getpid() << endl; + lockfile.close(); +} + diff --git a/slim/app.h b/slim/app.h new file mode 100644 index 0000000..e0b9230 --- /dev/null +++ b/slim/app.h @@ -0,0 +1,115 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * + * 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 of the License, or + * (at your option) any later version. + */ + +#ifndef _APP_H_ +#define _APP_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "panel.h" +#include "cfg.h" +#include "image.h" + +#ifdef USE_PAM +#include "PAM.h" +#endif +#ifdef USE_CONSOLEKIT +#include "Ck.h" +#endif + +class App +{ +public: + App(int argc, char **argv); + ~App(); + void Run(); + int GetServerPID(); + void RestartServer(); + void StopServer(); + + /* Lock functions */ + void GetLock(); + void RemoveLock(); + + bool isServerStarted(); + +private: + void Login(); + void Reboot(); + void Halt(); + void Suspend(); + void Console(); + void Exit(); + void KillAllClients(Bool top); + void ReadConfig(); + void OpenLog(); + void CloseLog(); + void CreateServerAuth(); + char *StrConcat(const char *str1, const char *str2); + void UpdatePid(); + + bool AuthenticateUser(bool focuspass); + + static void replaceVariables(std::string &input, + const std::string &var, + const std::string &value); + + /* Server functions */ + int StartServer(); + int ServerTimeout(int timeout, char *string); + int WaitForServer(); + + /* Private data */ + Window Root; ///< The root window of the default screen, on which to draw + Display *Dpy; ///< Connection to the X-server + int Scr; ///< Which "screen" to use (which will be the default one) + Panel *LoginPanel; ///< The panel we display and interact through + int ServerPID; ///< Process ID of the X-server we launched + const char *DisplayName; ///< The display to request, usually ":0.0" + bool serverStarted; ///< Whether we (think we) have started an X server + +#ifdef USE_PAM + PAM::Authenticator pam; ///< Interface to the PAM authentication library +#endif +#ifdef USE_CONSOLEKIT + Ck::Session ck; ///< Interface to ConsoleKit, if used +#endif + + /* Options */ + Cfg *cfg; ///< Collection of options from the configuration file + + void blankScreen(); + + bool firstlogin; ///< Whether to exhibit first login behaviour, or repeat + bool daemonmode; ///< Are we running as a daemon? + bool force_nodaemon; ///< Are we forced NOT to be a daemon? + /* For testing themes */ + char *testtheme; ///< Name of the theme to test, from command line + bool testing; ///< Whether we're running in theme testing mode + short tww, twh; ///< The user's requested test window size + +#ifdef USE_CONSOLEKIT + bool consolekit_support_enabled; ///< Whether to use ConsoleKit (not compatible with systemd) +#endif + + std::string themeName; ///< Name of the theme in use + std::string mcookie; ///< Randomly generated X auth cookie + + const int mcookiesize; +}; + +#endif /* _APP_H_ */ diff --git a/slim/cfg.cpp b/slim/cfg.cpp new file mode 100644 index 0000000..37fb10d --- /dev/null +++ b/slim/cfg.cpp @@ -0,0 +1,478 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2012-13 Nobuhiro Iwamatsu + * Copyright (C) 2022-23 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "util.h" // for Util::random +#include "log.h" // for logStream + +#include "cfg.h" + +using namespace std; + +typedef pair option; + +/** + * Constructor: creates the Cfg object and populates the available options + * with default values. + */ +Cfg::Cfg() + : currentSession(-1) +{ + /* Configuration options */ + options.insert(option("default_path","/bin:/usr/bin:/usr/local/bin")); + options.insert(option("default_xserver","/usr/bin/X")); + options.insert(option("xserver_arguments","")); + options.insert(option("numlock","")); + options.insert(option("daemon","")); + options.insert(option("xauth_path","/usr/bin/xauth")); + options.insert(option("login_cmd","exec /bin/bash -login ~/.xinitrc %session")); + options.insert(option("halt_cmd","/sbin/shutdown -h now")); + options.insert(option("reboot_cmd","/sbin/shutdown -r now")); + options.insert(option("suspend_cmd","")); + options.insert(option("sessionstart_cmd","")); + options.insert(option("sessionstop_cmd","")); + options.insert(option("console_cmd","/usr/bin/xterm -C -fg white -bg black +sb -g %dx%d+%d+%d -fn %dx%d -T ""Console login"" -e /bin/sh -c ""/bin/cat /etc/issue; exec /bin/login""")); + options.insert(option("screenshot_cmd","import -window root /slim.png")); + options.insert(option("default_user","")); + options.insert(option("focus_password","no")); + options.insert(option("auto_login","no")); + options.insert(option("current_theme","default")); + options.insert(option("lockfile","/var/run/slim.lock")); + options.insert(option("logfile","/var/log/slim.log")); + options.insert(option("authfile","/var/run/slim.auth")); + options.insert(option("shutdown_msg","The system is halting...")); + options.insert(option("reboot_msg","The system is rebooting...")); + options.insert(option("sessions", "")); + options.insert(option("sessiondir","")); + options.insert(option("hidecursor","false")); + + /* Theme stuff */ + options.insert(option("background_style","stretch")); + options.insert(option("background_color","#CCCCCC")); + + options.insert(option("input_panel_x","50%")); /* Panel position on screen */ + options.insert(option("input_panel_y","40%")); + options.insert(option("input_font","Verdana:size=11")); + options.insert(option("input_color", "#000000")); + options.insert(option("input_shadow_xoffset", "0")); + options.insert(option("input_shadow_yoffset", "0")); + options.insert(option("input_shadow_color","#FFFFFF")); + options.insert(option("input_name_x","200")); /* relative to panel */ + options.insert(option("input_name_y","154")); + options.insert(option("input_pass_x","-1")); /* default is single inputbox */ + options.insert(option("input_pass_y","-1")); + + options.insert(option("welcome_msg","Welcome to %host")); + options.insert(option("welcome_font","Verdana:size=14")); + options.insert(option("welcome_color","#FFFFFF")); + options.insert(option("welcome_x","-1")); + options.insert(option("welcome_y","-1")); + options.insert(option("welcome_shadow_xoffset", "0")); + options.insert(option("welcome_shadow_yoffset", "0")); + options.insert(option("welcome_shadow_color","#FFFFFF")); + + options.insert(option("username_msg","Please enter your username")); + options.insert(option("username_font","Verdana:size=12")); + options.insert(option("username_color","#FFFFFF")); + options.insert(option("username_x","-1")); + options.insert(option("username_y","-1")); + options.insert(option("username_shadow_xoffset", "0")); + options.insert(option("username_shadow_yoffset", "0")); + options.insert(option("username_shadow_color","#FFFFFF")); + + options.insert(option("password_msg","Please enter your password")); + options.insert(option("password_x","-1")); + options.insert(option("password_y","-1")); + + options.insert(option("msg_font","Verdana:size=16:bold")); + options.insert(option("msg_color","#FFFFFF")); + options.insert(option("msg_x","40")); + options.insert(option("msg_y","40")); + options.insert(option("msg_shadow_xoffset", "0")); + options.insert(option("msg_shadow_yoffset", "0")); + options.insert(option("msg_shadow_color","#FFFFFF")); + + options.insert(option("session_msg","Session:")); + options.insert(option("session_font","Verdana:size=16:bold")); + options.insert(option("session_color","#FFFFFF")); + options.insert(option("session_x","50%")); + options.insert(option("session_y","90%")); + options.insert(option("session_shadow_xoffset", "0")); + options.insert(option("session_shadow_yoffset", "0")); + options.insert(option("session_shadow_color","#FFFFFF")); + + // What to do if the authorisation fails + options.insert(option("keep_user_on_fail", "0")); + options.insert(option("wrong_passwd_timeout", "2")); + options.insert(option("passwd_feedback_msg", "Authentication failed")); + options.insert(option("passwd_feedback_capslock", "Authentication failed (CapsLock is on)")); + options.insert(option("passwd_feedback_x", "-1")); /* no feedback by default */ + options.insert(option("passwd_feedback_y", "-1")); + options.insert(option("bell", "0")); + + // slimlock-specific options + options.insert(option("dpms_standby_timeout", "60")); + options.insert(option("dpms_off_timeout", "600")); + options.insert(option("show_username", "1")); + options.insert(option("show_welcome_msg", "0")); + options.insert(option("tty_lock", "1")); + + error = ""; +} + +Cfg::~Cfg() +{ + options.clear(); +} + +/** + * Parses known options from the given configfile / themefile + * + * @param configfile Path to configuration or theme + * @return true on sucess, false if file not found + */ +bool Cfg::readConf(string configfile) +{ + int n = -1; + size_t pos = 0; + string line, next, op, fn(configfile); + map::iterator it; + ifstream cfgfile(fn.c_str()); + + if (!cfgfile) + { + error = "Cannot read configuration file: " + configfile; + return false; + } + while (getline(cfgfile, line)) + { + // New parser to fix ticket #4 + pos = line.length(); + if ( ( pos > 0 ) && ( line[pos-1] == '\\' ) ) + { + line.replace ( pos-1, 1, " " ); + next = next + line; + continue; + } + + if ( !next.empty() ) + { + line = next + line; + next = ""; + } + + // Ignore blank lines and comment lines + if ( line.empty() || line[0] == '#' ) + continue; + + // Now parse and assign + if ( !parseOption ( line ) ) + cerr << error << '\n'; // not a fatal error + } + cfgfile.close(); + + fillSessionList(); + + return true; +} + +/** + * Sets an option value from a line. Returns true on success. + */ +bool Cfg::parseOption ( string line ) +{ + size_t pos = 0; + const string delims = " \t"; + string name, value; + + // First split the line into a name/value pair + pos = line.find_first_of ( delims ); + if ( pos == string::npos ) + { + error = "Badly formed line: " + line; + return false; + } + name = line.substr ( 0, pos ); + value = Trim ( line.substr ( pos ) ); + if ( value.empty() ) + { + error = "Badly formed line: " + line; + return false; + } + + // Look to see if it's a known option + if ( options.find ( name ) == options.end() ) + { + error = "Unknown option name: " + name; + return false; + } + // finally assign it + options[name] = value; + return true; +} + + +const string& Cfg::getError() const +{ + return error; +} + +string& Cfg::getOption(string option) +{ + return options[option]; +} + +/* return a trimmed string */ +string Cfg::Trim( const string& s ) +{ + if ( s.empty() ) { + return s; + } + int pos = 0; + string line = s; + int len = line.length(); + while ( pos < len && isspace( line[pos] ) ) { + ++pos; + } + line.erase( 0, pos ); + pos = line.length()-1; + while ( pos > -1 && isspace( line[pos] ) ) { + --pos; + } + if ( pos != -1 ) { + line.erase( pos+1 ); + } + return line; +} + +/* Return the welcome message with replaced vars */ +string Cfg::getWelcomeMessage() +{ + string s = getOption("welcome_msg"); + int n = s.find("%host"); + if (n >= 0) { + string tmp = s.substr(0, n); + char host[40]; + gethostname(host,40); + tmp = tmp + host; + tmp = tmp + s.substr(n+5, s.size() - n); + s = tmp; + } + n = s.find("%domain"); + if (n >= 0) { + string tmp = s.substr(0, n); + char domain[40]; + if ( getdomainname(domain,40) == 0 ) + tmp = tmp + domain; + else + tmp = tmp + ""; + tmp = tmp + s.substr(n+7, s.size() - n); + s = tmp; + } + return s; +} + +int Cfg::string2int(const char* string, bool* ok) +{ + char* err = 0; + int l = (int)strtol(string, &err, 10); + if (ok) { + *ok = (*err == 0); + } + return (*err == 0) ? l : 0; +} + +int Cfg::getIntOption(std::string option) +{ + return string2int(options[option].c_str()); +} + + +/** + * Get absolute position + * + * Converts a config position string into absolute coordinates. If the string + * is a plain number, this is just an atoi but if there is a percentage sign + * then the value is converted using the size of the canvas and the object. + * + * @param position Configured position as a string + * @param max Size of canvas in the relevant axis + * @param width Size of the object being placed + * @return Absolute coordinate to achieve placement + */ +int Cfg::absolutepos(const string& position, int max, int width) +{ + int n = position.find("%"); + if (n>0) { /* X Position expressed in percentage */ + int result = (max*string2int(position.substr(0, n).c_str())/100) - (width / 2); + return result < 0 ? 0 : result ; + } else { /* Absolute X position */ + return string2int(position.c_str()); + } +} + +/* split a comma separated string into a vector of strings */ +void Cfg::split(vector& v, const string& str, char c, bool useEmpty) +{ + v.clear(); + string::const_iterator s = str.begin(); + string tmp; + while (true) { + string::const_iterator begin = s; + while (*s != c && s != str.end()) + { + ++s; + } + tmp = string(begin, s); + if (useEmpty || tmp.size() > 0) + v.push_back(tmp); + if (s == str.end()) { + break; + } + if (++s == str.end()) { + if (useEmpty) + v.push_back(""); + break; + } + } +} + +void Cfg::fillSessionList() +{ + string strSessionList = getOption("sessions"); + string strSessionDir = getOption("sessiondir"); + + sessions.clear(); + + if ( !strSessionDir.empty() ) + { + DIR *pDir = opendir(strSessionDir.c_str()); + + if (pDir != NULL) { + struct dirent *pDirent = NULL; + + while ((pDirent = readdir(pDir)) != NULL) { + string strFile(strSessionDir); + strFile += "/"; + strFile += pDirent->d_name; + + struct stat oFileStat; + + if (stat(strFile.c_str(), &oFileStat) == 0) { + if (S_ISREG(oFileStat.st_mode) && + access(strFile.c_str(), R_OK) == 0){ + ifstream desktop_file(strFile.c_str()); + if (desktop_file){ + string line, session_name = "", session_exec = ""; + while (getline( desktop_file, line )) { + if (line.substr(0, 5) == "Name=") { + session_name = line.substr(5); + if (!session_exec.empty()) + break; + } + else if (line.substr(0, 5) == "Exec=") { + session_exec = line.substr(5); + if (!session_name.empty()) + break; + } + } + desktop_file.close(); + if (!session_name.empty() && !session_exec.empty()) { + pair session(session_name,session_exec); + sessions.push_back(session); + } else if (access(strFile.c_str(), X_OK) == 0) { + pair session(string(pDirent->d_name),strFile); + sessions.push_back(session); + } + } + } + } + } + closedir(pDir); + } + } + + if (sessions.empty()) + { + if (strSessionList.empty()) + { + pair session("",""); + sessions.push_back(session); + } + else + { + // iterate through the split of the session list + vector sessit; + split(sessit,strSessionList,',',false); + for (vector::iterator it = sessit.begin(); it != sessit.end(); ++it) + { + pair session(*it,*it); + sessions.push_back(session); + } + } + } +} + + +pair Cfg::nextSession() +{ + currentSession = (currentSession + 1) % sessions.size(); + return sessions[currentSession]; +} + + +/* + * Choose a theme at random from the list in the config file. IF the theme + * file cannot be found then issue a warning and try again. + */ +string Cfg::findValidRandomTheme ( const string& set ) +{ + /* extract random theme from theme set; return empty string on error */ + string name = set; + struct stat buf; + + if (name[name.length()-1] == ',') + { + name.erase(name.length() - 1); + } + + Util::srandom(Util::makeseed()); + + vector themes; + string themefile; + Cfg::split(themes, name, ','); + do { + int sel = Util::random() % themes.size(); + + name = Cfg::Trim(themes[sel]); + themefile = string(THEMESDIR) +"/" + name + THEMESFILE; + if (stat(themefile.c_str(), &buf) != 0) + { + themes.erase(find(themes.begin(), themes.end(), name)); + logStream << APPNAME << ": Invalid theme in config: " + << name << endl; + name = ""; + } + } while (name == "" && themes.size()); + return name; +} + diff --git a/slim/cfg.h b/slim/cfg.h new file mode 100644 index 0000000..f258908 --- /dev/null +++ b/slim/cfg.h @@ -0,0 +1,58 @@ +/* SLiM - Simple Login Manager + Copyright (C) 2004-06 Simone Rota + Copyright (C) 2004-06 Johannes Winkelmann + + 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 of the License, or + (at your option) any later version. +*/ + +#ifndef _CFG_H_ +#define _CFG_H_ + +#include +#include +#include + +#define INPUT_MAXLENGTH_NAME 30 +#define INPUT_MAXLENGTH_PASSWD 50 + +#define CFGFILE SYSCONFDIR"/slim.conf" +#define THEMESDIR PKGDATADIR"/themes" +#define THEMESFILE "/slim.theme" + +class Cfg +{ + +public: + Cfg(); + ~Cfg(); + + bool readConf(std::string configfile); + bool parseOption ( std::string line ); + const std::string& getError() const; + std::string& getOption(std::string option); + int getIntOption(std::string option); + std::string getWelcomeMessage(); + + static int absolutepos(const std::string &position, int max, int width); + static int string2int(const char *string, bool *ok = 0); + static void split(std::vector &v, const std::string &str, + char c, bool useEmpty=true); + static std::string Trim(const std::string &s); + + std::pair nextSession(); + static std::string findValidRandomTheme(const std::string &set); + +private: + void fillSessionList(); + +private: + std::map options; + std::vector > sessions; + int currentSession; + std::string error; +}; + +#endif /* _CFG_H_ */ diff --git a/slim/cmake/modules/FONTCONFIGConfig.cmake b/slim/cmake/modules/FONTCONFIGConfig.cmake new file mode 100644 index 0000000..60128c4 --- /dev/null +++ b/slim/cmake/modules/FONTCONFIGConfig.cmake @@ -0,0 +1,71 @@ +# +# Find the native FONTCONFIG includes and library +# + +# This module defines +# FONTCONFIG_INCLUDE_DIR, where to find art*.h etc +# FONTCONFIG_LIBRARY, the libraries to link against to use FONTCONFIG. +# FONTCONFIG_FOUND, If false, do not try to use FONTCONFIG. +# LIBFONTCONFIG_LIBS, link information +# LIBFONTCONFIG_CFLAGS, cflags for include information + +IF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.5) + INCLUDE(UsePkgConfig) + PKGCONFIG(fontconfig _fontconfigIncDir _fontconfigLinkDir _fontconfigLinkFlags _fontconfigCflags) + SET(FONTCONFIG_LIBS ${_fontconfigCflags}) +ELSE (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.5) + INCLUDE(FindPkgConfig) + pkg_search_module(FONTCONFIG REQUIRED fontconfig) +ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} LESS 2.5) + +#INCLUDE(UsePkgConfig) + +# use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls +#PKGCONFIG(fontconfig _fontconfigIncDir _fontconfigLinkDir _fontconfigLinkFlags _fontconfigCflags) + +#SET(FONTCONFIG_LIBS ${_fontconfigCflags}) + +IF(BUILD_OSX_BUNDLE) + FIND_PATH(FONTCONFIG_INCLUDE_DIR + NAMES fontconfig/fontconfig.h + PATHS ${FONTCONFIG_INCLUDE_DIRS} /opt/local/include + NO_DEFAULT_PATH + ) + FIND_LIBRARY(FONTCONFIG_LIBRARY + NAMES fontconfig + PATHS ${FONTCONFIG_LIBRARY_DIRS} /opt/local/lib + NO_DEFAULT_PATH + ) +ELSE(BUILD_OSX_BUNDLE) + FIND_PATH(FONTCONFIG_INCLUDE_DIR + NAMES fontconfig/fontconfig.h + PATHS ${FONTCONFIG_INCLUDE_DIRS} + ${_fontconfigIncDir} + /usr/include + /usr/local/include + PATH_SUFFIXES fontconfig + ) + # quick hack as the above finds it nicely but our source includes the libart_lgpl text at the moment + #STRING(REGEX REPLACE "/libart_lgpl" "" FONTCONFIG_INCLUDE_DIR ${FONTCONFIG_INCLUDE_DIR}) + FIND_LIBRARY(FONTCONFIG_LIBRARY NAMES fontconfig + PATHS ${FONTCONFIG_LIBRARY_DIRS} /usr/lib /usr/local/lib + ) +ENDIF(BUILD_OSX_BUNDLE) + + +# MESSAGE(STATUS "fclib ${FONTCONFIG_LIBRARY}") +# MESSAGE(STATUS "fcinclude ${FONTCONFIG_INCLUDE_DIR}") + + +IF (FONTCONFIG_LIBRARY) + IF (FONTCONFIG_INCLUDE_DIR) + SET( FONTCONFIG_FOUND "YES" ) + SET( FONTCONFIG_LIBRARIES ${FONTCONFIG_LIBRARY} ) + FIND_PROGRAM(FONTCONFIG_CONFIG NAMES fontconfig-config PATHS ${prefix}/bin ${exec_prefix}/bin /usr/local/bin /opt/local/bin /usr/bin /usr/nekoware/bin /usr/X11/bin) +# EXEC_PROGRAM(${FONTCONFIG_CONFIG} ARGS --libs OUTPUT_VARIABLE FONTCONFIG_LIBS) +# EXEC_PROGRAM(${FONTCONFIG_CONFIG} ARGS --cflags OUTPUT_VARIABLE FONTCONFIG_CFLAGS) +# MESSAGE(STATUS ${FONTCONFIG_LIBS}) +# MESSAGE(STATUS ${FONTCONFIG_CFLAGS}) + ENDIF (FONTCONFIG_INCLUDE_DIR) +ENDIF (FONTCONFIG_LIBRARY) diff --git a/slim/cmake/modules/FindCkConnector.cmake b/slim/cmake/modules/FindCkConnector.cmake new file mode 100644 index 0000000..5130ca0 --- /dev/null +++ b/slim/cmake/modules/FindCkConnector.cmake @@ -0,0 +1,59 @@ +# - Try to find the ConsoleKit connector library (libck-connector) +# Once done this will define +# +# CKCONNECTOR_FOUND - system has the CK Connector +# CKCONNECTOR_INCLUDE_DIR - the CK Connector include directory +# CKCONNECTOR_LIBRARIES - the libraries needed to use CK Connector + +# Copyright (c) 2008, Kevin Kofler, +# modeled after FindLibArt.cmake: +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +if (CKCONNECTOR_INCLUDE_DIR AND CKCONNECTOR_LIBRARIES) + + # in cache already + SET(CKCONNECTOR_FOUND TRUE) + +else (CKCONNECTOR_INCLUDE_DIR AND CKCONNECTOR_LIBRARIES) + + IF (NOT WIN32) + FIND_PACKAGE(PkgConfig) + IF (PKG_CONFIG_FOUND) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + pkg_check_modules(_CKCONNECTOR_PC QUIET ck-connector) + ENDIF (PKG_CONFIG_FOUND) + ENDIF (NOT WIN32) + + FIND_PATH(CKCONNECTOR_INCLUDE_DIR ck-connector.h + ${_CKCONNECTOR_PC_INCLUDE_DIRS} + ) + + FIND_LIBRARY(CKCONNECTOR_LIBRARIES NAMES ck-connector + PATHS + ${_CKCONNECTOR_PC_LIBDIR} + ) + + + if (CKCONNECTOR_INCLUDE_DIR AND CKCONNECTOR_LIBRARIES) + set(CKCONNECTOR_FOUND TRUE) + endif (CKCONNECTOR_INCLUDE_DIR AND CKCONNECTOR_LIBRARIES) + + + if (CKCONNECTOR_FOUND) + if (NOT CkConnector_FIND_QUIETLY) + message(STATUS "Found ck-connector: ${CKCONNECTOR_LIBRARIES}") + endif (NOT CkConnector_FIND_QUIETLY) + else (CKCONNECTOR_FOUND) + if (CkConnector_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find ck-connector") + endif (CkConnector_FIND_REQUIRED) + endif (CKCONNECTOR_FOUND) + + MARK_AS_ADVANCED(CKCONNECTOR_INCLUDE_DIR CKCONNECTOR_LIBRARIES) + +endif (CKCONNECTOR_INCLUDE_DIR AND CKCONNECTOR_LIBRARIES) diff --git a/slim/cmake/modules/FindDBus.cmake b/slim/cmake/modules/FindDBus.cmake new file mode 100644 index 0000000..f227cc2 --- /dev/null +++ b/slim/cmake/modules/FindDBus.cmake @@ -0,0 +1,72 @@ +# - Try to find the low-level D-Bus library +# Once done this will define +# +# DBUS_FOUND - system has D-Bus +# DBUS_INCLUDE_DIR - the D-Bus include directory +# DBUS_ARCH_INCLUDE_DIR - the D-Bus architecture-specific include directory +# DBUS_LIBRARIES - the libraries needed to use D-Bus + +# Copyright (c) 2008, Kevin Kofler, +# modeled after FindLibArt.cmake: +# Copyright (c) 2006, Alexander Neundorf, +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + +if (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) + + # in cache already + SET(DBUS_FOUND TRUE) + +else (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) + + IF (NOT WIN32) + FIND_PACKAGE(PkgConfig) + IF (PKG_CONFIG_FOUND) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + pkg_check_modules(_DBUS_PC QUIET dbus-1) + ENDIF (PKG_CONFIG_FOUND) + ENDIF (NOT WIN32) + + FIND_PATH(DBUS_INCLUDE_DIR dbus/dbus.h + ${_DBUS_PC_INCLUDE_DIRS} + /usr/include + /usr/include/dbus-1.0 + /usr/local/include + ) + + FIND_PATH(DBUS_ARCH_INCLUDE_DIR dbus/dbus-arch-deps.h + ${_DBUS_PC_INCLUDE_DIRS} + /usr/lib${LIB_SUFFIX}/include + /usr/lib${LIB_SUFFIX}/dbus-1.0/include + /usr/lib64/include + /usr/lib64/dbus-1.0/include + /usr/lib/include + /usr/lib/dbus-1.0/include + ) + + FIND_LIBRARY(DBUS_LIBRARIES NAMES dbus-1 dbus + PATHS + ${_DBUS_PC_LIBDIR} + ) + + + if (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) + set(DBUS_FOUND TRUE) + endif (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) + + + if (DBUS_FOUND) + if (NOT DBus_FIND_QUIETLY) + message(STATUS "Found D-Bus: ${DBUS_LIBRARIES}") + endif (NOT DBus_FIND_QUIETLY) + else (DBUS_FOUND) + if (DBus_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find D-Bus") + endif (DBus_FIND_REQUIRED) + endif (DBUS_FOUND) + + MARK_AS_ADVANCED(DBUS_INCLUDE_DIR DBUS_ARCH_INCLUDE_DIR DBUS_LIBRARIES) + +endif (DBUS_INCLUDE_DIR AND DBUS_ARCH_INCLUDE_DIR AND DBUS_LIBRARIES) diff --git a/slim/cmake/modules/FindPAM.cmake b/slim/cmake/modules/FindPAM.cmake new file mode 100644 index 0000000..f209c0b --- /dev/null +++ b/slim/cmake/modules/FindPAM.cmake @@ -0,0 +1,74 @@ +# - Try to find the PAM libraries +# Once done this will define +# +# PAM_FOUND - system has pam +# PAM_INCLUDE_DIR - the pam include directory +# PAM_LIBRARIES - libpam library + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + # Already in cache, be silent + set(PAM_FIND_QUIETLY TRUE) +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h) +find_library(PAM_LIBRARY pam) +find_library(DL_LIBRARY dl) + +if (PAM_INCLUDE_DIR AND PAM_LIBRARY) + set(PAM_FOUND TRUE) + if (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY}) + else (DL_LIBRARY) + set(PAM_LIBRARIES ${PAM_LIBRARY}) + endif (DL_LIBRARY) + + if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + # darwin claims to be something special + set(HAVE_PAM_PAM_APPL_H 1) + endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h) + + if (NOT DEFINED PAM_MESSAGE_CONST) + include(CheckCXXSourceCompiles) + # XXX does this work with plain c? + check_cxx_source_compiles(" +#if ${HAVE_PAM_PAM_APPL_H}+0 +# include +#else +# include +#endif + +static int PAM_conv( + int num_msg, + const struct pam_message **msg, /* this is the culprit */ + struct pam_response **resp, + void *ctx) +{ + return 0; +} + +int main(void) +{ + struct pam_conv PAM_conversation = { + &PAM_conv, /* this bombs out if the above does not match */ + 0 + }; + + return 0; +} +" PAM_MESSAGE_CONST) + endif (NOT DEFINED PAM_MESSAGE_CONST) + set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message") + +endif (PAM_INCLUDE_DIR AND PAM_LIBRARY) + +if (PAM_FOUND) + if (NOT PAM_FIND_QUIETLY) + message(STATUS "Found PAM: ${PAM_LIBRARIES}") + endif (NOT PAM_FIND_QUIETLY) +else (PAM_FOUND) + if (PAM_FIND_REQUIRED) + message(FATAL_ERROR "PAM was not found") + endif(PAM_FIND_REQUIRED) +endif (PAM_FOUND) + +mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST) \ No newline at end of file diff --git a/slim/const.h b/slim/const.h new file mode 100644 index 0000000..e1bc5af --- /dev/null +++ b/slim/const.h @@ -0,0 +1,40 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2022-23 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#ifndef _CONST_H_ +#define _CONST_H_ + +#define DISPLAY ":0.0" + +#define CONSOLE_STR "console" +#define HALT_STR "halt" +#define REBOOT_STR "reboot" +#define EXIT_STR "exit" +#define SUSPEND_STR "suspend" + +#define HIDE 0 +#define SHOW 1 + +#define OK_EXIT 0 +#define ERR_EXIT 1 + +/* variables replaced in login_cmd */ +#define SESSION_VAR "%session" +#define THEME_VAR "%theme" + +/* variables replaced in pre-session_cmd and post-session_cmd */ +#define USER_VAR "%user" + +/* max height/width for images */ +#define MAX_DIMENSION 10000 + +#endif /* _CONST_H_ */ diff --git a/slim/image.cpp b/slim/image.cpp new file mode 100644 index 0000000..4db4756 --- /dev/null +++ b/slim/image.cpp @@ -0,0 +1,958 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2012 Nobuhiro Iwamatsu + * Copyright (C) 2022-23 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + * + * The following code has been adapted and extended from + * xplanet 1.0.1, Copyright (C) 2002-04 Hari Nair + */ + +#include +#include +#include +#include +#include +#include + +using namespace std; + +#include "log.h" +#include "const.h" +#include "image.h" + +extern "C" { + #include + #include +} + +Image::Image() + : width(0), height(0), area(0), rgb_data(NULL), png_alpha(NULL) +{ +} + +Image::Image ( const int w, const int h, const unsigned char *rgb, + const unsigned char *alpha) + : width(w), height(h), area(w*h) +{ + width = w; + height = h; + area = w * h; + + rgb_data = (unsigned char *) malloc(3 * area); + memcpy(rgb_data, rgb, 3 * area); + + if (alpha == NULL) { + png_alpha = NULL; + } else { + png_alpha = (unsigned char *) malloc(area); + memcpy(png_alpha, alpha, area); + } +} + +Image::~Image() +{ + free(rgb_data); + free(png_alpha); +} + +bool Image::Read(const char *filename) +{ + char buf[4]; + unsigned char *ubuf = (unsigned char *) buf; + int success; + int nr; + + FILE *file; + file = fopen(filename, "rb"); + if (file == NULL) + return(false); + + /* see what kind of file we have */ + + nr = fread(buf, 1, 4, file); + fclose(file); + if ( nr < 4 ) + return false; // Failed to read 4 bytes; probably empty file + + if ((ubuf[0] == 0x89) && !strncmp("PNG", buf+1, 3)) + success = readPng(filename, &width, &height, &rgb_data, &png_alpha); + else if ((ubuf[0] == 0xff) && (ubuf[1] == 0xd8)) + success = readJpeg(filename, &width, &height, &rgb_data); + else + { + fprintf(stderr, "Unknown image format\n"); + success = 0; + } + return(success == 1); +} + +void Image::Reduce(const int factor) +{ + if (factor < 1) + return; + + int scale = 1; + for (int i = 0; i < factor; i++) + scale *= 2; + + double scale2 = scale*scale; + + int w = width / scale; + int h = height / scale; + int new_area = w * h; + + unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area); + memset(new_rgb, 0, 3 * new_area); + + unsigned char *new_alpha = NULL; + if (png_alpha != NULL) { + new_alpha = (unsigned char *) malloc(new_area); + memset(new_alpha, 0, new_area); + } + + int ipos = 0; + for (int j = 0; j < height; j++) { + int js = j / scale; + for (int i = 0; i < width; i++) { + int is = i/scale; + for (int k = 0; k < 3; k++) + new_rgb[3*(js * w + is) + k] += static_cast ((rgb_data[3*ipos + k] + 0.5) / scale2); + + if (png_alpha != NULL) + new_alpha[js * w + is] += static_cast (png_alpha[ipos]/scale2); + ipos++; + } + } + + free(rgb_data); + free(png_alpha); + + rgb_data = new_rgb; + png_alpha = new_alpha; + width = w; + height = h; + + area = w * h; +} + +void Image::Resize(const int w, const int h) +{ + + if (width==w && height==h){ + return; + } + + int new_area = w * h; + + unsigned char *new_rgb = (unsigned char *) malloc(3 * new_area); + unsigned char *new_alpha = NULL; + if (png_alpha != NULL) + new_alpha = (unsigned char *) malloc(new_area); + + const double scale_x = ((double) w) / width; + const double scale_y = ((double) h) / height; + + int ipos = 0; + for (int j = 0; j < h; j++) { + const double y = j / scale_y; + for (int i = 0; i < w; i++) { + const double x = i / scale_x; + if (new_alpha == NULL) + getPixel(x, y, new_rgb + 3*ipos); + else + getPixel(x, y, new_rgb + 3*ipos, new_alpha + ipos); + ipos++; + } + } + + free(rgb_data); + free(png_alpha); + + rgb_data = new_rgb; + png_alpha = new_alpha; + width = w; + height = h; + + area = w * h; +} + +/* Find the color of the desired point using bilinear interpolation. */ +/* Assume the array indices refer to the center of the pixel, so each */ +/* pixel has corners at (i - 0.5, j - 0.5) and (i + 0.5, j + 0.5) */ +void Image::getPixel(double x, double y, unsigned char *pixel) +{ + getPixel(x, y, pixel, NULL); +} + +void Image::getPixel(double x, double y, unsigned char *pixel, unsigned char *alpha) +{ + if (x < -0.5) + x = -0.5; + if (x >= width - 0.5) + x = width - 0.5; + + if (y < -0.5) + y = -0.5; + if (y >= height - 0.5) + y = height - 0.5; + + int ix0 = (int) (floor(x)); + int ix1 = ix0 + 1; + if (ix0 < 0) + ix0 = width - 1; + if (ix1 >= width) + ix1 = 0; + + int iy0 = (int) (floor(y)); + int iy1 = iy0 + 1; + if (iy0 < 0) + iy0 = 0; + if (iy1 >= height) + iy1 = height - 1; + + const double t = x - floor(x); + const double u = 1 - (y - floor(y)); + + double weight[4]; + weight[1] = t * u; + weight[0] = u - weight[1]; + weight[2] = 1 - t - u + weight[1]; + weight[3] = t - weight[1]; + + unsigned char *pixels[4]; + pixels[0] = rgb_data + 3 * (iy0 * width + ix0); + pixels[1] = rgb_data + 3 * (iy0 * width + ix1); + pixels[2] = rgb_data + 3 * (iy1 * width + ix0); + pixels[3] = rgb_data + 3 * (iy1 * width + ix1); + + memset(pixel, 0, 3); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) + pixel[j] += (unsigned char) (weight[i] * pixels[i][j]); + } + + if (alpha != NULL) { + unsigned char pixels[4]; + pixels[0] = png_alpha[iy0 * width + ix0]; + pixels[1] = png_alpha[iy0 * width + ix1]; + pixels[2] = png_alpha[iy0 * width + ix0]; + pixels[3] = png_alpha[iy1 * width + ix1]; + + for (int i = 0; i < 4; i++) + *alpha = (unsigned char) (weight[i] * pixels[i]); + } +} + + +/** + * Merge the image with a background, taking care of the image Alpha + * transparency. (background alpha is ignored). + * + * The image is merged with the section of background at position (x, y). + * The background must fully contain the image. + * If the image does not have any transparency (no alpha data) then this + * is a no-operation. + * @note use of double to calculate the new value of a U8 + */ +void Image::Merge ( const Image* background, const int x, const int y ) +{ + if ( ( x + width > background->Width() ) + || ( y + height > background->Height() ) ) + return; + + if (png_alpha != NULL) + { + unsigned char *new_rgb = (unsigned char *) malloc(3 * width * height); + const unsigned char *bg_rgb = background->getRGBData(); + double tmp; + int opos = 0; + for (int j = 0; j < height; j++) + { + int ipos = (y+j) * background->Width() + x; + for (int i = 0; i < width; i++) + { + for (int k = 0; k < 3; k++) + { + tmp = rgb_data[3*opos + k]*png_alpha[opos]/255.0 + + bg_rgb[3*ipos + k]*(1-png_alpha[opos]/255.0); + new_rgb[3*opos + k] = static_cast (tmp); + } + opos++; + ipos++; + } + } + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + png_alpha = NULL; + } +} + + +/* Merge the image with a background, taking care of the + * image Alpha transparency. (background alpha is ignored). + * The images is merged on position (x, y) on the + * background, the background must contain the image. + */ +#define IMG_POS_RGB(p, x) (3 * p + x) +void Image::Merge_non_crop(Image* background, const int x, const int y) +{ + int bg_w = background->Width(); + int bg_h = background->Height(); + + if (x + width > bg_w || y + height > bg_h) + return; + + double tmp; + unsigned char *new_rgb = (unsigned char *)malloc(3 * bg_w * bg_h); + const unsigned char *bg_rgb = background->getRGBData(); + int pnl_pos = 0; + int bg_pos = 0; + int pnl_w_end = x + width; + int pnl_h_end = y + height; + + memcpy(new_rgb, bg_rgb, 3 * bg_w * bg_h); + + for (int j = 0; j < bg_h; j++) { + for (int i = 0; i < bg_w; i++) { + if (j >= y && i >= x && j < pnl_h_end && i < pnl_w_end ) { + for (int k = 0; k < 3; k++) { + if (png_alpha != NULL) + tmp = rgb_data[IMG_POS_RGB(pnl_pos, k)] + * png_alpha[pnl_pos]/255.0 + + bg_rgb[IMG_POS_RGB(bg_pos, k)] + * (1 - png_alpha[pnl_pos]/255.0); + else + tmp = rgb_data[IMG_POS_RGB(pnl_pos, k)]; + + new_rgb[IMG_POS_RGB(bg_pos, k)] = static_cast(tmp); + } + pnl_pos++; + } + bg_pos++; + } + } + + width = bg_w; + height = bg_h; + + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + png_alpha = NULL; +} + + +/* Tile the image growing its size to the minimum entire + * multiple of w * h. + * The new dimensions should be > of the current ones. + * Note that this flattens image (alpha removed) + */ +void Image::Tile(const int w, const int h) +{ + if (w < width || h < height) + return; + + int nx = w / width; + if (w % width > 0) + nx++; + int ny = h / height; + if (h % height > 0) + ny++; + + int newwidth = nx*width; + int newheight=ny*height; + + unsigned char *new_rgb = (unsigned char *) malloc(3 * newwidth * newheight); + memset(new_rgb, 0, 3 * width * height * nx * ny); + + int ipos = 0; + int opos = 0; + + for (int r = 0; r < ny; r++) { + for (int c = 0; c < nx; c++) { + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + opos = j*width + i; + ipos = r*width*height*nx + j*newwidth + c*width +i; + for (int k = 0; k < 3; k++) { + new_rgb[3*ipos + k] = static_cast (rgb_data[3*opos + k]); + } + } + } + } + } + + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + png_alpha = NULL; + width = newwidth; + height = newheight; + area = width * height; + Crop(0,0,w,h); +} + + +/* Crop the image + */ +void Image::Crop(const int x, const int y, const int w, const int h) +{ + if (x+w > width || y+h > height) { + return; + } + + int x2 = x + w; + int y2 = y + h; + unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h); + memset(new_rgb, 0, 3 * w * h); + unsigned char *new_alpha = NULL; + if (png_alpha != NULL) { + new_alpha = (unsigned char *) malloc(w * h); + memset(new_alpha, 0, w * h); + } + + int ipos = 0; + int opos = 0; + + for (int j = 0; j < height; j++) { + for (int i = 0; i < width; i++) { + if (j>=y && i>=x && j (rgb_data[3*opos + k]); + } + if (png_alpha != NULL) + new_alpha[ipos] = static_cast (png_alpha[opos]); + ipos++; + } + opos++; + } + } + + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + if (png_alpha != NULL) + png_alpha = new_alpha; + width = w; + height = h; + area = w * h; +} + + +/* Center the image in a rectangle of given width and height. + * Fills the remaining space (if any) with the hex color + */ +void Image::Center(const int w, const int h, const char *hex) +{ + unsigned long packed_rgb; + sscanf(hex, "%lx", &packed_rgb); + + unsigned long r = packed_rgb>>16; + unsigned long g = packed_rgb>>8 & 0xff; + unsigned long b = packed_rgb & 0xff; + + unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h); + memset(new_rgb, 0, 3 * w * h); + + int x = (w - width) / 2; + int y = (h - height) / 2; + + if (x<0) { + Crop((width - w)/2,0,w,height); + x = 0; + } + if (y<0) { + Crop(0,(height - h)/2,width,h); + y = 0; + } + int x2 = x + width; + int y2 = y + height; + + int ipos = 0; + int opos = 0; + double tmp; + + area = w * h; + for (int i = 0; i < area; i++) { + new_rgb[3*i] = r; + new_rgb[3*i+1] = g; + new_rgb[3*i+2] = b; + } + + if (png_alpha != NULL) { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + if (j>=y && i>=x && j (tmp); + } + opos++; + } + } + } + } else { + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + if (j>=y && i>=x && j (tmp); + } + opos++; + } + } + } + } + + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + png_alpha = NULL; + width = w; + height = h; +} + + +/* Fill the image with the given color and adjust its dimensions + * to passed values. + */ +void Image::Plain(const int w, const int h, const char *hex) +{ + unsigned long packed_rgb; + sscanf(hex, "%lx", &packed_rgb); + + unsigned long r = packed_rgb>>16; + unsigned long g = packed_rgb>>8 & 0xff; + unsigned long b = packed_rgb & 0xff; + + unsigned char *new_rgb = (unsigned char *) malloc(3 * w * h); + memset(new_rgb, 0, 3 * w * h); + + area = w * h; + for (int i = 0; i < area; i++) { + new_rgb[3*i] = r; + new_rgb[3*i+1] = g; + new_rgb[3*i+2] = b; + } + + free(rgb_data); + free(png_alpha); + rgb_data = new_rgb; + png_alpha = NULL; + width = w; + height = h; +} + + +void Image::computeShift(unsigned long mask, + unsigned char &left_shift, + unsigned char &right_shift) +{ + left_shift = 0; + right_shift = 8; + if (mask != 0) { + while ((mask & 0x01) == 0) { + left_shift++; + mask >>= 1; + } + while ((mask & 0x01) == 1) { + right_shift--; + mask >>= 1; + } + } +} + + +Pixmap Image::createPixmap(Display* dpy, int scr, Window win) +{ + int i, j; /* loop variables */ + + const int depth = DefaultDepth(dpy, scr); + Visual *visual = DefaultVisual(dpy, scr); + Colormap colormap = DefaultColormap(dpy, scr); + + Pixmap tmp = XCreatePixmap(dpy, win, width, height, depth); + + char *pixmap_data = NULL; + switch (depth) { + case 32: + case 24: + pixmap_data = new char[4 * width * height]; + break; + case 16: + case 15: + pixmap_data = new char[2 * width * height]; + break; + case 8: + pixmap_data = new char[width * height]; + break; + default: + break; + } + + XImage *ximage = XCreateImage(dpy, visual, depth, ZPixmap, 0, + pixmap_data, width, height, + 8, 0); + + int entries; + XVisualInfo v_template; + v_template.visualid = XVisualIDFromVisual(visual); + XVisualInfo *visual_info = XGetVisualInfo(dpy, VisualIDMask, + &v_template, &entries); + + unsigned long ipos = 0; + switch (visual_info->c_class) { + case PseudoColor: { + XColor xc; + xc.flags = DoRed | DoGreen | DoBlue; + + int num_colors = 256; + XColor *colors = new XColor[num_colors]; + for (i = 0; i < num_colors; i++) + colors[i].pixel = (unsigned long) i; + XQueryColors(dpy, colormap, colors, num_colors); + + int *closest_color = new int[num_colors]; + + for (i = 0; i < num_colors; i++) { + xc.red = (i & 0xe0) << 8; /* highest 3 bits */ + xc.green = (i & 0x1c) << 11; /* middle 3 bits */ + xc.blue = (i & 0x03) << 14; /* lowest 2 bits */ + + /* find the closest color in the colormap */ + double distance, distance_squared, min_distance = 0; + for (int ii = 0; ii < num_colors; ii++) { + distance = colors[ii].red - xc.red; + distance_squared = distance * distance; + distance = colors[ii].green - xc.green; + distance_squared += distance * distance; + distance = colors[ii].blue - xc.blue; + distance_squared += distance * distance; + + if ((ii == 0) || (distance_squared <= min_distance)) { + min_distance = distance_squared; + closest_color[i] = ii; + } + } + } + + for (j = 0; j < height; j++) { + for (i = 0; i < width; i++) { + xc.red = (unsigned short) (rgb_data[ipos++] & 0xe0); + xc.green = (unsigned short) (rgb_data[ipos++] & 0xe0); + xc.blue = (unsigned short) (rgb_data[ipos++] & 0xc0); + + xc.pixel = xc.red | (xc.green >> 3) | (xc.blue >> 6); + XPutPixel(ximage, i, j, + colors[closest_color[xc.pixel]].pixel); + } + } + delete [] colors; + delete [] closest_color; + } + break; + case TrueColor: { + unsigned char red_left_shift; + unsigned char red_right_shift; + unsigned char green_left_shift; + unsigned char green_right_shift; + unsigned char blue_left_shift; + unsigned char blue_right_shift; + + computeShift(visual_info->red_mask, red_left_shift, + red_right_shift); + computeShift(visual_info->green_mask, green_left_shift, + green_right_shift); + computeShift(visual_info->blue_mask, blue_left_shift, + blue_right_shift); + + unsigned long pixel; + unsigned long red, green, blue; + for (j = 0; j < height; j++) { + for (i = 0; i < width; i++) { + red = (unsigned long) + rgb_data[ipos++] >> red_right_shift; + green = (unsigned long) + rgb_data[ipos++] >> green_right_shift; + blue = (unsigned long) + rgb_data[ipos++] >> blue_right_shift; + + pixel = (((red << red_left_shift) & visual_info->red_mask) + | ((green << green_left_shift) + & visual_info->green_mask) + | ((blue << blue_left_shift) + & visual_info->blue_mask)); + + XPutPixel(ximage, i, j, pixel); + } + } + } + break; + default: { + logStream << APPNAME << ": could not load image" << endl; + return(tmp); + } + } + + GC gc = XCreateGC(dpy, win, 0, NULL); + XPutImage(dpy, tmp, gc, ximage, 0, 0, 0, 0, width, height); + + XFreeGC(dpy, gc); + + XFree(visual_info); + + delete [] pixmap_data; + + /* Set ximage data to NULL since pixmap data was deallocated above */ + ximage->data = NULL; + XDestroyImage(ximage); + + return(tmp); +} + +int Image::readJpeg(const char *filename, int *width, int *height, + unsigned char **rgb) +{ + int ret = 0; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char *ptr = NULL; + + FILE *infile = fopen(filename, "rb"); + if (infile == NULL) { + logStream << APPNAME << "Cannot fopen file: " << filename << endl; + return ret; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + /* Prevent against integer overflow */ + if ( cinfo.output_width >= MAX_DIMENSION + || cinfo.output_height >= MAX_DIMENSION) + { + logStream << APPNAME << "Unreasonable dimension found in file: " + << filename << endl; + goto close_file; + } + + *width = cinfo.output_width; + *height = cinfo.output_height; + + rgb[0] = (unsigned char*) + malloc(3 * cinfo.output_width * cinfo.output_height); + if (rgb[0] == NULL) { + logStream << APPNAME << ": Can't allocate memory for JPEG file." + << endl; + goto close_file; + } + + if (cinfo.output_components == 3) { + ptr = rgb[0]; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &ptr, 1); + ptr += 3 * cinfo.output_width; + } + } else if (cinfo.output_components == 1) { + ptr = (unsigned char*) malloc(cinfo.output_width); + if (ptr == NULL) { + logStream << APPNAME << ": Can't allocate memory for JPEG file." + << endl; + goto rgb_free; + } + + unsigned int ipos = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &ptr, 1); + + for (unsigned int i = 0; i < cinfo.output_width; i++) { + memset(rgb[0] + ipos, ptr[i], 3); + ipos += 3; + } + } + + free(ptr); + } + + jpeg_finish_decompress(&cinfo); + + ret = 1; + goto close_file; + +rgb_free: + free(rgb[0]); + +close_file: + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + return(ret); +} + +int Image::readPng(const char *filename, int *width, int *height, + unsigned char **rgb, unsigned char **alpha) +{ + int ret = 0; + + png_structp png_ptr; + png_infop info_ptr; + png_bytepp row_pointers; + + unsigned char *ptr = NULL; + png_uint_32 w, h; + int bit_depth, color_type, interlace_type; + int i; + + FILE *infile = fopen(filename, "rb"); + if (infile == NULL) { + logStream << APPNAME << "Can not fopen file: " << filename << endl; + return ret; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp) NULL, + (png_error_ptr) NULL, + (png_error_ptr) NULL); + if (!png_ptr) { + goto file_close; + } + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp) NULL, + (png_infopp) NULL); + } + +#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 4 + if (setjmp(png_jmpbuf((png_ptr)))) + goto png_destroy; +#else + if (setjmp(png_ptr->jmpbuf)) + goto png_destroy; +#endif + + + png_init_io(png_ptr, infile); + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, + &interlace_type, (int *) NULL, (int *) NULL); + + /* Prevent against integer overflow */ + if(w >= MAX_DIMENSION || h >= MAX_DIMENSION) { + logStream << APPNAME << "Unreasonable dimension found in file: " + << filename << endl; + goto png_destroy; + } + + *width = (int) w; + *height = (int) h; + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + alpha[0] = (unsigned char *) malloc(*width * *height); + if (alpha[0] == NULL) { + logStream << APPNAME + << ": Can't allocate memory for alpha channel in PNG file." + << endl; + goto png_destroy; + } + } + + /* Change a paletted/grayscale image to RGB */ + if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) + { + png_set_expand(png_ptr); + } + + /* Change a grayscale image to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + { + png_set_gray_to_rgb(png_ptr); + } + + /* If the PNG file has 16 bits per channel, strip them down to 8 */ + if (bit_depth == 16) { + png_set_strip_16(png_ptr); + } + + /* use 1 byte per pixel */ + png_set_packing(png_ptr); + + row_pointers = (png_byte **) malloc(*height * sizeof(png_bytep)); + if (row_pointers == NULL) { + logStream << APPNAME << ": Can't allocate memory for PNG file." << endl; + goto png_destroy; + } + + for (i = 0; i < *height; i++) { + row_pointers[i] = (png_byte*) malloc(4 * *width); + if (row_pointers == NULL) { + logStream << APPNAME << ": Can't allocate memory for PNG file." + << endl; + goto rows_free; + } + } + + png_read_image(png_ptr, row_pointers); + + rgb[0] = (unsigned char *) malloc(3 * (*width) * (*height)); + if (rgb[0] == NULL) { + logStream << APPNAME << ": Can't allocate memory for PNG file." << endl; + goto rows_free; + } + + if (alpha[0] == NULL) { + ptr = rgb[0]; + for (i = 0; i < *height; i++) { + memcpy(ptr, row_pointers[i], 3 * (*width)); + ptr += 3 * (*width); + } + } else { + ptr = rgb[0]; + for (i = 0; i < *height; i++) { + unsigned int ipos = 0; + for (int j = 0; j < *width; j++) { + *ptr++ = row_pointers[i][ipos++]; + *ptr++ = row_pointers[i][ipos++]; + *ptr++ = row_pointers[i][ipos++]; + alpha[0][i * (*width) + j] = row_pointers[i][ipos++]; + } + } + } + + ret = 1; /* data reading is OK */ + +rows_free: + for (i = 0; i < *height; i++) { + if (row_pointers[i] != NULL ) { + free(row_pointers[i]); + } + } + + free(row_pointers); + +png_destroy: + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + +file_close: + fclose(infile); + return(ret); +} + diff --git a/slim/image.h b/slim/image.h new file mode 100644 index 0000000..0d414a3 --- /dev/null +++ b/slim/image.h @@ -0,0 +1,75 @@ +/* SLiM - Simple Login Manager + Copyright (C) 2004-06 Simone Rota + Copyright (C) 2004-06 Johannes Winkelmann + Copyright (C) 2012 Nobuhiro Iwamatsu + + 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 of the License, or + (at your option) any later version. + + The following code has been adapted and extended from + xplanet 1.0.1, Copyright (C) 2002-04 Hari Nair +*/ + +#ifndef _IMAGE_H_ +#define _IMAGE_H_ + +#include +#include + +class Image +{ +public: + Image(); + Image(const int w, const int h, const unsigned char *rgb, + const unsigned char *alpha); + + ~Image(); + + const unsigned char *getPNGAlpha() const { + return(png_alpha); + } + const unsigned char *getRGBData() const { + return(rgb_data); + } + + void getPixel(double px, double py, unsigned char *pixel); + void getPixel(double px, double py, unsigned char *pixel, + unsigned char *alpha); + + int Width() const { + return(width); + } + int Height() const { + return(height); + } + + bool Read(const char *filename); + + void Reduce(const int factor); + void Resize(const int w, const int h); + void Merge ( const Image *background, const int x, const int y ); + void Merge_non_crop(Image* background, const int x, const int y); + void Crop(const int x, const int y, const int w, const int h); + void Tile(const int w, const int h); + void Center(const int w, const int h, const char *hex); + void Plain(const int w, const int h, const char *hex); + + void computeShift(unsigned long mask, unsigned char &left_shift, + unsigned char &right_shift); + + Pixmap createPixmap(Display *dpy, int scr, Window win); + +private: + int width, height, area; + unsigned char *rgb_data; + unsigned char *png_alpha; + + int readJpeg(const char *filename, int *width, int *height, + unsigned char **rgb); + int readPng(const char *filename, int *width, int *height, + unsigned char **rgb, unsigned char **alpha); +}; + +#endif /* _IMAGE_H_ */ diff --git a/slim/jpeg.c b/slim/jpeg.c new file mode 100644 index 0000000..f239220 --- /dev/null +++ b/slim/jpeg.c @@ -0,0 +1,101 @@ +/**************************************************************************** + jpeg.c - read and write jpeg images using libjpeg routines + Copyright (C) 2002 Hari Nair + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +****************************************************************************/ + +#include +#include +#include + +#include +#include "const.h" + +int read_jpeg(const char *filename, int *width, int *height, unsigned char **rgb) +{ + int ret = 0; + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char *ptr = NULL; + unsigned int i, ipos; + + FILE *infile = fopen(filename, "rb"); + if (infile == NULL) { + fprintf(stderr, "Can not fopen file: %s\n",filename); + return ret; + } + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + jpeg_stdio_src(&cinfo, infile); + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + /* Prevent against integer overflow */ + if(cinfo.output_width >= MAX_DIMENSION || cinfo.output_height >= MAX_DIMENSION) { + fprintf(stderr, "Unreasonable dimension found in file: %s\n",filename); + goto close_file; + } + + *width = cinfo.output_width; + *height = cinfo.output_height; + + rgb[0] = malloc(3 * cinfo.output_width * cinfo.output_height); + if (rgb[0] == NULL) { + fprintf(stderr, "Can't allocate memory for JPEG file.\n"); + goto close_file; + } + + if (cinfo.output_components == 3) { + ptr = rgb[0]; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &ptr, 1); + ptr += 3 * cinfo.output_width; + } + } else if (cinfo.output_components == 1) { + ptr = malloc(cinfo.output_width); + if (ptr == NULL) { + fprintf(stderr, "Can't allocate memory for JPEG file.\n"); + goto rgb_free; + } + + ipos = 0; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, &ptr, 1); + + for (i = 0; i < cinfo.output_width; i++) { + memset(rgb[0] + ipos, ptr[i], 3); + ipos += 3; + } + } + + free(ptr); + } + + jpeg_finish_decompress(&cinfo); + + ret = 1; + goto close_file; + +rgb_free: + free(rgb[0]); + +close_file: + jpeg_destroy_decompress(&cinfo); + fclose(infile); + + return(ret); +} diff --git a/slim/log.cpp b/slim/log.cpp new file mode 100644 index 0000000..d9fd5d8 --- /dev/null +++ b/slim/log.cpp @@ -0,0 +1,57 @@ +/* SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2022 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include "const.h" +#include "log.h" +#include +#include + +LogUnit::LogUnit() +{ + logOut = &cerr; +} + +bool LogUnit::openLog(const char * filename) +{ + if (logFile.is_open()) { + cerr << APPNAME + << ": opening a new Log file, while another is already open" + << endl; + logFile.close(); + } + + // cerr is the default + if ( ( strcmp(filename, "/dev/stderr") == 0 ) + || ( strcmp(filename, "stderr") == 0 ) ) + { + logOut = &cerr; + return true; + } + + logFile.open(filename, ios_base::out|ios_base::app); + if ( logFile ) + { + logOut = &logFile; + return true; + } + return false; +} + +void LogUnit::closeLog() +{ + if (logFile.is_open()) + logFile.close(); +} + + +/* Now instantiate a singleton for all the rest of the code to use */ +LogUnit logStream; diff --git a/slim/log.h b/slim/log.h new file mode 100644 index 0000000..1cfb905 --- /dev/null +++ b/slim/log.h @@ -0,0 +1,47 @@ +#ifndef _LOG_H_ +#define _LOG_H_ + +#ifdef USE_CONSOLEKIT +#include "Ck.h" +#endif +#ifdef USE_PAM +#include "PAM.h" +#endif +#include + +using namespace std; + +class LogUnit +{ + ofstream logFile; + ostream * logOut; +public: + bool openLog(const char * filename); + void closeLog(); + + LogUnit(); + + ~LogUnit() { closeLog(); } + + template LogUnit & operator<<(const Type & text) + { + *logOut << text; logOut->flush(); + return *this; + } + + LogUnit & operator<<(ostream & (*fp)(ostream&)) + { + *logOut << fp; logOut->flush(); + return *this; + } + + LogUnit & operator<<(ios_base & (*fp)(ios_base&)) + { + *logOut << fp; logOut->flush(); + return *this; + } +}; + +extern LogUnit logStream; + +#endif /* _LOG_H_ */ diff --git a/slim/main.cpp b/slim/main.cpp new file mode 100644 index 0000000..909aa0f --- /dev/null +++ b/slim/main.cpp @@ -0,0 +1,24 @@ +/* + * SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * + * 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 of the License, or + * (at your option) any later version. +*/ + +#include "app.h" +#include "const.h" + +App* LoginApp = 0; + +int main(int argc, char** argv) +{ + LoginApp = new App(argc, argv); + LoginApp->Run(); + return 0; +} + diff --git a/slim/numlock.cpp b/slim/numlock.cpp new file mode 100644 index 0000000..ed92885 --- /dev/null +++ b/slim/numlock.cpp @@ -0,0 +1,112 @@ +/* + * SLiM - Simple Login Manager + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2012 Nobuhiro Iwamatsu + * + * 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 of the License, or + * (at your option) any later version. + * + * Code adapted from NumLockX, look at the end of this file for + * the original Copyright information. +*/ + +#include "numlock.h" +#include + +NumLock::NumLock() +{ +} + +int NumLock::xkb_init(Display* dpy) +{ + int xkb_opcode, xkb_event, xkb_error; + int xkb_lmaj = XkbMajorVersion; + int xkb_lmin = XkbMinorVersion; + + return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) + && XkbQueryExtension( dpy, &xkb_opcode, &xkb_event, &xkb_error, + &xkb_lmaj, &xkb_lmin ); +} + +unsigned int NumLock::xkb_mask_modifier( XkbDescPtr xkb, const char *name ) +{ + int i; + if( !xkb || !xkb->names ) + return 0; + + for( i = 0; i < XkbNumVirtualMods; i++ ) { + char* modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] ); + if( modStr != NULL && strcmp(name, modStr) == 0 ) { + unsigned int mask; + XkbVirtualModsToReal( xkb, 1 << i, &mask ); + return mask; + } + } + return 0; +} + +unsigned int NumLock::xkb_numlock_mask(Display* dpy) +{ + XkbDescPtr xkb; + + xkb = XkbGetKeyboard( dpy, XkbAllComponentsMask, XkbUseCoreKbd ); + if( xkb != NULL ) { + unsigned int mask = xkb_mask_modifier( xkb, "NumLock" ); + XkbFreeKeyboard( xkb, 0, True ); + return mask; + } + return 0; +} + +void NumLock::control_numlock(Display *dpy, bool flag) +{ + unsigned int mask; + + if( !xkb_init(dpy) ) + return; + + mask = xkb_numlock_mask(dpy); + if( mask == 0 ) + return; + + if( flag == true ) + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, mask); + else + XkbLockModifiers ( dpy, XkbUseCoreKbd, mask, 0); +} + +void NumLock::setOn(Display *dpy) +{ + control_numlock(dpy, true); +} + +void NumLock::setOff(Display *dpy) +{ + control_numlock(dpy, false); +} + +/* + * Copyright (C) 2000-2001 Lubos Lunak + * Copyright (C) 2001 Oswald Buddenhagen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. +*/ diff --git a/slim/numlock.h b/slim/numlock.h new file mode 100644 index 0000000..d7bb911 --- /dev/null +++ b/slim/numlock.h @@ -0,0 +1,33 @@ +/* SLiM - Simple Login Manager + Copyright (C) 2004-06 Simone Rota + Copyright (C) 2004-06 Johannes Winkelmann + Copyright (C) 2012 Nobuhiro Iwamatsu + + 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 of the License, or + (at your option) any later version. +*/ + +#ifndef _NUMLOCK_H_ +#define _NUMLOCK_H_ + +#include +#include +#include + +class NumLock +{ +public: + NumLock(); + static void setOn(Display *dpy); + static void setOff(Display *dpy); + +private: + static int xkb_init(Display *dpy); + static unsigned int xkb_mask_modifier(XkbDescPtr xkb, const char *name); + static unsigned int xkb_numlock_mask(Display *dpy); + static void control_numlock(Display *dpy, bool flag); +}; + +#endif /* _NUMLOCK_H_ */ diff --git a/slim/pam.sample b/slim/pam.sample new file mode 100755 index 0000000..78a981f --- /dev/null +++ b/slim/pam.sample @@ -0,0 +1,10 @@ +#%PAM-1.0 +auth requisite pam_nologin.so +auth required pam_env.so +auth required pam_unix.so +account required pam_unix.so +password required pam_unix.so +session required pam_limits.so +session required pam_unix.so +session optional pam_loginuid.so +session optional pam_ck_connector.so diff --git a/slim/panel.cpp b/slim/panel.cpp new file mode 100644 index 0000000..73f7aba --- /dev/null +++ b/slim/panel.cpp @@ -0,0 +1,1035 @@ +/* + * SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2022-23 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include // for XA_PIXMAP +#include // for sleep +#include "const.h" +#include "image.h" +#include "log.h" +#include "cfg.h" +#include "switchuser.h" +#include "panel.h" + +using namespace std; + +Panel::Panel(Display* dpy, int scr, Window root, Cfg* config, + const string& themedir, PanelType panel_mode) + : cfg(config), mode(panel_mode), Dpy(dpy), Scr(scr), Root(root), + session_name(""), session_exec("") +{ + if (mode == Mode_Lock) + { + Win = root; + viewport = GetPrimaryViewport(); + } + else if ( mode == Mode_Test ) + { + XWindowAttributes attributes; + XGetWindowAttributes(Dpy, Root, &attributes); + viewport.x = 0; // Not actually used. The window's position + viewport.y = 0; // is irrelevant to our drawing functions + viewport.width = attributes.width; + viewport.height = attributes.height; + } + else + { + /* The existing behaviour in DM mode was to always use the full + * screen size */ + viewport.x = 0; + viewport.y = 0; + // AFAICT the following two lines, used in GetPrimaryViewport, do + // exactly the same as the ones that follow, which were used elsewhere + //viewport.width = DisplayWidth(Dpy, Scr); + //viewport.height = DisplayHeight(Dpy, Scr); + viewport.width = XWidthOfScreen(ScreenOfDisplay(Dpy, Scr)); + viewport.height = XHeightOfScreen(ScreenOfDisplay(Dpy, Scr)); + } + + /* Init GC */ + XGCValues gcv; + unsigned long gcm; + gcm = GCForeground|GCBackground|GCGraphicsExposures; + gcv.foreground = GetColor("black"); + gcv.background = GetColor("white"); + gcv.graphics_exposures = False; + + TextGC = XCreateGC(Dpy, Root, gcm, &gcv); + + if (mode == Mode_Lock) { + gcm = GCGraphicsExposures; + gcv.graphics_exposures = False; + WinGC = XCreateGC(Dpy, Win, gcm, &gcv); + if (WinGC == 0) { + cerr << APPNAME << ": failed to create pixmap\n."; + exit(ERR_EXIT); + } + } + + // Intern _XROOTPMAP_ID property - does this belong here? + BackgroundPixmapId = XInternAtom(Dpy, "_XROOTPMAP_ID", False); + + font = XftFontOpenName(Dpy, Scr, cfg->getOption("input_font").c_str()); + welcomefont = XftFontOpenName(Dpy, Scr, cfg->getOption("welcome_font").c_str()); + enterfont = XftFontOpenName(Dpy, Scr, cfg->getOption("username_font").c_str()); + msgfont = XftFontOpenName(Dpy, Scr, cfg->getOption("msg_font").c_str()); + + Visual* visual = DefaultVisual(Dpy, Scr); + Colormap colormap = DefaultColormap(Dpy, Scr); + /* NOTE: using XftColorAllocValue() would be a better solution. Lazy me. */ + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_color").c_str(), &inputcolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("input_shadow_color").c_str(), &inputshadowcolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_color").c_str(), &welcomecolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("welcome_shadow_color").c_str(), &welcomeshadowcolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_color").c_str(), &entercolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("username_shadow_color").c_str(), &entershadowcolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_color").c_str(), &msgcolor); + XftColorAllocName(Dpy, visual, colormap, cfg->getOption("msg_shadow_color").c_str(), &msgshadowcolor); + XftColorAllocName(Dpy, visual, colormap, + cfg->getOption("session_color").c_str(), &sessioncolor); + XftColorAllocName(Dpy, visual, colormap, + cfg->getOption("session_shadow_color").c_str(), &sessionshadowcolor); + + /* Load properties from config / theme */ + input_name_x = cfg->getIntOption("input_name_x"); + input_name_y = cfg->getIntOption("input_name_y"); + input_pass_x = cfg->getIntOption("input_pass_x"); + input_pass_y = cfg->getIntOption("input_pass_y"); + inputShadowXOffset = cfg->getIntOption("input_shadow_xoffset"); + inputShadowYOffset = cfg->getIntOption("input_shadow_yoffset"); + + if (input_pass_x < 0 || input_pass_y < 0){ /* single inputbox mode */ + input_pass_x = input_name_x; + input_pass_y = input_name_y; + } + + /* Load panel and background image */ + string panelpng = ""; + panelpng = panelpng + themedir +"/panel.png"; + image = new Image; + bool loaded = image->Read(panelpng.c_str()); + if (!loaded) { /* try jpeg if png failed */ + panelpng = themedir + "/panel.jpg"; + loaded = image->Read(panelpng.c_str()); + if (!loaded) { + logStream << APPNAME + << ": could not load panel image for theme '" + << basename((char*)themedir.c_str()) << "'" + << endl; + exit(ERR_EXIT); + } + } + + bgImg = new Image(); + string bgstyle = cfg->getOption("background_style"); + if (bgstyle != "color") { + panelpng = themedir +"/background.png"; + loaded = bgImg->Read(panelpng.c_str()); + if (!loaded) { /* try jpeg if png failed */ + panelpng = themedir + "/background.jpg"; + loaded = bgImg->Read(panelpng.c_str()); + if (!loaded){ + logStream << APPNAME + << ": could not load background image for theme '" + << basename((char*)themedir.c_str()) << "'" + << endl; + exit(ERR_EXIT); + } + } + } + + if (bgstyle == "stretch") + bgImg->Resize(viewport.width, viewport.height); + else if (bgstyle == "tile") + bgImg->Tile(viewport.width, viewport.height); + else if (bgstyle == "center") { + string hexvalue = cfg->getOption("background_color"); + hexvalue = hexvalue.substr(1,6); + bgImg->Center(viewport.width, + viewport.height, + hexvalue.c_str()); + } else { // plain color or error + string hexvalue = cfg->getOption("background_color"); + hexvalue = hexvalue.substr(1,6); + bgImg->Center(viewport.width, + viewport.height, + hexvalue.c_str()); + } + + string cfgX = cfg->getOption("input_panel_x"); + string cfgY = cfg->getOption("input_panel_y"); + + X = Cfg::absolutepos(cfgX, viewport.width, image->Width()); + Y = Cfg::absolutepos(cfgY, viewport.height, image->Height()); + + if (mode == Mode_Lock) { + // In slimlock, the window we draw on is always the root, not the + // XSimpleWindow created by OpenPanel to represent the panel area. + // Thus we apply a hack to offset the input positions. It's a shame + // we don't do the job properly, as this hack isn't applied to any + // of the static text + input_name_x += X; + input_name_y += Y; + input_pass_x += X; + input_pass_y += Y; + } + + if (mode == Mode_Lock) { + /* Merge image into background without crop, so that PanelPixmap is + * the whole screen (background image with panel drawn on it) */ + image->Merge_non_crop(bgImg, X, Y); + } else { + /* Merge image with cropped background, so that PanelPixmap is the + * panel with the relevant part of the X root image included instead + * of the alpha channel */ + image->Merge(bgImg, X, Y); + } + PanelPixmap = image->createPixmap(Dpy, Scr, Root); + + /* Read (and substitute vars in) the welcome message */ + welcome_message = cfg->getWelcomeMessage(); + + if (mode == Mode_Lock) { + SetName(getenv("USER")); + field = Get_Passwd; + OnExpose(); + } + MsgExtents.width = 0; +} + +Panel::~Panel() +{ + Visual* visual = DefaultVisual(Dpy, Scr); + Colormap colormap = DefaultColormap(Dpy, Scr); + + XftColorFree(Dpy, visual, colormap, &inputcolor); + XftColorFree(Dpy, visual, colormap, &inputshadowcolor); + XftColorFree(Dpy, visual, colormap, &welcomecolor); + XftColorFree(Dpy, visual, colormap, &welcomeshadowcolor); + XftColorFree(Dpy, visual, colormap, &entercolor); + XftColorFree(Dpy, visual, colormap, &entershadowcolor); + XftColorFree(Dpy, visual, colormap, &msgcolor); + XftColorFree(Dpy, visual, colormap, &msgshadowcolor); + XftColorFree(Dpy, visual, colormap, &sessioncolor); + XftColorFree(Dpy, visual, colormap, &sessionshadowcolor); + + XFreeGC(Dpy, TextGC); + XftFontClose(Dpy, font); + XftFontClose(Dpy, msgfont); + XftFontClose(Dpy, welcomefont); + XftFontClose(Dpy, enterfont); + + if (mode == Mode_Lock) + XFreeGC(Dpy, WinGC); + + delete bgImg; + delete image; +} + + +/** + * Set the (previously loaded and adjusted) background image as the window + * background for the root window. + */ +void Panel::setBackground(void) +{ + Pixmap p = bgImg->createPixmap(Dpy, Scr, Root); + XSetWindowBackgroundPixmap(Dpy, Root, p); + XChangeProperty(Dpy, Root, BackgroundPixmapId, XA_PIXMAP, 32, + PropModeReplace, (unsigned char *)&p, 1); + + XClearWindow(Dpy, Root); + + XFlush(Dpy); +} + + +/* Hide the cursor */ +void Panel::HideCursor() +{ + if (cfg->getOption("hidecursor") == "true") + { + XColor black; + char cursordata[1]; + Pixmap cursorpixmap; + Cursor cursor; + cursordata[0]=0; + cursorpixmap = XCreateBitmapFromData(Dpy, Root, cursordata, 1, 1); + black.red=0; + black.green=0; + black.blue=0; + cursor = XCreatePixmapCursor(Dpy, cursorpixmap, cursorpixmap, &black, &black, 0, 0); + //XFreePixmap(dpy, cursorpixmap); // man page is confusing as to whether this is right + XDefineCursor(Dpy, Root, cursor); + } +} + + +/* Open the login panel. Not used by slimlock */ +void Panel::OpenPanel() +{ + /* Create window */ + Win = XCreateSimpleWindow(Dpy, Root, X, Y, + image->Width(), + image->Height(), + 0, GetColor("white"), GetColor("white")); + + /* Events */ + XSelectInput(Dpy, Win, ExposureMask | KeyPressMask); + + /* Set background */ + XSetWindowBackgroundPixmap(Dpy, Win, PanelPixmap); + + /* Show window */ + XMapWindow(Dpy, Win); + XMoveWindow(Dpy, Win, X, Y); /* override wm positioning (for tests) */ + + /* Grab keyboard */ + XGrabKeyboard(Dpy, Win, False, GrabModeAsync, GrabModeAsync, CurrentTime); + + XFlush(Dpy); +} + +void Panel::ClosePanel() +{ + XUngrabKeyboard(Dpy, CurrentTime); + XUnmapWindow(Dpy, Win); + XDestroyWindow(Dpy, Win); + XFlush(Dpy); +} + +void Panel::WrongPassword(int timeout) +{ + string message; + + if ( mode != Mode_Lock ) + { + XClearWindow(Dpy, Root); + } + +#if 0 + if (CapsLockOn) + message = cfg->getOption("passwd_feedback_capslock"); + else +#endif + message = cfg->getOption("passwd_feedback_msg"); + + XftDraw *draw = XftDrawCreate ( Dpy, Root, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr) ); + XftTextExtents8(Dpy, msgfont, reinterpret_cast(message.c_str()), + message.length(), &MsgExtents); + + string cfgX = cfg->getOption("passwd_feedback_x"); + string cfgY = cfg->getOption("passwd_feedback_y"); + int shadowXOffset = cfg->getIntOption("msg_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("msg_shadow_yoffset"); + int msg_x = Cfg::absolutepos(cfgX, viewport.width, MsgExtents.width); + int msg_y = Cfg::absolutepos(cfgY, viewport.height, MsgExtents.height); + + MsgExtents.x = msg_x; + MsgExtents.y = msg_y - MsgExtents.height; + + if ( timeout > 0 ) + { + OnExpose(); + if ( msg_x >= 0 && msg_y >= 0 ) + SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message, + &msgshadowcolor, shadowXOffset, shadowYOffset); + + if (cfg->getOption("bell") == "1") + XBell(Dpy, 100); + XFlush(Dpy); + sleep(timeout); + } + ResetPasswd(); + if ( mode != Mode_Lock ) + { + if ( cfg->getIntOption("keep_user_on_fail") == 0 ) + { + ResetName(); + } + field = Get_Name; + } + OnExpose(); + // The message should stay on the screen even after the password field is + // cleared, methinks. I don't like this solution, but it works. + if ( msg_x >= 0 && msg_y >= 0 ) + SlimDrawString8(draw, &msgcolor, msgfont, msg_x, msg_y, message, + &msgshadowcolor, shadowXOffset, shadowYOffset); + XSync(Dpy, True); + XftDrawDestroy(draw); +} + +void Panel::Message(const string& text) +{ + string cfgX, cfgY; + XGlyphInfo extents; + XftDraw *draw; + + // The message positions are screen-relative, not panel-relative + draw = XftDrawCreate(Dpy, Root, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + + XftTextExtents8(Dpy, msgfont, + reinterpret_cast(text.c_str()), + text.length(), &extents); + cfgX = cfg->getOption("msg_x"); + cfgY = cfg->getOption("msg_y"); + int shadowXOffset = cfg->getIntOption("msg_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("msg_shadow_yoffset"); + int msg_x, msg_y; + + msg_x = Cfg::absolutepos(cfgX, viewport.width, extents.width); + msg_y = Cfg::absolutepos(cfgY, viewport.height, extents.height); + + SlimDrawString8 (draw, &msgcolor, msgfont, msg_x, msg_y, + text, + &msgshadowcolor, + shadowXOffset, shadowYOffset); + XFlush(Dpy); + XftDrawDestroy(draw); +} + +unsigned long Panel::GetColor(const char* colorname) +{ + XColor color; + XWindowAttributes attributes; + + XGetWindowAttributes(Dpy, Root, &attributes); + + color.pixel = 0; + + if(!XParseColor(Dpy, attributes.colormap, colorname, &color)) + logStream << APPNAME << ": can't parse color " << colorname << endl; + else if(!XAllocColor(Dpy, attributes.colormap, &color)) + logStream << APPNAME << ": can't allocate color " << colorname << endl; + + return color.pixel; +} + +void Panel::TextCursor(int visible) +{ + const char* text = NULL; + int xx = 0, yy = 0, y2 = 0, cheight = 0; + const char* txth = "Wj"; /* used to get cursor height */ + + // The constructor and other conditionals guarantee that + // if (mode == Mode_Lock) field = Get_Passwd; + + switch(field) { + case Get_Passwd: + text = HiddenPasswdBuffer.c_str(); + xx = input_pass_x; + yy = input_pass_y; + break; + + case Get_Name: + text = NameBuffer.c_str(); + xx = input_name_x; + yy = input_name_y; + break; + } + + XGlyphInfo extents; + XftTextExtents8(Dpy, font, (XftChar8*)txth, strlen(txth), &extents); + cheight = extents.height; + y2 = yy - extents.y + extents.height; + XftTextExtents8(Dpy, font, (XftChar8*)text, strlen(text), &extents); + xx += extents.width; + + if(visible == SHOW) { + if (mode == Mode_Lock) { + xx += viewport.x; + yy += viewport.y; + y2 += viewport.y; + } + XSetForeground(Dpy, TextGC, + GetColor(cfg->getOption("input_color").c_str())); + + XDrawLine(Dpy, Win, TextGC, + xx+1, yy-cheight, + xx+1, y2); + } else { + if (mode == Mode_Lock) + ApplyBackground(Rectangle(xx+1, yy-cheight, + 1, y2-(yy-cheight)+1)); + else + XClearArea(Dpy, Win, xx+1, yy-cheight, + 1, y2-(yy-cheight)+1, false); + } +} + +void Panel::EventHandler(const Panel::FieldType& curfield) +{ + XEvent event; + field = curfield; + bool loop = true; + + if ( (mode != Mode_Lock) && ( MsgExtents.width == 0 ) ) + OnExpose(); + + struct pollfd x11_pfd = {0}; + x11_pfd.fd = ConnectionNumber(Dpy); + x11_pfd.events = POLLIN; + + while (loop) + { + if (XPending(Dpy) || poll(&x11_pfd, 1, -1) > 0) + { + while(XPending(Dpy)) + { + XNextEvent(Dpy, &event); + switch(event.type) + { + case Expose: + OnExpose(); + break; + + case KeyPress: + loop=OnKeyPress(event); + break; + } + } + if ( MsgExtents.width > 0 ) + { + if (mode == Mode_Lock) + ApplyBackground(Rectangle(MsgExtents.x, + MsgExtents.y, MsgExtents.width+1, + MsgExtents.height+2)); + else + XClearArea(Dpy, Root, MsgExtents.x, MsgExtents.y, + MsgExtents.width+1, MsgExtents.height+2, false); + MsgExtents.width = 0; + } + } + } + + return; +} + +void Panel::OnExpose(void) +{ + XftDraw *draw = XftDrawCreate(Dpy, Win, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + + if (mode == Mode_Lock) + ApplyBackground(); + else + XClearWindow(Dpy, Win); + + if (input_pass_x != input_name_x || input_pass_y != input_name_y){ + SlimDrawString8 (draw, &inputcolor, font, input_name_x, input_name_y, + NameBuffer, + &inputshadowcolor, + inputShadowXOffset, inputShadowYOffset); + SlimDrawString8 (draw, &inputcolor, font, input_pass_x, input_pass_y, + HiddenPasswdBuffer, + &inputshadowcolor, + inputShadowXOffset, inputShadowYOffset); + } else { /*single input mode */ + switch(field) { + case Get_Passwd: + SlimDrawString8 (draw, &inputcolor, font, + input_pass_x, input_pass_y, + HiddenPasswdBuffer, + &inputshadowcolor, + inputShadowXOffset, inputShadowYOffset); + break; + case Get_Name: + SlimDrawString8 (draw, &inputcolor, font, + input_name_x, input_name_y, + NameBuffer, + &inputshadowcolor, + inputShadowXOffset, inputShadowYOffset); + break; + } + } + + XftDrawDestroy (draw); + TextCursor(SHOW); + ShowText(); +} + +void Panel::EraseLastChar(std::string &formerString) +{ + switch(field) { + case Get_Name: + if (! NameBuffer.empty()) { + formerString=NameBuffer; + NameBuffer.erase(--NameBuffer.end()); + } + break; + + case Get_Passwd: + if (!PasswdBuffer.empty()) { + formerString=HiddenPasswdBuffer; + PasswdBuffer.erase(--PasswdBuffer.end()); + HiddenPasswdBuffer.erase(--HiddenPasswdBuffer.end()); + } + break; + } +} + +bool Panel::OnKeyPress(XEvent& event) +{ + char ascii; + KeySym keysym; + XComposeStatus compstatus; + int xx = 0; + int yy = 0; + string text; + string formerString = ""; + + XLookupString(&event.xkey, &ascii, 1, &keysym, &compstatus); + switch(keysym){ + case XK_F1: + if ( mode != Mode_Lock ) // Can't change session in a screen lock + SwitchSession(); + return true; + + case XK_F11: + /* Take a screenshot */ + if ( system(cfg->getOption("screenshot_cmd").c_str()) < 0 ) + logStream << APPNAME << ": screenshot_cmd failed" << endl; + return true; + + case XK_Return: + case XK_KP_Enter: + if (field==Get_Name) + { + /* Don't allow an empty username */ + if (NameBuffer.empty()) + return true; + + if (NameBuffer==CONSOLE_STR) + action = Console; + else if (NameBuffer==HALT_STR) + action = Halt; + else if (NameBuffer==REBOOT_STR) + action = Reboot; + else if (NameBuffer==SUSPEND_STR) + action = Suspend; + else if (NameBuffer==EXIT_STR) + action = Exit; + else + action = Login; + } + return false; + + default: + break; + } + + TextCursor(HIDE); + switch(keysym){ + case XK_Delete: + case XK_BackSpace: + EraseLastChar(formerString); + break; + + case XK_w: + case XK_u: + if (reinterpret_cast(event).state & ControlMask) + { + switch(field) { + case Get_Passwd: + formerString = HiddenPasswdBuffer; + HiddenPasswdBuffer.clear(); + PasswdBuffer.clear(); + break; + case Get_Name: + formerString = NameBuffer; + NameBuffer.clear(); + break; + } + break; + } + /* Deliberate fall-through */ + case XK_h: + if (reinterpret_cast(event).state & ControlMask) + { + EraseLastChar(formerString); + break; + } + /* Deliberate fall-through */ + default: + if (isprint(ascii) && (keysym < XK_Shift_L || keysym > XK_Hyper_R)) + { + switch(field) { + case Get_Name: + formerString=NameBuffer; + if (NameBuffer.length() < INPUT_MAXLENGTH_NAME-1) + { + NameBuffer.append(&ascii,1); + } + break; + case Get_Passwd: + formerString=HiddenPasswdBuffer; + if (PasswdBuffer.length() < INPUT_MAXLENGTH_PASSWD-1) + { + PasswdBuffer.append(&ascii,1); + HiddenPasswdBuffer.append("*"); + } + break; + } + } + else + { // *RP* I think this is to fix the fake bolding if the user presses TAB + return true; //nodraw if notchange + } + break; + } + + XGlyphInfo extents; + XftDraw *draw = XftDrawCreate(Dpy, Win, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + + switch(field) { + case Get_Name: + text = NameBuffer; + xx = input_name_x; + yy = input_name_y; + break; + + case Get_Passwd: + text = HiddenPasswdBuffer; + xx = input_pass_x; + yy = input_pass_y; + break; + } + + if (!formerString.empty()) + { + const char* txth = "Wj"; /* get proper maximum height ? */ + XftTextExtents8(Dpy, font, + reinterpret_cast(txth), strlen(txth), &extents); + int maxHeight = extents.height; + + XftTextExtents8(Dpy, font, + reinterpret_cast(formerString.c_str()), + formerString.length(), &extents); + int maxLength = extents.width; + + if (mode == Mode_Lock) + ApplyBackground(Rectangle(input_pass_x - 3, + input_pass_y - maxHeight - 3, + maxLength + 6, maxHeight + 6)); + else + XClearArea(Dpy, Win, xx - 3, yy-maxHeight - 3, + maxLength + 6, maxHeight + 6, false); + } + + if (!text.empty()) + { + SlimDrawString8 (draw, &inputcolor, font, xx, yy, + text, + &inputshadowcolor, + inputShadowXOffset, inputShadowYOffset); + } + + XftDrawDestroy (draw); + TextCursor(SHOW); + return true; +} + + +/* Draw welcome and "enter username" messages */ +void Panel::ShowText() +{ + string cfgX, cfgY; + XGlyphInfo extents; + + bool singleInputMode = ( input_name_x == input_pass_x + && input_name_y == input_pass_y ); + + /// @bug this draw context is assumed relative to the panel but in lock + /// mode it's actually relative to the background + XftDraw *draw = XftDrawCreate(Dpy, Win, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + /* welcome message */ + XftTextExtents8(Dpy, welcomefont, (XftChar8*)welcome_message.c_str(), + strlen(welcome_message.c_str()), &extents); + cfgX = cfg->getOption("welcome_x"); + cfgY = cfg->getOption("welcome_y"); + int shadowXOffset = cfg->getIntOption("welcome_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("welcome_shadow_yoffset"); + + welcome_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); + welcome_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); + if (welcome_x >= 0 && welcome_y >= 0) { + SlimDrawString8 (draw, &welcomecolor, welcomefont, + welcome_x, welcome_y, + welcome_message, + &welcomeshadowcolor, shadowXOffset, shadowYOffset); + } + + /* Enter username-password message */ + string msg; + if (!singleInputMode|| field == Get_Passwd) { + msg = cfg->getOption("password_msg"); + XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(), + strlen(msg.c_str()), &extents); + cfgX = cfg->getOption("password_x"); + cfgY = cfg->getOption("password_y"); + int shadowXOffset = cfg->getIntOption("username_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("username_shadow_yoffset"); + password_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); + password_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); + if (password_x >= 0 && password_y >= 0){ + SlimDrawString8 (draw, &entercolor, enterfont, password_x, password_y, + msg, &entershadowcolor, shadowXOffset, shadowYOffset); + } + } + + if ((!singleInputMode|| field == Get_Name) && mode != Mode_Lock ) { + msg = cfg->getOption("username_msg"); + XftTextExtents8(Dpy, enterfont, (XftChar8*)msg.c_str(), + strlen(msg.c_str()), &extents); + cfgX = cfg->getOption("username_x"); + cfgY = cfg->getOption("username_y"); + int shadowXOffset = cfg->getIntOption("username_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("username_shadow_yoffset"); + username_x = Cfg::absolutepos(cfgX, image->Width(), extents.width); + username_y = Cfg::absolutepos(cfgY, image->Height(), extents.height); + if (username_x >= 0 && username_y >= 0){ + SlimDrawString8 (draw, &entercolor, enterfont, username_x, username_y, + msg, &entershadowcolor, shadowXOffset, shadowYOffset); + } + } + XftDrawDestroy(draw); + + if (mode == Mode_Lock) { + // If only the password box is visible, draw the user name somewhere too + string user_msg = "User: " + GetName(); + int show_username = cfg->getIntOption("show_username"); + if (singleInputMode && show_username) { + Message(user_msg); + } + } +} + + +string Panel::getSession() +{ + return session_exec; +} + + +/* choose next available session type. Not used in lock mode */ +void Panel::SwitchSession() +{ + pair ses = cfg->nextSession(); + session_name = ses.first; + session_exec = ses.second; + if (session_name.size() > 0) { + ShowSession(); + } + } + + +/* Display session type on the screen. Not used in lock mode */ +void Panel::ShowSession() +{ + string msg_x, msg_y; + XClearWindow(Dpy, Root); + string currsession = cfg->getOption("session_msg") + " " + session_name; + XGlyphInfo extents; + + sessionfont = XftFontOpenName(Dpy, Scr, cfg->getOption("session_font").c_str()); + + XftDraw *draw = XftDrawCreate(Dpy, Root, + DefaultVisual(Dpy, Scr), DefaultColormap(Dpy, Scr)); + XftTextExtents8(Dpy, sessionfont, reinterpret_cast(currsession.c_str()), + currsession.length(), &extents); + msg_x = cfg->getOption("session_x"); + msg_y = cfg->getOption("session_y"); + int x = Cfg::absolutepos(msg_x, viewport.width, extents.width); + int y = Cfg::absolutepos(msg_y, viewport.height, extents.height); + int shadowXOffset = cfg->getIntOption("session_shadow_xoffset"); + int shadowYOffset = cfg->getIntOption("session_shadow_yoffset"); + + SlimDrawString8(draw, &sessioncolor, sessionfont, x, y, + currsession, + &sessionshadowcolor, + shadowXOffset, shadowYOffset); + XFlush(Dpy); + XftDrawDestroy(draw); +} + + +void Panel::SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font, + int x, int y, const string& str, + XftColor* shadowColor, + int xOffset, int yOffset) +{ + if (mode == Mode_Lock) { + x += viewport.x; + y += viewport.y; + } + + if (xOffset && yOffset) { + XftDrawStringUtf8(d, shadowColor, font, x+xOffset, y+yOffset, + reinterpret_cast(str.c_str()), str.length()); + } + + XftDrawStringUtf8(d, color, font, x, y, + reinterpret_cast(str.c_str()), str.length()); +} + +Panel::ActionType Panel::getAction(void) const +{ + return action; +} + +void Panel::Reset(void) +{ + ResetName(); + ResetPasswd(); +} + +void Panel::ResetName(void) +{ + NameBuffer.clear(); +} + +void Panel::ResetPasswd(void) +{ + PasswdBuffer.clear(); + HiddenPasswdBuffer.clear(); +} + + +/* Pre-load the user name input box with the provided string. + * Used to set the default user, if so configured, and by slimlock to set the + * currently logged-in user. + */ +void Panel::SetName(const string& name) +{ + NameBuffer=name; + action = Login; +} + +const string& Panel::GetName(void) const +{ + return NameBuffer; +} + +const string& Panel::GetPasswd(void) const +{ + return PasswdBuffer; +} + + +/** + * Identify the viewport (physical screen?) to draw on. This allows slimlock + * to handle Xinerama-type multi-monitor setups. Not currently used by slim + */ +Rectangle Panel::GetPrimaryViewport() +{ + Rectangle fallback; + Rectangle result; + + RROutput primary; + XRROutputInfo *primary_info; + XRRScreenResources *resources; + XRRCrtcInfo *crtc_info; + + int crtc; + + fallback.x = 0; + fallback.y = 0; + fallback.width = DisplayWidth(Dpy, Scr); + fallback.height = DisplayHeight(Dpy, Scr); + + resources = XRRGetScreenResources(Dpy, Win); + if (!resources) + { + cerr << "XRRGetScreenResources failed\n"; + return fallback; + } + + primary = XRRGetOutputPrimary(Dpy, Win); + if (!primary) { + // No "primary" defined (by the WM, usually) but could still have + // multiple monitors or setups, so default to the first output. + primary = resources->outputs[0]; + } + + primary_info = XRRGetOutputInfo(Dpy, resources, primary); + if (!primary_info) { + cerr << "XRRGetOutputInfo failed\n"; + XRRFreeScreenResources(resources); + return fallback; + } + + // Fixes bug with multiple monitors. Just pick first monitor if + // XRRGetOutputInfo gives returns bad value for crtc. + if (primary_info->crtc < 1) { + if (primary_info->ncrtc > 0) { + crtc = primary_info->crtcs[0]; + } else { + cerr << "Cannot get crtc from xrandr.\n"; + exit(EXIT_FAILURE); + } + } else { + crtc = primary_info->crtc; + } + + crtc_info = XRRGetCrtcInfo(Dpy, resources, crtc); + + if (!crtc_info) { + cerr << "XRRGetCrtcInfo failed\n"; + XRRFreeOutputInfo(primary_info); + XRRFreeScreenResources(resources); + return fallback; + } + + result.x = crtc_info->x; + result.y = crtc_info->y; + result.width = crtc_info->width; + result.height = crtc_info->height; + + XRRFreeCrtcInfo(crtc_info); + XRRFreeOutputInfo(primary_info); + XRRFreeScreenResources(resources); + + return result; +} + + +/** + * Re-draw the background over a rectangle. This method is only used in "lock" + * mode - the DM mode uses XClearArea instead. + */ +void Panel::ApplyBackground(Rectangle rect) +{ + int ret = 0; + + if (rect.is_empty()) { + rect.x = 0; + rect.y = 0; + rect.width = viewport.width; + rect.height = viewport.height; + } + + ret = XCopyArea(Dpy, PanelPixmap, Win, WinGC, + rect.x, rect.y, rect.width, rect.height, + viewport.x + rect.x, viewport.y + rect.y); + + if (!ret) + cerr << APPNAME << ": failed to put pixmap on the screen\n."; +} + diff --git a/slim/panel.h b/slim/panel.h new file mode 100644 index 0000000..8a7afbf --- /dev/null +++ b/slim/panel.h @@ -0,0 +1,188 @@ +/* SLiM - Simple Login Manager + Copyright (C) 1997, 1998 Per Liden + Copyright (C) 2004-06 Simone Rota + Copyright (C) 2004-06 Johannes Winkelmann + Copyright (C) 2013 Nobuhiro Iwamatsu + Copyright (C) 2022-23 Rob Pearce + + 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 of the License, or + (at your option) any later version. +*/ + +#ifndef _PANEL_H_ +#define _PANEL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NEEDS_BASENAME +#include +#endif + + +// Forward declarations +class Image; +class Cfg; + + +struct Rectangle { + int x; + int y; + unsigned int width; + unsigned int height; + + Rectangle() : x(0), y(0), width(0), height(0) {}; + Rectangle(int x, int y, unsigned int width, unsigned int height) + : x(x), y(y), width(width), height(height) + {} + + bool is_empty() const { return width == 0 || height == 0; } +}; + +class Panel +{ +public: + enum ActionType { + Login, + UnLock = Login, // slimlock doesn't actually care about this + Console, + Reboot, + Halt, + Exit, + Suspend + }; + + enum FieldType { + Get_Name, + Get_Passwd + }; + + enum PanelType { + Mode_DM, + Mode_Test, + Mode_Lock + }; + + Panel(Display *dpy, int scr, Window root, Cfg *config, + const std::string& themed, PanelType panel_mode); + ~Panel(); + void OpenPanel(); + void ClosePanel(); + void WrongPassword(int timeout); + void Message(const std::string &text); + void EventHandler(const FieldType &curfield); + std::string getSession(); + ActionType getAction(void) const; + + void Reset(void); + void ResetName(void); + void ResetPasswd(void); + void SetName(const std::string &name); + const std::string& GetName(void) const; + const std::string& GetPasswd(void) const; + void SwitchSession(); + + Atom BackgroundPixmapId; // from XInternAtom -- does it need to be a member var? + void setBackground(void); + void HideCursor(); + +private: + Panel(); + void TextCursor(int visible); + unsigned long GetColor(const char *colorname); + void OnExpose(void); + void EraseLastChar(std::string &formerString); + bool OnKeyPress(XEvent& event); + void ShowText(); + void ShowSession(); + + void SlimDrawString8(XftDraw *d, XftColor *color, XftFont *font, + int x, int y, const std::string &str, + XftColor *shadowColor, + int xOffset, int yOffset); + + Rectangle GetPrimaryViewport(); + void ApplyBackground(Rectangle = Rectangle()); + + /* Private data */ + PanelType mode; /* work mode */ + Cfg *cfg; + Display *Dpy; + int Scr; + Window Win; + Window Root; + int X, Y; + GC TextGC; + GC WinGC; + XftFont *font; + XftColor inputshadowcolor; + XftColor inputcolor; + XftColor msgcolor; + XftColor msgshadowcolor; + XftFont *msgfont; + XftFont *welcomefont; + XftColor welcomecolor; + XftFont *sessionfont; + XftColor sessioncolor; + XftColor sessionshadowcolor; + XftColor welcomeshadowcolor; + XftFont *enterfont; + XftColor entercolor; + XftColor entershadowcolor; + ActionType action; + FieldType field; + XGlyphInfo MsgExtents; + + /* Username/Password */ + std::string NameBuffer; + std::string PasswdBuffer; + std::string HiddenPasswdBuffer; + + /* screen stuff */ + Rectangle viewport; + + /* Configuration */ + int input_name_x; + int input_name_y; + int input_pass_x; + int input_pass_y; + int inputShadowXOffset; + int inputShadowYOffset; + int welcome_x; + int welcome_y; + int welcome_shadow_xoffset; + int welcome_shadow_yoffset; + int session_shadow_xoffset; + int session_shadow_yoffset; + int username_x; + int username_y; + int username_shadow_xoffset; + int username_shadow_yoffset; + int password_x; + int password_y; + std::string welcome_message; + + /* Pixmap data */ + Pixmap PanelPixmap; + + Image *image; + Image *bgImg; + + std::string themedir; + + /* Session handling */ + std::string session_name; + std::string session_exec; +}; + +#endif /* _PANEL_H_ */ diff --git a/slim/png.c b/slim/png.c new file mode 100644 index 0000000..601e435 --- /dev/null +++ b/slim/png.c @@ -0,0 +1,173 @@ +/**************************************************************************** + png.c - read and write png images using libpng routines. + Distributed with Xplanet. + Copyright (C) 2002 Hari Nair + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include "const.h" + +int read_png(const char *filename, int *width, int *height, unsigned char **rgb, + unsigned char **alpha) +{ + int ret = 0; + + png_structp png_ptr; + png_infop info_ptr; + png_bytepp row_pointers; + + unsigned char *ptr = NULL; + png_uint_32 w, h; + int bit_depth, color_type, interlace_type; + int i; + + FILE *infile = fopen(filename, "rb"); + if (infile == NULL) { + fprintf(stderr, "Can not fopen file: %s\n", filename); + return ret; + } + + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (png_voidp)NULL, + (png_error_ptr)NULL, + (png_error_ptr)NULL); + if (!png_ptr) + goto file_close; + + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, + (png_infopp)NULL, + (png_infopp)NULL); + } + +#if PNG_LIBPNG_VER_MAJOR >= 1 && PNG_LIBPNG_VER_MINOR >= 4 + if (setjmp(png_jmpbuf((png_ptr)))) +#else + if (setjmp(png_ptr->jmpbuf)) +#endif + goto png_destroy; + + png_init_io(png_ptr, infile); + png_read_info(png_ptr, info_ptr); + + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, + &interlace_type, (int *) NULL, (int *) NULL); + + /* Prevent against integer overflow */ + if (w >= MAX_DIMENSION || h >= MAX_DIMENSION) { + fprintf(stderr, + "Unreasonable dimension found in file: %s\n", filename); + goto png_destroy; + } + + *width = (int) w; + *height = (int) h; + + if (color_type == PNG_COLOR_TYPE_RGB_ALPHA + || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { + alpha[0] = malloc(*width * *height); + if (alpha[0] == NULL) { + fprintf(stderr, + "Can't allocate memory for alpha channel in PNG file.\n"); + goto png_destroy; + } + } + + /* Change a paletted/grayscale image to RGB */ + if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) + png_set_expand(png_ptr); + + /* Change a grayscale image to RGB */ + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + /* If the PNG file has 16 bits per channel, strip them down to 8 */ + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + /* use 1 byte per pixel */ + png_set_packing(png_ptr); + + row_pointers = malloc(*height * sizeof(png_bytep)); + if (row_pointers == NULL) { + fprintf(stderr, "Can't allocate memory for PNG file.\n"); + goto png_destroy; + } + + for (i = 0; i < *height; i++) { + row_pointers[i] = malloc(4 * *width); + if (row_pointers == NULL) { + fprintf(stderr, + "Can't allocate memory for PNG line.\n"); + goto rows_free; + } + } + + png_read_image(png_ptr, row_pointers); + + rgb[0] = malloc(3 * *width * *height); + if (rgb[0] == NULL) { + fprintf(stderr, "Can't allocate memory for PNG file.\n"); + goto rows_free; + } + + if (alpha[0] == NULL) { + ptr = rgb[0]; + for (i = 0; i < *height; i++) { + memcpy(ptr, row_pointers[i], 3 * *width); + ptr += 3 * *width; + } + } else { + int j; + ptr = rgb[0]; + for (i = 0; i < *height; i++) { + int ipos = 0; + for (j = 0; j < *width; j++) { + *ptr++ = row_pointers[i][ipos++]; + *ptr++ = row_pointers[i][ipos++]; + *ptr++ = row_pointers[i][ipos++]; + alpha[0][i * *width + j] + = row_pointers[i][ipos++]; + } + } + } + + ret = 1; /* data reading is OK */ + +rows_free: + for (i = 0; i < *height; i++) { + if (row_pointers[i] != NULL) + free(row_pointers[i]); + } + + free(row_pointers); + +png_destroy: + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); + +file_close: + fclose(infile); + return ret; +} diff --git a/slim/slim.1 b/slim/slim.1 new file mode 100644 index 0000000..ed3de1d --- /dev/null +++ b/slim/slim.1 @@ -0,0 +1,126 @@ +.TH slim 1 "January 14, 2023" "" "" +.SH NAME +\fBslim \fP- Simple LogIn Manager +\fB +.SH SYNOPSIS +.nf +.fam C +\fBslim\fP [\fIoptions\fP] [] +.fam T +.fi +.SH DESCRIPTION +SLiM is a lightweight login manager for X11, allowing the initialization +of a graphical session by entering username and password in a login screen. +It is desktop-agnostic and should work (suitably configured) with any +session style. +.SH OPTIONS +.TP +.B +\fB-c\fP /path/to/config +select a non-standard configuration file instead of /etc/slim.conf +.TP +.B +\fB-d\fP +run as a daemon +.TP +.B +\fB-n\fP +do NOT run as a daemon +.TP +.B +\fB-s\fP +be systemd compatible by disabling consolekit support. This option does +nothing if consolekit support has not been built in. +.TP +.B +\fB-p\fP /path/to/theme +display a preview of the theme. An already running X11 session +is required for theme preview. +.TP +.B +\fB-w\fP x +set the size of the window used for theme preview. This option is only valid +after a -p option +.TP +.B +\fB-h\fP +display a brief help message +.TP +.B +\fB-v\fP +display version information +.SH EXAMPLES +.TP +.B +\fBslim\fP \fB-d\fP +run \fBslim\fP in daemon mode +.TP +.B +\fBslim\fP \fB-p\fP /usr/share/slim/themes/default \fB-w\fP 800x600 +preview of the default theme at low resolution +.SH STARTING SLIM AT BOOT +Please refer to the documentation of your Operating System to make \fBslim\fP +automatically startup after the system boots. In particular, the method is +very different between SysV, OpenRC, runit and systemd init processes. +.SH CONFIGURATION +Global configuration is stored in the /etc/slim.conf file. See the comments +inside the file for a detailed explanation of the \fIoptions\fP. +.SH USAGE AND SPECIAL USERNAMES +When started, \fBslim\fP will show a login panel; enter the username and +password of the user you want to login as. +.PP +Special usernames: +.TP +.B +console +open an xterm login console. No password needed +.TP +.B +exit +quit \fBslim\fP. No password needed. See the note in \fBBUGS\fI. +.TP +.B +halt +shutdown the machine +.TP +.B +reboot +reboot the machine +.TP +.B +suspend +power-suspend the machine +.PP +See the configuration file for customizing the above commands. +The 'halt' and 'reboot' commands need the root password, and hence may not +work on systems where root login is disabled. +.PP +Shortcuts: +.TP +.B +F11 +executes a custom command (by default takes a screenshot) +.TP +.B +F1 +choose session type from session list. +.SH BUGS +When run as a daemon from OpenRC (and possibly other init systems), causing +SLiM to quit can sometimes result in a blank screen and unresponsive computer. +This includes when using the "exit" special username. +.PP +Probably still a few more but I'm working on them. +.SH AUTHORS +Simone Rota +.PP +Johannes Winkelmann +.PP +Nobuhiro Iwamatsu +.PP +Rob Pearce +.SH SEE ALSO +.\" .BR slim.conf (5), ... once I've written one! +.BR slimlock (1) +.PP +See the online documentation at the SLiM web site for further information +on themes, FAQs, etc. diff --git a/slim/slim.conf b/slim/slim.conf new file mode 100644 index 0000000..3e946d9 --- /dev/null +++ b/slim/slim.conf @@ -0,0 +1,124 @@ +# Path, X server and arguments (if needed) +# Note: -xauth $authfile is automatically appended, vt07 appended if no +# vtxx argument given. +default_path /bin:/usr/bin:/usr/local/bin +default_xserver /usr/bin/X +#xserver_arguments -dpi 75 +xserver_arguments -nolisten tcp -deferglyphs 16 + +# Full path to the xauth binary +xauth_path /usr/bin/xauth + +# Xauth file for server +authfile /var/run/slim.auth + + +# Commands for halt, login, etc. +halt_cmd /sbin/shutdown -h now +reboot_cmd /sbin/shutdown -r now +console_cmd /usr/bin/xterm -C -fg white -bg black +sb -T "Console login" -e /bin/sh -c "/bin/cat /etc/issue; exec /bin/login" +#suspend_cmd /usr/sbin/suspend + +# Activate numlock when slim starts. Valid values: on|off +# Default is to not change it +#numlock on + +# Hide the mouse cursor (note: does not work with some WMs). +# Valid values: true|false +#hidecursor false + +# This command is executed after a succesful login. +# You can place the %session and %theme variables to handle launching of +# specific commands in .xinitrc depending on chosen session and slim theme. +# Ensure that the command handles an empty %session, as that is the default +# +# NOTE: if your system does not have bash you need to adjust the command +# according to your preferred shell, e.g. for freebsd use: +# login_cmd exec /bin/sh - ~/.xinitrc %session +login_cmd exec /bin/bash -login ~/.xinitrc %session + +# Commands executed when starting and exiting a session. +# They can be used for registering a X11 session with +# sessreg. You can use the %user variable +# +# sessionstart_cmd some command +# sessionstop_cmd some command +sessionstart_cmd /usr/bin/sessreg -a -l "$DISPLAY" %user +sessionstop_cmd /usr/bin/sessreg -d -l "$DISPLAY" %user + +# Start in daemon mode. Valid values: yes | no +# Note that this can be overridden by the command line options "-d" and "-n" +daemon yes + +# Available sessions: +# The current chosen session name replaces %session in the login_cmd +# above, so your login command can handle different sessions. +# If no session is chosen (via F1), %session will be an empty string. This +# allows the script to handle default in a user-specific manner, if desired. +# See the xinitrc.sample file shipped with slim sources. +#sessions xfce4,icewm-session,wmaker,blackbox + +# Alternatively, read available sessions from the contents of a +# directory. The directory can contain either executable scripts, +# or xsessions .desktop files. In the case of .desktop files, the name +# displayed is the Name= value and the string substutited in place of +# %session is the Exec= value -- note that this may provide a full +# path to the session executable! +#sessiondir /usr/share/xsessions + + +# Executed when pressing F11 (requires imagemagick) +screenshot_cmd import -window root /slim.png +# Alternative using scrot. +#screenshot_cmd scrot /root/slim.png + +# Delay after failed authentication before allowing another attempt +# NOTE: This delay is additional to the time PAM takes to fail, and +# the feedback message remains after this delay. While the +# default value is 2 seconds, it's quite reasonable to set it +# to zero. +wrong_passwd_timeout 0 + +# Whether to sound the bell on failed login +#bell 0 + +# Whether to leave the username intact if authorisation fails. For +# users who mistype their password, 1 is better. +#keep_user_on_fail 0 + +# default user, leave blank to not pre-load the username. +#default_user simone + +# Focus the password field on start when default_user is set +# Set to "yes" to enable this feature +#focus_password no + +# Automatically login the default user (without entering +# the password. Set to "yes" to enable this feature +#auto_login no + + +# current theme, use comma separated list to specify a set to +# randomly choose from +current_theme default + +# Lock file +lockfile /run/slim.pid + +# Log file - full path for a file, or just stderr (or /dev/stderr) to send +# all log messages to stderr. +logfile /var/log/slim.log + +#---------------------------------------------------- +# The following options might be considered better placed in the theme.They +# will work either way; the theme takes priority if duplicated + +# welcome message. Available variables: %host, %domain +welcome_msg Welcome to %host + +# Session message. Prepended to the session name when pressing F1 +#session_msg Session: + +# shutdown / reboot messages +shutdown_msg The system is halting... +reboot_msg The system is rebooting... diff --git a/slim/slim.service b/slim/slim.service new file mode 100644 index 0000000..d58971c --- /dev/null +++ b/slim/slim.service @@ -0,0 +1,10 @@ +[Unit] +Description=SLiM Simple Login Manager +After=systemd-user-sessions.service + +[Service] +ExecStart=/usr/bin/slim -n -s +Restart=on-failure + +[Install] +Alias=display-manager.service diff --git a/slim/slimlock.1 b/slim/slimlock.1 new file mode 100644 index 0000000..d703956 --- /dev/null +++ b/slim/slimlock.1 @@ -0,0 +1,78 @@ +.TH slimlock 1 "January 12, 2023" "version 0.9" +.SH NAME +\fBslimlock\fP - Unholy Screen Locker +\fB +.SH SYNOPSIS +.nf +.fam C +\fBslimlock\fP [\-v] +.fam T +.fi +.SH DESCRIPTION +The Frankenstein's monster of screen lockers. Grafting SLiM and slock together +leads to blood, tears, and locked screens. +.SH OPTIONS +.TP +.B +\fB-v\fP +display version information +.SH CONFIGURATION +Slimlock reads the same configuration files you use for SLiM. It looks in +\fICFGDIR/slim.conf\fP and \fICFGDIR/slimlock.conf\fP, where \fICFGDIR\fP +is defined when built - usually \fI/etc\fP. +The options that are read from slim.conf are +hidecursor, current_theme, background_color and background_style, +screenshot_cmd, and welcome_msg. See the SLiM docs for more information. + +slimlock.conf contains the following settings: + +.TP +.B dpms_standby_timeout +number of seconds of inactivity before the screen blanks. +.BI "Default: " 60 +.TP +.B dpms_off_timeout +number of seconds of inactivity before the screen is turned off. +.BI "Default: " 600 +.TP +.B wrong_passwd_timeout +delay in seconds after an incorrect password is entered. +.BI "Default: " 2 +.TP +.B passwd_feedback_msg +message to display after a failed authentication attempt. +.BI "Default: " "Authentication failed" +.TP +.B passwd_feedback_capslock +message to display after a failed authentication attempt if the CapsLock is on. +.BI "Default: " "Authentication failed (CapsLock is on)" +.TP +.B show_username +1 to show username on themes with single input field; 0 to disable. +.BI "Default: " 1 +.TP +.B show_welcome_msg +1 to show SLiM's welcome message; 0 to disable. +.BI "Default: " 0 +.TP +.B tty_lock +1 to disallow virtual terminals switching; 0 to allow. +.BI "Default: " 1 +.TP +.B bell +1 to enable the bell on authentication failure; 0 to disable. +.BI "Default: " 1 +.SH BUGS +Where the theme defines a welcome message or password prompt, \fBslimlock\fP +displays them in the wrong place. +.PP +Using the same theme as \fBslim\fP assumes the theme is appropriate. Most of +the themes out there (including our defaults) put the word "Login" on the +panel, which isn't quite right for a screen lock. +.PP +To alleviate both of these, you can specify a different theme in +\fICFGDIR/slimlock.conf\fP, which will override the setting in +\fICFGDIR/slim.conf\fP, but you still need to find (or create) a suitable +lock theme. +.SH "SEE ALSO" +.BR slim (1) diff --git a/slim/slimlock.conf b/slim/slimlock.conf new file mode 100644 index 0000000..61e02fe --- /dev/null +++ b/slim/slimlock.conf @@ -0,0 +1,11 @@ +dpms_standby_timeout 60 +dpms_off_timeout 600 + +wrong_passwd_timeout 2 +passwd_feedback_x 50% +passwd_feedback_y 10% +passwd_feedback_msg Authentication failed +passwd_feedback_capslock Authentication failed (CapsLock is on) +show_username 1 +show_welcome_msg 0 +tty_lock 0 diff --git a/slim/slimlock.cpp b/slim/slimlock.cpp new file mode 100644 index 0000000..5b60726 --- /dev/null +++ b/slim/slimlock.cpp @@ -0,0 +1,342 @@ +/* slimlock + * Copyright (c) 2010-2012 Joel Burget + * Copyright (c) 2022-2023 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for usleep +#include +#include +#include +#include +#include + +#include "cfg.h" +#include "util.h" +#include "panel.h" +#include "const.h" + +#undef APPNAME +#define APPNAME "slimlock" +#define SLIMLOCKCFG SYSCONFDIR"/slimlock.conf" + +using namespace std; + +bool AuthenticateUser(); +static int ConvCallback(int num_msgs, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr); +void HandleSignal(int sig); +void *RaiseWindow(void *data); + +// In the absence of a class instance to contain these, just make them file +// public, as they're needed by multiple functions +static Display* Dpy; +static int Scr; +static Window Root; +static Window win; +static Cfg* cfg; +static Panel* LoginPanel; + +static pam_handle_t *pam_handle; + +static const struct pam_conv conv = {ConvCallback, NULL}; + +static CARD16 dpms_standby, dpms_suspend, dpms_off, dpms_level; +static BOOL dpms_state, using_dpms; + +static int term; // Used by a C callback function + + +static void die(const char *errstr, ...) +{ + va_list ap; + + va_start(ap, errstr); + vfprintf(stderr, errstr, ap); + va_end(ap); + exit(EXIT_FAILURE); +} + +int main(int argc, char **argv) +{ + if((argc == 2) && !strcmp("-v", argv[1])) + die ( APPNAME "-" VERSION ", © 2010-2012 Joel Burget\nUpdates © 2022-2023 Rob Pearce\n" ); + else if(argc != 1) + die ( "usage: " APPNAME " [-v]\n" ); + + void (*prev_fn)(int); + + // restore DPMS settings should slimlock be killed in the line of duty + prev_fn = signal(SIGTERM, HandleSignal); + if (prev_fn == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + // create a lock file to solve mutliple instances problem + // /var/lock used to be the place to put this, now it's /run/lock + // ...i think + struct stat statbuf; + int lock_file; + + // try /run/lock first, since i believe it's preferred + if (!stat("/run/lock", &statbuf)) + lock_file = open ( "/run/lock/" APPNAME ".lock", O_CREAT | O_RDWR, 0666); + else + lock_file = open ( "/var/lock/" APPNAME ".lock", O_CREAT | O_RDWR, 0666); + + int rc = flock(lock_file, LOCK_EX | LOCK_NB); + + if(rc) { + if(EWOULDBLOCK == errno) + die(APPNAME" already running\n"); + } + + unsigned int cfg_passwd_timeout; + + /* Read configuration and theme */ + cfg = new Cfg; + cfg->readConf(CFGFILE); + cfg->readConf(SLIMLOCKCFG); + string themeName = ""; + string themebase = ""; + string themefile = ""; + string themedir = ""; + + themebase = string(THEMESDIR) + "/"; + themeName = cfg->getOption("current_theme"); + string::size_type pos; + if ((pos = themeName.find(",")) != string::npos) + { + /* input is a set */ + themeName = cfg->findValidRandomTheme(themeName); + } + + bool loaded = false; + while (!loaded) + { + themedir = themebase + themeName; + themefile = themedir + THEMESFILE; + if (!cfg->readConf(themefile)) + { + if (themeName == "default") + { + cerr << APPNAME << ": Failed to open default theme file " + << themefile << endl; + exit(ERR_EXIT); + } + else + { + cerr << APPNAME << ": Invalid theme in config: " + << themeName << endl; + themeName = "default"; + } + } + else + { + loaded = true; + } + } + + const char *DisplayName = getenv("DISPLAY"); + if (!DisplayName) + DisplayName = DISPLAY; + + Dpy = XOpenDisplay(DisplayName); + if ( Dpy == 0 ) + die(APPNAME": cannot open display\n"); + + /* Get screen and root window */ + Scr = DefaultScreen(Dpy); + Root = RootWindow(Dpy, Scr); + + XSetWindowAttributes wa; + wa.override_redirect = 1; + wa.background_pixel = BlackPixel(Dpy, Scr); + + // Create a full screen window + win = XCreateWindow(Dpy, Root, + 0, 0, DisplayWidth(Dpy, Scr), DisplayHeight(Dpy, Scr), + 0, DefaultDepth(Dpy, Scr), CopyFromParent, + DefaultVisual(Dpy, Scr), CWOverrideRedirect | CWBackPixel, + &wa); + XMapWindow(Dpy, win); + + XFlush(Dpy); + for (int len = 1000; len; len--) { + if(XGrabKeyboard(Dpy, Root, True, GrabModeAsync, GrabModeAsync, CurrentTime) + == GrabSuccess) + break; + usleep(1000); + } + XSelectInput(Dpy, win, ExposureMask | KeyPressMask); + + /* Create panel */ + LoginPanel = new Panel(Dpy, Scr, win, cfg, themedir, Panel::Mode_Lock); + LoginPanel->HideCursor(); + + int ret = pam_start(APPNAME, LoginPanel->GetName().c_str(), &conv, &pam_handle); + // If we can't start PAM, just exit because slimlock won't work right + if (ret != PAM_SUCCESS) + die("PAM: %s\n", pam_strerror(pam_handle, ret)); + + // disable tty switching + if(cfg->getOption("tty_lock") == "1") { + if ((term = open("/dev/console", O_RDWR)) == -1) + perror("error opening console"); + + if ((ioctl(term, VT_LOCKSWITCH)) == -1) + perror("error locking console"); + } + + // Set up DPMS + unsigned int cfg_dpms_standby, cfg_dpms_off; + cfg_dpms_standby = Cfg::string2int(cfg->getOption("dpms_standby_timeout").c_str()); + cfg_dpms_off = Cfg::string2int(cfg->getOption("dpms_off_timeout").c_str()); + using_dpms = DPMSCapable(Dpy) && (cfg_dpms_standby > 0); + if (using_dpms) { + DPMSGetTimeouts(Dpy, &dpms_standby, &dpms_suspend, &dpms_off); + + DPMSSetTimeouts(Dpy, cfg_dpms_standby, + cfg_dpms_standby, cfg_dpms_off); + + DPMSInfo(Dpy, &dpms_level, &dpms_state); + if (!dpms_state) + DPMSEnable(Dpy); + } + + // Get password timeout + cfg_passwd_timeout = Cfg::string2int(cfg->getOption("wrong_passwd_timeout").c_str()); + // Let's just make sure it has a sane value + cfg_passwd_timeout = cfg_passwd_timeout > 60 ? 60 : cfg_passwd_timeout; + + pthread_t raise_thread; + pthread_create(&raise_thread, NULL, RaiseWindow, NULL); + +#if 0 // The DM code does this: + /* Init Root */ + LoginPanel->setBackground(); + + /* Show panel */ + LoginPanel->OpenPanel(); +#endif + + // Main loop + while (true) + { + LoginPanel->ResetPasswd(); + + // AuthenticateUser returns true if authenticated + if (AuthenticateUser()) + break; + + LoginPanel->WrongPassword(cfg_passwd_timeout); + } + + // kill thread before destroying the window that it's supposed to be raising + pthread_cancel(raise_thread); + + LoginPanel->ClosePanel(); + delete LoginPanel; + + // Get DPMS stuff back to normal + if (using_dpms) { + DPMSSetTimeouts(Dpy, dpms_standby, dpms_suspend, dpms_off); + // turn off DPMS if it was off when we entered + if (!dpms_state) + DPMSDisable(Dpy); + } + + XCloseDisplay(Dpy); + + close(lock_file); // will inherently release the flock + + if(cfg->getOption("tty_lock") == "1") { + if ((ioctl(term, VT_UNLOCKSWITCH)) == -1) { + perror("error unlocking console"); + } + } + close(term); + + return 0; +} + + +static int ConvCallback(int num_msgs, const struct pam_message **msg, + struct pam_response **resp, void *appdata_ptr) +{ + LoginPanel->EventHandler(Panel::Get_Passwd); + + // PAM expects an array of responses, one for each message + if (num_msgs == 0 || + (*resp = (pam_response*) calloc(num_msgs, sizeof(struct pam_message))) == NULL) + return PAM_BUF_ERR; + + for (int i = 0; i < num_msgs; i++) { + if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF && + msg[i]->msg_style != PAM_PROMPT_ECHO_ON) + continue; + + // return code is currently not used but should be set to zero + resp[i]->resp_retcode = 0; + if ((resp[i]->resp = strdup(LoginPanel->GetPasswd().c_str())) == NULL) { + free(*resp); + return PAM_BUF_ERR; + } + } + + return PAM_SUCCESS; +} + +bool AuthenticateUser() +{ + return(pam_authenticate(pam_handle, 0) == PAM_SUCCESS); +} + + +void HandleSignal(int sig) +{ + // Get DPMS stuff back to normal + if (using_dpms) { + DPMSSetTimeouts(Dpy, dpms_standby, dpms_suspend, dpms_off); + // turn off DPMS if it was off when we entered + if (!dpms_state) + DPMSDisable(Dpy); + } + + if ((ioctl(term, VT_UNLOCKSWITCH)) == -1) { + perror("error unlocking console"); + } + close(term); + + LoginPanel->ClosePanel(); + delete LoginPanel; + + die(APPNAME": Caught signal; dying\n"); +} + +void* RaiseWindow(void *data) +{ + while(1) { + XRaiseWindow(Dpy, win); + sleep(1); + } + + return (void *)0; +} diff --git a/slim/slimlock.pam b/slim/slimlock.pam new file mode 100644 index 0000000..7b88c2e --- /dev/null +++ b/slim/slimlock.pam @@ -0,0 +1,2 @@ +#%PAM-1.0 +auth required pam_unix.so nodelay nullok diff --git a/slim/switchuser.cpp b/slim/switchuser.cpp new file mode 100644 index 0000000..ca936ae --- /dev/null +++ b/slim/switchuser.cpp @@ -0,0 +1,74 @@ +/* + * SLiM - Simple Login Manager + * Copyright (C) 1997, 1998 Per Liden + * Copyright (C) 2004-06 Simone Rota + * Copyright (C) 2004-06 Johannes Winkelmann + * Copyright (C) 2022 Rob Pearce + * + * 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 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include "const.h" +#include "cfg.h" +#include "log.h" +#include "util.h" +#include "switchuser.h" + +using namespace std; + +SwitchUser::SwitchUser(struct passwd *pw, Cfg *c, const string& display, + char** _env) + : cfg(c), Pw(pw), env(_env) +{ +} + +SwitchUser::~SwitchUser() +{ + /* Never called */ +} + +void SwitchUser::Login(const char* cmd, const char* mcookie) +{ + SetUserId(); + SetClientAuth(mcookie); + Execute(cmd); +} + +void SwitchUser::SetUserId() +{ + if( (Pw == 0) || + (initgroups(Pw->pw_name, Pw->pw_gid) != 0) || + (setgid(Pw->pw_gid) != 0) || + (setuid(Pw->pw_uid) != 0) ) { + logStream << APPNAME << ": could not switch user id" << endl; + exit(ERR_EXIT); + } +} + +void SwitchUser::Execute(const char* cmd) +{ + if ( chdir(Pw->pw_dir) < 0 ) + logStream << APPNAME << ": unable to chdir() to user's home: " << strerror(errno) << endl; + logStream.closeLog(); + execle(Pw->pw_shell, Pw->pw_shell, "-c", cmd, NULL, env); + /// @todo this copy-paste of App:CloseLog() should be cleaned up + if ( !logStream.openLog( cfg->getOption("logfile").c_str() ) ) { + cerr << APPNAME << ": Could not access log file: " << cfg->getOption("logfile") << endl; + exit(ERR_EXIT); + } + logStream << APPNAME << ": could not execute login command: " << strerror(errno) << endl; +} + +void SwitchUser::SetClientAuth(const char* mcookie) +{ + string home = string(Pw->pw_dir); + string authfile = home + "/.Xauthority"; + remove(authfile.c_str()); + Util::add_mcookie(mcookie, ":0", cfg->getOption("xauth_path"), + authfile); +} diff --git a/slim/switchuser.h b/slim/switchuser.h new file mode 100644 index 0000000..49a1215 --- /dev/null +++ b/slim/switchuser.h @@ -0,0 +1,45 @@ +/* SLiM - Simple Login Manager + Copyright (C) 1997, 1998 Per Liden + Copyright (C) 2004-06 Simone Rota + Copyright (C) 2004-06 Johannes Winkelmann + + 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 of the License, or + (at your option) any later version. +*/ + +#ifndef _SWITCHUSER_H_ +#define _SWITCHUSER_H_ + +#include +#include +#include +#include +#include +#include +#include + +class Cfg; + +class SwitchUser +{ +public: + SwitchUser(struct passwd *pw, Cfg *c, const std::string& display, + char** _env); + ~SwitchUser(); + void Login(const char* cmd, const char* mcookie); + +private: + SwitchUser(); + void SetEnvironment(); + void SetUserId(); + void Execute(const char* cmd); + void SetClientAuth(const char* mcookie); + Cfg* cfg; + struct passwd *Pw; + + char** env; +}; + +#endif /* _SWITCHUSER_H_ */ diff --git a/slim/themes/CMakeLists.txt b/slim/themes/CMakeLists.txt new file mode 100644 index 0000000..9eee86c --- /dev/null +++ b/slim/themes/CMakeLists.txt @@ -0,0 +1 @@ +subdirs (default original) diff --git a/slim/themes/default/CMakeLists.txt b/slim/themes/default/CMakeLists.txt new file mode 100644 index 0000000..5860362 --- /dev/null +++ b/slim/themes/default/CMakeLists.txt @@ -0,0 +1,5 @@ +set (THEMES "themes/default") + +install(FILES slim.theme DESTINATION ${PKGDATADIR}/${THEMES}) +install(FILES panel.png DESTINATION ${PKGDATADIR}/${THEMES}) +install(FILES background.jpg DESTINATION ${PKGDATADIR}/${THEMES}) diff --git a/slim/themes/default/COPYRIGHT.background b/slim/themes/default/COPYRIGHT.background new file mode 100644 index 0000000..83a03e1 --- /dev/null +++ b/slim/themes/default/COPYRIGHT.background @@ -0,0 +1 @@ +Green glass image copyright Rob Pearce (c)2022 diff --git a/slim/themes/default/COPYRIGHT.panel b/slim/themes/default/COPYRIGHT.panel new file mode 100644 index 0000000..d219b0f --- /dev/null +++ b/slim/themes/default/COPYRIGHT.panel @@ -0,0 +1 @@ +The icon in the panel was downloaded from https://icons8.com diff --git a/slim/themes/default/LICENSE.panel b/slim/themes/default/LICENSE.panel new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/slim/themes/default/LICENSE.panel @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/slim/themes/default/background.jpg b/slim/themes/default/background.jpg new file mode 100644 index 0000000..98233f5 Binary files /dev/null and b/slim/themes/default/background.jpg differ diff --git a/slim/themes/default/panel.png b/slim/themes/default/panel.png new file mode 100644 index 0000000..8eaeaf9 Binary files /dev/null and b/slim/themes/default/panel.png differ diff --git a/slim/themes/default/slim.theme b/slim/themes/default/slim.theme new file mode 100644 index 0000000..4bef732 --- /dev/null +++ b/slim/themes/default/slim.theme @@ -0,0 +1,52 @@ +# Green glass theme for SLiM +# by Rob Pearce + +# Messages (e.g. shutdown) +msg_color #FFFFFF +msg_font Verdana:size=18:bold:dpi=75 +msg_x 50% +msg_y 40% +msg_shadow_color #702342 +msg_shadow_xoffset 1 +msg_shadow_yoffset 1 + +# valid values: stretch, tile +background_style stretch +background_color #11dd56 + +# Input controls +input_panel_x 50% +input_panel_y 45% +input_name_x 394 +input_name_y 142 +input_pass_x 394 +input_pass_y 178 +input_font Verdana:size=12:dpi=75 +input_color #000000 + +# Username / password request +username_font Verdana:size=18:dpi=75 +username_color #FFDFFF +username_x 270 +username_y 144 +password_x 270 +password_y 180 +username_shadow_color #704f42 +username_shadow_xoffset 1 +username_shadow_yoffset 1 + +username_msg Username: +password_msg Password: + +# Welcome message +welcome_font Verdana:size=28:bold:dpi=75 +welcome_color #F4D5C0 +welcome_x 50% +welcome_y 40 +welcome_msg Login to %host +welcome_shadow_xoffset 2 +welcome_shadow_yoffset 2 +welcome_shadow_color #338353 + +passwd_feedback_x 50% +passwd_feedback_y 80% diff --git a/slim/themes/original/CMakeLists.txt b/slim/themes/original/CMakeLists.txt new file mode 100644 index 0000000..6b2e009 --- /dev/null +++ b/slim/themes/original/CMakeLists.txt @@ -0,0 +1,5 @@ +set (THEMES "themes/original") + +install(FILES slim.theme DESTINATION ${PKGDATADIR}/${THEMES}) +install(FILES panel.png DESTINATION ${PKGDATADIR}/${THEMES}) +install(FILES background.jpg DESTINATION ${PKGDATADIR}/${THEMES}) diff --git a/slim/themes/original/COPYRIGHT.background b/slim/themes/original/COPYRIGHT.background new file mode 100644 index 0000000..6ddda8f --- /dev/null +++ b/slim/themes/original/COPYRIGHT.background @@ -0,0 +1,2 @@ +Text. 04 is copyright (c) 2005 by rafael nascimento +http://darkevil.deviantart.com diff --git a/slim/themes/original/COPYRIGHT.panel b/slim/themes/original/COPYRIGHT.panel new file mode 100644 index 0000000..0096f9d --- /dev/null +++ b/slim/themes/original/COPYRIGHT.panel @@ -0,0 +1,16 @@ + Lila SVG Icon and Theme Artwork + Copyright (C) 2004 Lila Community + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \ No newline at end of file diff --git a/slim/themes/original/LICENSE.panel b/slim/themes/original/LICENSE.panel new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/slim/themes/original/LICENSE.panel @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 of the License, 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, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/slim/themes/original/background.jpg b/slim/themes/original/background.jpg new file mode 100644 index 0000000..bc9f71b Binary files /dev/null and b/slim/themes/original/background.jpg differ diff --git a/slim/themes/original/panel.png b/slim/themes/original/panel.png new file mode 100644 index 0000000..3841543 Binary files /dev/null and b/slim/themes/original/panel.png differ diff --git a/slim/themes/original/slim.theme b/slim/themes/original/slim.theme new file mode 100644 index 0000000..3ba62df --- /dev/null +++ b/slim/themes/original/slim.theme @@ -0,0 +1,37 @@ +# text04 theme for SLiM +# by Johannes Winkelmann + +# Messages (ie: shutdown) +msg_color #FFFFFF +msg_font Verdana:size=18:bold:dpi=75 +msg_x 50% +msg_y 40% +msg_shadow_color #702342 +msg_shadow_xoffset 1 +msg_shadow_yoffset 1 + +# valid values: stretch, tile +background_style stretch +background_color #eedddd + +# Input controls +input_panel_x 25% +input_panel_y 65% +input_name_x 394 +input_name_y 181 +input_font Verdana:size=12:dpi=75 +input_color #000000 + +# Username / password request +username_font Verdana:size=14:bold:dpi=75 +username_color #f9f9f9 +username_x 280 +username_y 183 +password_x 50% +password_y 183 +username_shadow_color #702342 +username_shadow_xoffset 1 +username_shadow_yoffset 1 + +username_msg Username: +password_msg Password: diff --git a/slim/themes/suckless/panel.png b/slim/themes/suckless/panel.png new file mode 100644 index 0000000..8eaeaf9 Binary files /dev/null and b/slim/themes/suckless/panel.png differ diff --git a/slim/themes/suckless/slim.theme b/slim/themes/suckless/slim.theme new file mode 100644 index 0000000..3e74b5d --- /dev/null +++ b/slim/themes/suckless/slim.theme @@ -0,0 +1,52 @@ +# Green glass theme for SLiM +# by Rob Pearce + +# Messages (e.g. shutdown) +msg_color #ced9dc +msg_font Hack Nerd Font:size=18:bold:dpi=75 +msg_x 50% +msg_y 40% +msg_shadow_color #702342 +msg_shadow_xoffset 1 +msg_shadow_yoffset 1 + +# valid values: stretch, tile +background_style stretch +background_color #003E53 + +# Input controls +input_panel_x 50% +input_panel_y 45% +input_name_x 394 +input_name_y 142 +input_pass_x 394 +input_pass_y 178 +input_font Hack Nerd Font:size=12:dpi=75 +input_color #003E53 + +# Username / password request +username_font Hack Nerd Font:size=18:dpi=75 +username_color #ced9dc +username_x 270 +username_y 144 +password_x 270 +password_y 180 +username_shadow_color #003E53 +username_shadow_xoffset 1 +username_shadow_yoffset 1 + +username_msg Username: +password_msg Password: + +# Welcome message +welcome_font Hack Nerd Font:size=28:bold:dpi=75 +welcome_color #37817E +welcome_x 50% +welcome_y 40 +welcome_msg Login to %host +welcome_shadow_xoffset 2 +welcome_shadow_yoffset 2 +welcome_shadow_color #003E53 + +passwd_feedback_x 50% +passwd_feedback_y 80% diff --git a/slim/util.cpp b/slim/util.cpp new file mode 100644 index 0000000..2028058 --- /dev/null +++ b/slim/util.cpp @@ -0,0 +1,70 @@ +/* + * SLiM - Simple Login Manager + * Copyright (C) 2009 Eygene Ryabinkin + * + * 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 of the License, or + * (at your option) any later version. +*/ + +#include + +#include +#include +#include +#include + +#include "util.h" + +/* + * Adds the given cookie to the specified Xauthority file. + * Returns true on success, false on fault. + */ +bool Util::add_mcookie(const std::string &mcookie, const char *display, + const std::string &xauth_cmd, const std::string &authfile) +{ + FILE *fp; + std::string cmd = xauth_cmd + " -f " + authfile + " -q"; + + fp = popen(cmd.c_str(), "w"); + if (!fp) + return false; + fprintf(fp, "remove %s\n", display); + fprintf(fp, "add %s %s %s\n", display, ".", mcookie.c_str()); + fprintf(fp, "exit\n"); + + pclose(fp); + return true; +} + +/* + * Interface for random number generator. Just now it uses ordinary + * random/srandom routines and serves as a wrapper for them. + */ +void Util::srandom(unsigned long seed) +{ + ::srandom(seed); +} + +long Util::random(void) +{ + return ::random(); +} + +/* + * Makes seed for the srandom() using "random" values obtained from + * getpid(), time(NULL) and others. + */ +long Util::makeseed(void) +{ + struct timespec ts; + long pid = getpid(); + long tm = time(NULL); + + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + ts.tv_sec = ts.tv_nsec = 0; + } + + return pid + tm + (ts.tv_sec ^ ts.tv_nsec); +} diff --git a/slim/util.h b/slim/util.h new file mode 100644 index 0000000..4e28a40 --- /dev/null +++ b/slim/util.h @@ -0,0 +1,25 @@ +/* SLiM - Simple Login Manager + Copyright (C) 2009 Eygene Ryabinkin + + 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 of the License, or + (at your option) any later version. +*/ +#ifndef _UTIL_H__ +#define _UTIL_H__ + +#include + +namespace Util +{ + bool add_mcookie(const std::string &mcookie, const char *display, + const std::string &xauth_cmd, const std::string &authfile); + + void srandom(unsigned long seed); + long random(void); + + long makeseed(void); +} + +#endif /* _UTIL_H__ */ diff --git a/slim/xinitrc.sample b/slim/xinitrc.sample new file mode 100644 index 0000000..8ac5a7f --- /dev/null +++ b/slim/xinitrc.sample @@ -0,0 +1,23 @@ +# the following variable defines the session which is started if the user +# doesn't explicitly select a session +DEFAULT_SESSION=twm + +case $1 in +xfce4) + exec startxfce4 + ;; +icewm) + icewmbg & + icewmtray & + exec icewm + ;; +wmaker) + exec wmaker + ;; +blackbox) + exec blackbox + ;; +*) + exec $DEFAULT_SESSION + ;; +esac