pax_global_header00006660000000000000000000000064110137712360014513gustar00rootroot0000000000000052 comment=881d380f122a71efe32098308320f06d83e774f0 vlock-2.2.2/000077500000000000000000000000001101377123600126345ustar00rootroot00000000000000vlock-2.2.2/.gitignore000066400000000000000000000000441101377123600146220ustar00rootroot00000000000000*.o *.so config.mk vlock vlock-main vlock-2.2.2/COPYING000066400000000000000000000431141101377123600136720ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 Lesser General Public License instead of this License. vlock-2.2.2/ChangeLog000066400000000000000000000131071101377123600144100ustar00rootroot000000000000002008-05-14 Frank Benkstein vlock 2.2.2 * Fix and improve build system: use LDLIBS instead of LDFLAGS where appropriate and provide EXTRA_CFLAGS/EXTRA_LDFLAGS parameters in ./configure. * Display number of authentication tries on exit. 2008-03-26 Frank Benkstein vlock 2.2.1 * Fix a script handling bug. 2008-03-21 Frank Benkstein vlock 2.2 * Small plugin documentation update. 2008-01-15 Frank Benkstein vlock 2.2 rc5 * Fixed snprintf() corner case in "new" module. * Restore timeout if select() was interrupted by a signal in prompt(). 2007-12-29 Frank Benkstein vlock 2.2 rc4 * Slightly changed the fix for the previous security issue: directory parts are now silently stripped from erroneous names instead of reported as errors. * Fixed a file descriptor leak in script handling. * Fixed a bug where vlock would run in an infinite loop if not started from a terminal. Now it simply exits. * Added unit tests (using cunit). 2007-11-28 Frank Benkstein vlock 2.2 rc3 * A critical security problem (local root exploit) was fixed: Previous versions of vlock (since 2.2 alpha1) contained a serious flaw that allowed any user to execute arbitrary code as root . This was possible because plugin names could contain "../" thus escaping the pre-defined plugin directory. All users of vlock 2.2 development versions are advised to update. * Improved script handling. * Added two more scripts. 2007-11-26 Frank Benkstein vlock 2.2 rc2 * Fixed man page installation error. 2007-11-17 Frank Benkstein vlock 2.2 rc1 * Updated documentation. 2007-11-04 Frank Benkstein vlock 2.2 beta2 * Improved error handling and error messages. * Fixed portability bugs. * Fixed dependency handling bug. 2007-10-16 Frank Benkstein vlock 2.2 beta1 * Documentation update. * Improved error handling. 2007-10-03 Frank Benkstein vlock 2.2 alpha3 * Added ./configure script. * Improved plugin handling. * Improved error handling. * Improved child process handling. 2007-09-21 Frank Benkstein vlock 2.2 alpha2 * Added libcaca based screen saver module. * Miscellaneous minor fixes. 2007-09-19 Frank Benkstein vlock 2.2 alpha1 * Medium rewrite was undertaken. * Support for plugins (modules and scripts) was added. * vlock-all, vlock-new, and vlock-nosysrq were converted to modules. * Other sample modules and scripts were added. * vlock now tries to terminale cleanly if killed by SIGTERM and on errors. 2007-09-08 Frank Benkstein vlock 2.1 * Documentation update. 2007-08-29 Frank Benkstein vlock 2.1 rc1 * Really tiny cleanups. 2007-08-23 Frank Benkstein vlock 2.1 beta1 * Fix compilation on Debian/kFreeBSD. 2007-08-16 Frank Benkstein vlock 2.1 alpha2 * Locking message now configurable. * Added configuration file support: ~/.vlockrc. * "vlock --new" now works even if stdin is not a terminal. * Improved documentation. 2007-08-11 Frank Benkstein vlock 2.1 alpha1 * Added FreeBSD support. Build with PAM_LIBS=-lpam, install with INSTALL=ginstall. * Improved signal handling. * Added timeout support to password prompts. 2007-08-09 Frank Benkstein vlock 2.0 * No changes since vlock 2.0 rc2. 2007-08-06 Frank Benkstein vlock 2.0 rc2 * Improved man page and error messages. * Fixed bug that prevented shadow authentication from working. * vlock can now only be killed by root. This will probably be fixed in future releases. 2007-08-05 Frank Benkstein vlock 2.0 rc1 * Code cleanups (comments, style). * Fixed two merely theoretical security problems. 2007-08-02 Frank Benkstein vlock 2.0 beta2 * Many improvements to security. * General cleanup to source tree. * ChangeLog added. * Manpages updated. * LICENSE file updated, license terms unchanged. * README updated. * Added a SECURITY blurb. * -n,--new option now implies -a,--all instead of requiring it. 2007-07-30 Frank Benkstein vlock 2.0 beta1 * Added manpages for all tools. * Enter key must be pressed before authentication is started. * When run as root, vlock now locks as the user given by the $USER environment variable. * Add -n,--new option to run vlock on a new virtual console. 2007-07-28 Frank Benkstein vlock 2.0 alpha2 * Shadow authentication added. 2007-07-25 Frank Benkstein vlock 2.0 alpha1 * Complete rewrite. vlock is now made of several tools called from a shell script. 2007-05-23 Frank Benkstein vlock 1.4 * No changes since rc2. 2007-05-10 Frank Benkstein vlock 1.4 rc2 * Make install rule made more friendly to packagers. * Updated license declarations: sources now clearly state "GNU General Public License version 2". 2007-05-09 Frank Benkstein vlock 1.4 rc1 * Some compile time warnings fixed.. * Support for SysRq disabling added. 1999-01-13 Michael Johnson vlock 1.3 1998-03-12 Michael Johnson vlock 1.2 vlock-2.2.2/Makefile000066400000000000000000000072121101377123600142760ustar00rootroot00000000000000# vlock makefile include config.mk VPATH = src VLOCK_VERSION = 2.2.2 PROGRAMS = vlock vlock-main .PHONY: all all: $(PROGRAMS) .PHONY: debug debug: @$(MAKE) DEBUG=y ifeq ($(ENABLE_PLUGINS),yes) all: plugins endif .PHONY: plugins plugins: modules scripts .PHONY: modules modules: @$(MAKE) -C modules .PHONY: scripts scripts: @$(MAKE) -C scripts .PHONY: check memcheck check memcheck: @$(MAKE) -C tests $@ ### configuration ### config.mk: $(info ) $(info ###################################################) $(info # Creating default configuration. #) $(info # Run ./configure or edit config.mk to customize. #) $(info ###################################################) $(info ) @./configure --quiet ### installation rules ### .PHONY: install install: install-programs install-man ifeq ($(ENABLE_PLUGINS),yes) install: install-plugins endif .PHONY: install-programs install-programs: $(PROGRAMS) $(MKDIR_P) -m 755 $(DESTDIR)$(PREFIX)/bin $(INSTALL) -m 755 -o root -g $(ROOT_GROUP) vlock $(DESTDIR)$(BINDIR)/vlock $(MKDIR_P) -m 755 $(DESTDIR)$(PREFIX)/sbin $(INSTALL) -m 4711 -o root -g $(ROOT_GROUP) vlock-main $(DESTDIR)$(SBINDIR)/vlock-main .PHONY: install-plugins install-plugins: install-modules install-scripts .PHONY: install-modules install-modules: @$(MAKE) -C modules install .PHONY: install-scripts install-scripts: @$(MAKE) -C scripts install .PHONY: install-man install-man: $(MKDIR_P) -m 755 $(DESTDIR)$(MANDIR)/man1 $(INSTALL) -m 644 -o root -g $(ROOT_GROUP) man/vlock.1 $(DESTDIR)$(MANDIR)/man1/vlock.1 $(MKDIR_P) -m 755 $(DESTDIR)$(MANDIR)/man8 $(INSTALL) -m 644 -o root -g $(ROOT_GROUP) man/vlock-main.8 $(DESTDIR)$(MANDIR)/man8/vlock-main.8 $(MKDIR_P) -m 755 $(DESTDIR)$(MANDIR)/man5 $(INSTALL) -m 644 -o root -g $(ROOT_GROUP) man/vlock-plugins.5 $(DESTDIR)$(MANDIR)/man5/vlock-plugins.5 ### build rules ### vlock: vlock.sh config.mk Makefile $(BOURNE_SHELL) -n $< sed \ -e 's,%BOURNE_SHELL%,$(BOURNE_SHELL),' \ -e 's,%PREFIX%,$(PREFIX),' \ -e 's,%VLOCK_VERSION%,$(VLOCK_VERSION),' \ -e 's,%VLOCK_ENABLE_PLUGINS%,$(ENABLE_PLUGINS),' \ $< > $@.tmp mv -f $@.tmp $@ override CFLAGS += -Isrc vlock-main: vlock-main.o prompt.o auth-$(AUTH_METHOD).o console_switch.o util.o auth-pam.o: auth-pam.c prompt.h auth.h auth-shadow.o: auth-shadow.c prompt.h auth.h prompt.o: prompt.c prompt.h vlock-main.o: vlock-main.c auth.h prompt.h util.h plugins.o: plugins.c tsort.h plugin.h plugins.h list.h util.h module.o : override CFLAGS += -DVLOCK_MODULE_DIR="\"$(MODULEDIR)\"" module.o: module.c plugin.h list.h util.h script.o : override CFLAGS += -DVLOCK_SCRIPT_DIR="\"$(SCRIPTDIR)\"" script.o: script.c plugin.h process.h list.h util.h plugin.o: plugin.c plugin.h list.h util.h tsort.o: tsort.c tsort.h list.h list.o: list.c list.h util.h console_switch.o: console_switch.c console_switch.h process.o: process.c process.h util.o: util.c util.h ifneq ($(ENABLE_ROOT_PASSWORD),yes) vlock-main.o : override CFLAGS += -DNO_ROOT_PASS endif ifeq ($(AUTH_METHOD),pam) vlock-main : override LDLIBS += $(PAM_LIBS) endif ifeq ($(AUTH_METHOD),shadow) vlock-main : override LDLIBS += $(CRYPT_LIB) endif ifeq ($(ENABLE_PLUGINS),yes) vlock-main: plugins.o plugin.o module.o process.o script.o tsort.o list.o # -rdynamic is needed so that the all plugin can access the symbols from console_switch.o vlock-main : override LDFLAGS += -rdynamic vlock-main : override LDLIBS += $(DL_LIB) vlock-main.o : override CFLAGS += -DUSE_PLUGINS vlock-main.o: plugins.h endif .PHONY: realclean realclean: clean $(RM) config.mk .PHONY: clean clean: $(RM) $(PROGRAMS) $(wildcard *.o) @$(MAKE) -C modules clean @$(MAKE) -C scripts clean @$(MAKE) -C tests clean vlock-2.2.2/PLUGINS000066400000000000000000000140541101377123600137040ustar00rootroot00000000000000OVERVIEW ======== Plugins are a way to extend vlock's functionality. They can define hooks that are called at certain points in a vlock session. There are two separate types of plugins: modules and scripts. Modules are shared objects that are loaded into vlock's address space. They run with the same privileges as vlock and thus are very powerful but also dangerous. Scripts may be any kind of executables located in vlock's script directory. They are run in separate processes with lowered privileges, i.e. the same as the user who started vlock. For simple tasks scripts should be preferred over modules. They are easier to develop and test and have a lower impact on security and stability. NB: The following interface is not yet declared stable. It is not guaranteed that plugins (modules or scripts) that work with vlock 2.2 will work with future versions. DEPENDENCIES ============ Plugins may depend on each other in several ways. There are six different types of dependencies. Each dependency type is represented by a list of plugin names. The way of declaring them is different for modules and scripts but their names and meaning are the same. Resolving the dependencies is done after all initially requested plugins are loaded and may fail if dependencies cannot be met. The names and meaning of the dependencies are as follows: requires: The plugins listed here must be loaded for the declaring plugin to work. If any of the plugins is not loaded yet it will be loaded automatically. Dependency resolving fails if a plugin cannot be loaded. needs: The plugins listed here must be loaded for the declaring plugin to work. Dependency resolving fails if any of the plugins listed here is not loaded. depends: The plugins listed here must be loaded for the declaring plugin to work. If any of the plugins listed here is not loaded the declaring plugin is automatically unloaded. Dependency resolving fails if the declaring plugin is already required by some other plugin. conflicts: The plugins listed here must not be loaded at the same time as the declaring plugin. Dependency resolving fails if any of the plugins listed here is loaded. The other two dependencies are used to specify the order of the plugins: preceeds: The plugins listed here must come after the declaring plugin. succeeds: The plugins listed here must come before the declaring plugin. Sorting the plugins may fail if the "preceeds" and "succeeds" dependencies introduce circles. HOOKS ===== There are four different hooks that plugins may declare: vlock_start: This hook is called once immediately after vlock is initialized and before any authentication prompt. If a plugin signals an error in this hook vlock aborts and calls the vlock_end hooks of all previously called modules. vlock_end: This hook is called once after successful authentication or if vlock is killed by SIGTERM. Errors in this hook are ignored. vlock_save: This hook is called after the vlock message is displayed every time the timeout expires or the escape key is pressed. If a plugin signals an error in this hook its vlock_save_abort hook is called and both hooks are not called again afterwards. vlock_save_abort: This hook is called after vlock_save was called and any key was pressed. If a plugin signals an error in this hook both this hook and the vlock_save hook are not called again. Note: Hooks should not block. Screensavers should be executed in a background process or thread. The only exception would be hooks that suspend the machine (though these technically do not block in the common sense). MODULES ======= Modules are shared objects that are loaded into vlock's address space. They export hook functions and dependencies as global functions. To ensure definitions modules should include vlock_plugin.h from the module subdirectory of the vlock source distribution. dependencies ------------ Dependencies are declared as NULL terminated arrays of const char pointers. Empty lists can be just left out. Example:: /* From nosysrq.c */ const char *preceeds[] = { "new", "all", NULL }; const char *depends[] = { "all", NULL }; hooks ----- Hooks are boolean functions that take a void pointer pointer. Their return status indicates success or failure. The argument points to a void pointer that may be set freely. It may be used to maintain state between the different hooks. It is initialized to NULL. Hook functions must not block and not terminate the program. On error they may print the cause of the error to stderr in addition to returning false. example ------- Please see modules/example_module.c in the vlock source distribution. SCRIPTS ======= Scripts are executables that are started as child processes of vlock. They run with the same privileges as the user starting vlock instead of the privileges of the vlock process. They communicate with vlock through command line arguments and pipes. dependencies ------------ To get the dependencies of a script it is run once for each dependency item with the dependency name as the single command line argument. Its standard output is redirected to a pipe that is read by vlock. The plugin should print the dependency items, if any, separated by arbitrary white space (carriage return, space or newline) and then exit. No errors are detected in this process. hooks ----- After the dependencies are read the script is run one last time this time with the string "hooks" as the single command line argument. Its standard input is redirected from a pipe that is written to by vlock. Whenever a hook should be executed its name followed by a new line character are written to the pipe. The script's standard output and standard error are redirected to /dev/null. The script should only exit if end-of-file is detected on standard in even in cases where no subsequent hooks need to be executed. Error detection is limited to detecting if the script exits prematurely. There is currently no way for a script what kind of error happened. example ------- Please see scripts/example_script.sh in the vlock source distribution. vlock-2.2.2/README000066400000000000000000000037341101377123600135230ustar00rootroot00000000000000:: VV VV LL OOO CCCCC KK KK VV VV LL OO OO CC KK KK originally written by VV VV LL OO OO CC KK Michael K. Johnson VV VV LL OO OO CC KK KK for Linux Journal VVV LLLLLLLL OOO CCCCC KK KK This is vlock, the Linux _V_irtual Console locking program. It allows you to lock one or all of the sessions of your Linux console display. Usage is very simple; by default, vlock locks the single console or terminal you are on. If you want to lock the console completely so that no one else can log into any of the virtual consoles (perhaps because you have login sessions running on several other virtual consoles at the same time), you use the -a or --all flag to cause vlock to not allow any user to switch to any console without typing your password. WARNING: If you lock all the consoles, they will be *really* locked. Unless you have a serial terminal, or can log in remotely to kill vlock, you *will not* be able to get back to your terminal session without correct authentication. After a new installation always test vlock in a terminal to verify that authentication is set up correctly. If you loose data because you have to reset your computer because of vlock -a, it is your own problem, not mine. I warned you. The root user will *always* be able to unlock any vlock session, unless disabled at compile time. vlock consists of several plugins. Some of them are potentially dangerous and access to them should be restricted. Please refer to SECURITY for a detailed description. "vlock -h" or "vlock --help" will get you a help message. To make vlock switch to a new console before locking, use the -n or --new flag. If installed with proper permissions this even works from an X11 session. The -n flag implies -a and thus all warnings about -a also apply to -n. vlock is maintained by Frank Benkstein . vlock-2.2.2/README.X11000066400000000000000000000006321101377123600140650ustar00rootroot00000000000000Although vlock is primarily designed to lock the text console of a machine it is also possible to use it from X. Using the "--new" option vlock will switch to an empty virtual terminal and then lock the display by preventing console switching entirely. After successful authentication it switches back to the virtual terminal that was active when vlock was started, i.e. your X session, when started from X. vlock-2.2.2/SECURITY000066400000000000000000000036421101377123600140330ustar00rootroot00000000000000OVERVIEW ======== vlock is a denial of service tool. Linux allows any user logged into the virtual console to lock the system completely. vlock takes this one step further and potenially grants this ability (and potentially other, even more hazardous ones) to any user through the use of plugins. The default installation sets permission that allow only users in the vlock group to run dangerous modules. Others will still be able to lock their own console or terminal and run plugins that are considered secure. This behavior is configurabe through ./configure option or by editing config.mk. DETAILS ======= vlock-main allows plugins to extend its functionality. These plugins are separated into two groups: modules and scripts. Both are only loaded from locations that are specified at compile time. It is extremely important that these directories are only writable by privileged users. MODULES ------- Modules are shared objects that are loaded into vlock's address space. Because vlock will most likely be installed setuid-root care must be taken that these modules are secure themselves and that access to potentially dangerous modules is limited to trusted users. UNDER NO CIRCUMSTANCES MUST UNPRIVILEGED USERS BE ALLOWED TO PUT THEIR OWN MODULES INTO VLOCK'S MODULE DIRECTORY. Doing this would allowing them to run any code they want with elevated privileges, i.e. as root. SCRIPTS ------- Scripts may be any kind of executables located in vlock's script directory. They are run with with lowered privileges, i.e. the same as the user who started vlock, in a separate process. They also don't have direct access to the terminal vlock runs on and thus may be considered a relatively secure alternative to modules. For any privileged operations they want to perform they have to use helpers such as sudo. Although less dangerous than modules vlock's script directory must still be protected the same as the module directory. vlock-2.2.2/STYLE000066400000000000000000000001271101377123600134570ustar00rootroot00000000000000Functions that fail should print an error message and set errno to 0 *OR* leave errno. vlock-2.2.2/TODO000066400000000000000000000004161101377123600133250ustar00rootroot00000000000000high ---- - comments - documentation medium ------ - generate error on invalid script - document ./configure options better - help distributors when vlock group is not avaiable at installation time - plugin to ensure that vlock is run only once on a machine low --- vlock-2.2.2/configure000077500000000000000000000231571101377123600145530ustar00rootroot00000000000000#!/bin/sh set -e error() { echo >&2 "$0: error: $@" } fatal_error() { error "$@" exit 1 } is_set() { ( eval [ "\"\${$1+set}\"" = "set" ] ) } show_usage() { cat <] Some influential environment variables: CC C compiler command CFLAGS C compiler flags EXTRA_CFLAGS additional C compiler flags (extends default) LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory EXTRA_LDFLAGS additional linker flags (extends default) VLOCK_GROUP group for restricted modules (default: vlock) VLOCK_MODE mode for restricted modules (default: 0750) Use these variables to override the choices made by \`configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to . EOT } set_variable() { eval "$1"='"$2"' } enable_feature() { case "$1" in plugins) ENABLE_PLUGINS="$2" ;; root-password) ENABLE_ROOT_PASSWORD="$2" ;; pam|shadow) if [ "$2" = "yes" ] ; then if [ -n "$auth_method" ] && [ "$auth_method" != "$1" ] ; then fatal_error "pam and shadow authentication are mutually exclusive" fi AUTH_METHOD="$1" else fatal_error "cannot disable authentication" fi ;; debug) if [ "$2" = "yes" ] ; then CFLAGS="${DEBUG_CFLAGS}" else CFLAGS="${DEFAULT_CFLAGS}" fi ;; *) fatal_error "invalid feature name: $1" ;; esac } parse_arguments() { local feature opt optarg while [ $# -gt 0 ] ; do if ! opt=`expr "x$1" : 'x\([^=]*\)=.*'` ; then opt="$1" fi if ! optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` ; then optarg="" fi case "$1" in --disable-*) feature=`expr "x$1" : 'x--disable-\(.*\)'` enable_feature "$feature" no shift ;; --enable-*=no) feature=`expr "x$1" : 'x--enable-\(.*\)=no'` enable_feature "$feature" no shift ;; --enable-*=yes) feature=`expr "x$1" : 'x--enable-\(.*\)=yes'` enable_feature "$feature" yes shift ;; --enable-*) feature=`expr "x$1" : 'x--enable-\(.*\)'` enable_feature "$feature" yes shift ;; *=*) shift # unshift set -- "$opt" "$optarg" "$@" ;; --prefix) PREFIX="$2" shift 2 || fatal_error "$1 argument missing" ;; --bindir) BINDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --sbindir) SBINDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --libdir) LIBDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --moduledir) MODULEDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --scriptdir) SCRIPTDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --mandir) MANDIR="$2" shift 2 || fatal_error "$1 argument missing" ;; --with-modules) MODULES="$2" shift 2 || fatal_error "$1 argument missing" ;; --with-scripts) SCRIPTS="$2" shift 2 || fatal_error "$1 argument missing" ;; EXTRA_CFLAGS) CFLAGS="${CFLAGS} $2" shift 2 || fatal_error "$1 value missing" ;; EXTRA_LDFLAGS) LDFLAGS="${LDFLAGS} $2" shift 2 || fatal_error "$1 value missing" ;; [A-Z]*) set_variable "$1" "$2" shift 2 || fatal_error "$1 value missing" ;; --quiet) verbose=0 shift ;; --help|-h) show_usage exit ;; -*) error "unrecognized option: $1" echo >&2 "Try \`$0 --help' for more information." exit 1 ;; *) error "invalid argument: $1" echo >&2 "Try \`$0 --help' for more information." exit 1 ;; esac done } set_defaults() { # architecture independent defaults PREFIX="/usr/local" BINDIR="\$(PREFIX)/bin" SBINDIR="\$(PREFIX)/sbin" LIBDIR="\$(PREFIX)/lib" MANDIR="\$(PREFIX)/share/man" SCRIPTDIR="\$(LIBDIR)/vlock/scripts" MODULEDIR="\$(LIBDIR)/vlock/modules" CC=gcc DEFAULT_CFLAGS="-O2 -Wall -W -pedantic -std=gnu99" DEBUG_CFLAGS="-O0 -g -Wall -W -pedantic -std=gnu99" CFLAGS="${DEFAULT_CFLAGS}" LD=ld LDFLAGS="" AUTH_METHOD="pam" ENABLE_ROOT_PASSWORD="yes" ENABLE_PLUGINS="yes" SCRIPTS="" VLOCK_GROUP="vlock" VLOCK_MODULE_MODE="0750" BOURNE_SHELL="/bin/sh" # architecture dependent defaults OS=`uname` for make in make gmake ; do if $make -f /dev/null -q -v 2>/dev/null | head -n 1 | grep -q "GNU Make" ; then MAKE="$make" break fi done ROOT_GROUP=`getent group | awk -F: '$3 == 0 { print $1 ; exit }'` case "$OS" in Linux) PAM_LIBS='-ldl -lpam' DL_LIB='-ldl' CRYPT_LIB='-lcrypt' MODULES="all.so new.so nosysrq.so" ;; GNU/kFreeBSD) PAM_LIBS='-ldl -lpam' DL_LIB='-ldl' CRYPT_LIB='-lcrypt' MODULES="all.so new.so" ;; FreeBSD) PAM_LIBS='-lpam' DL_LIB='' CRYPT_LIB='' MODULES="all.so new.so" ;; esac } parse_config_mk() { local tmpdir if [ -z "$MAKE" ] ; then error "GNU make not found" echo >&2 "Set MAKE environment variable to specify alternative." exit 1 fi tmpdir=`mktemp -d -t vlock-configure.XXXXXX` $MAKE -rR -f config.mk -p -q . 2>/dev/null | awk > "$tmpdir/config.mk" ' /^# makefile/ { p=1; next } /^#/ { p=0; next } p==1 && $1 != "MAKEFILE_LIST" && /^[A-Za-z_]+ :?= .*/ { print } ' while read line do variable_name=`expr "x${line}" : 'x\([[[:alpha:]_]\{1,\}\) :\{0,1\}='` if variable_value=`expr "x${line}" : 'x[[:alpha:]_]\{1,\} :\{0,1\}= \(.*\)'` ; then set_variable "$variable_name" "$variable_value" else set_variable "$variable_name" "" fi done < "$tmpdir/config.mk" rm -rf "$tmpdir" } show_summary() { cat < config.mk < vlock-2.2.2/man/vlock-plugins.5000066400000000000000000000030711101377123600162730ustar00rootroot00000000000000.TH VLOCK-PLUGINS 5 "10 November 2007" "Linux" "Linux Programmer's Manual" .SH NAME vlock-plugins \- plugin support for vlock .SH DESCRIPTION If vlock-main(8) is compiled with plugin support its default features are very limited: it can only lock the current session and ask for authenticiaton. However it is possible to extend this functions through plugins. These plugins are loaded when vlock-main starts and can provide hooks that are called at certain points during the lifetime of the vlock-main process. .PP .SH "DEFAULT PLUGINS" The following plugins are provided when installing vlock with default options: .PP .B all .IP This plugin locks all sessions by disabling console switching. It is also loaded when giving the \fB-a,--all\fR option to vlock(1). .PP .B new .IP This plugin switches to a new virtual console before disabling console switching through the "all" plugin. It is also loaded when giving the \fB-n,--new\fR option to vlock(1). .PP .B nosysrq .IP Linux only. This plugin disables the Linux SysRQ mechanism before the console switching is locked by the "all" plugin. It is also loaded when giving the \fB-s,--disable-sysrq\fR option to vlock(1). .PP .SH "ADDITIONAL PLUGINS" The following plugins are only available if explicitely selected at build time: .PP .B caca .IP This plugin runs a random libcaca screensaver when the screen is locked. .SH WRITING PLUGINS For information about writing plugins see the PLUGINS file in the vlock source distribution. .SH "SEE ALSO" .BR vlock (1), .BR vlock-main (8) .SH AUTHORS Frank Benkstein vlock-2.2.2/man/vlock.1000066400000000000000000000110031101377123600146020ustar00rootroot00000000000000.TH VLOCK 1 "28 July 2007" "Linux" "Linux User's Manual" .SH NAME vlock \- Virtual Console lock program .SH SYNOPSIS .B vlock [ -hv ] .PP .B vlock [ -acns ] [ -t ] [ plugins... ] .SH DESCRIPTION .B vlock is a program to lock one or more sessions on the Linux console. This is especially useful for Linux machines which have multiple users with access to the console. One user may lock his or her session(s) while still allowing other users to use the system on other virtual consoles. If desired, the entire console may be locked and virtual console switching disabled. .PP By default, only the current VC (virtual console) is locked. With the \fB-a,--all\fR option all VCs are locked. The locked VCs cannot be unlocked without the invoker's password or the root password. The root password will always be able to unlock any or all sessions, unless disabled at compile time. .PP Please note that it is entirely possible to completely lock yourself out of the console with the \fB-a,--all\fR option if you cannot remember your password! Unless you are able to kill vlock by logging in remotely via a serial terminal or network, a hard reset is the only method of ``unlocking'' the display. .PP When locking the entire console display it is sometimes still possible to kill vlock using the Secure Access Key (SAK) or other commands that are available through the SysRq mechanism. When the \fB-s,--disable-sysrq\fR and \fB-a,--all\fR options are given the SysRq mechanism is disabled while vlock is running. See /usr/src/linux/Documentation/sysrq.txt for more details. .PP vlock works for console sessions primarily. To lock the entire console display from an X session use the \fB-n,--new\fR option. This will make vlock switch to an empty virtual console to lock the display. .PP The options \fB-n,--new\fR, \fB-s,--disable-sysrq\fR, and \fB-t,--timeout\fR only work if vlock is compiled with plugin support. See the PLUGINS section for more information. .SH OPTIONS .B -a,--all .IP Lock all console sessions and disable VC switching. .PP .B -c,--current .IP Lock the current session (this is the default). .PP .B -n,--new .IP Switch to a new virtual console before locking all console sessions. .PP .B -s,--disable-sysrq .IP Disable the SysRq mechanism while consoles are locked. This option only works if the \fB-a,--all\fR option given. .PP .B -t,--timeout .IP Specify the timeout for the screensaver plugins. See vlock-plugins(5) for more information. .PP .B -h,--help .IP Print a brief help message. .PP .B -v,--version .IP Print the version number. .PP .SH "ENVIRONMENT VARIABLES" The following environment variables can be used to change vlock's behavior: .PP .B USER .IP If this variable is when \fBvlock\fR is run as root (uid 0) vlock locks the screen as this user instead of root. The root password will still be able to unlock the session, unless disabled at compile time. .PP .B VLOCK_ALL_MESSAGE .IP If this variable is set and all consoles are locked its contents will be used as the locking message instead of the default message. .PP .B VLOCK_CURRENT_MESSAGE .IP If this variable is set and only the current consoles is locked its contents will be used as the locking message instead of the default message. .PP .B VLOCK_MESSAGE .IP If this variable is set its contents will be used as the locking message instead of the default. This overrides the former two variables. .PP .B VLOCK_PLUGINS .IP If this variable is set it is interpreted as a space separated list of plugins that will be loaded when vlock starts additionally to the ones listed on the command line. .PP .B VLOCK_TIMEOUT .IP Set this variable to specify the timeout (in seconds) after which the screen saver plugins (if any) will be invoked. If this variable is unset or set to an invalid value or 0 no timeout is used. See vlock-plugins(5) for more information about plugins. .PP .B VLOCK_PROMPT_TIMEOUT .IP Set this variable to specify the amount of time (in seconds) you will have to enter your password at the password prompt. If this variable is unset or set to an invalid value or 0 no timeout is used. \fBWarning\fR: If this value is too low, you may not be able to unlock your session. .PP .SH FILES .B ~/.vlockrc .IP This file is read by \fBvlock\fR on startup if it exists. All the variables mentioned above can be set here. .SH SECURITY See the SECURITY file in the \fBvlock\fR distribution for more information. .PP .SH "SEE ALSO" .BR vlock-main (8), .BR vlock-plugins (5) .SH AUTHORS Michael K. Johnson .PP Frank Benkstein vlock-2.2.2/modules/000077500000000000000000000000001101377123600143045ustar00rootroot00000000000000vlock-2.2.2/modules/Makefile000066400000000000000000000016511101377123600157470ustar00rootroot00000000000000include ../config.mk MODULES += $(EXTRA_MODULES) .PHONY: all all: $(MODULES) .PHONY: install install: $(addprefix install-, $(MODULES)) MODULE_GROUP = $(ROOT_GROUP) MODULE_MODE = 0755 override CFLAGS += -I../src -fPIC #special build rules caca.so : override LDLIBS += -lcaca -lncurses all.o: all.c ../src/console_switch.h #generic build rule %.so : override LDFLAGS += -shared %.so: %.o $(LINK.o) -shared $^ $(LOADLIBES) $(LDLIBS) -o $@ # special installation rules install-new.so : MODULE_GROUP=$(VLOCK_GROUP) install-new.so : MODULE_MODE=$(VLOCK_MODULE_MODE) install-nosysrq.so : MODULE_GROUP=$(VLOCK_GROUP) install-nosysrq.so : MODULE_MODE=$(VLOCK_MODULE_MODE) # generic installation rule .PHONY: install-%.so install-%.so: %.so $(MKDIR_P) -m 755 $(DESTDIR)$(MODULEDIR) $(INSTALL) -m $(MODULE_MODE) -o root -g $(MODULE_GROUP) $< $(DESTDIR)$(MODULEDIR)/$< .PHONY: clean clean: $(RM) $(wildcard *.o) $(wildcard *.so) vlock-2.2.2/modules/all.c000066400000000000000000000016311101377123600152210ustar00rootroot00000000000000/* all.c -- console grabbing plugin for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include #include #include #include #include "vlock_plugin.h" #include "console_switch.h" bool vlock_start(void __attribute__((unused)) **ctx_ptr) { return lock_console_switch(); } bool vlock_end(void __attribute__((unused)) **ctx_ptr) { return unlock_console_switch(); } vlock-2.2.2/modules/caca.c000066400000000000000000000540011101377123600153370ustar00rootroot00000000000000/* caca.c -- a screen saving plugin for vlock, * the VT locking program for linux * * This file consists mostly of the code from cacademo from libcaca. Only * minor changes were necessary to fit it into vlock's module architecture. * These changes are copyright (C) 2007 Frank Benkstein. * * cacademo various demo effects for libcaca * Copyright (c) 1998 Michele Bini * 2003-2006 Jean-Yves Lamoureux * 2004-2006 Sam Hocevar * All Rights Reserved * * $Id: cacademo.c 1130 2007-06-28 12:49:28Z sam $ * * This program is free software. It comes without any warranty, to * the extent permitted by applicable law. You can redistribute it * and/or modify it under the terms of the Do What The Fuck You Want * To Public License, Version 2, as published by Sam Hocevar. See * http://sam.zoy.org/wtfpl/COPYING for more details. */ #include #include #include #include #include #ifndef M_PI # define M_PI 3.14159265358979323846 #endif #include #include #include #include #include #include #include #include "process.h" #include "vlock_plugin.h" enum action { PREPARE, INIT, UPDATE, RENDER, FREE }; void transition(cucul_canvas_t *, int, int); void plasma(enum action, cucul_canvas_t *); void metaballs(enum action, cucul_canvas_t *); void moire(enum action, cucul_canvas_t *); void matrix(enum action, cucul_canvas_t *); void (*fn[])(enum action, cucul_canvas_t *) = { plasma, metaballs, moire, matrix, }; #define DEMOS (sizeof(fn)/sizeof(*fn)) #define DEMO_FRAMES cucul_rand(500, 1000) #define TRANSITION_FRAMES 40 #define TRANSITION_COUNT 3 #define TRANSITION_CIRCLE 0 #define TRANSITION_STAR 1 #define TRANSITION_SQUARE 2 /* Common macros for dither-based demos */ #define XSIZ 256 #define YSIZ 256 /* Global variables */ static int frame = 0; static bool abort_requested = false; void handle_sigterm(int __attribute__((unused)) signum) { abort_requested = true; } static int caca_main(void *argument); bool vlock_save(void **ctx_ptr) { static struct child_process child = { .function = caca_main, .argument = NULL, .stdin_fd = REDIRECT_DEV_NULL, .stdout_fd = NO_REDIRECT, .stderr_fd = NO_REDIRECT, }; /* Initialize ncurses. */ initscr(); if (!create_child(&child)) return false; *ctx_ptr = &child; return true; } bool vlock_save_abort(void **ctx_ptr) { struct child_process *child = *ctx_ptr; if (child != NULL) { ensure_death(child->pid); /* Restore sane terminal and uninitialize ncurses. */ curs_set(1); refresh(); endwin(); *ctx_ptr = NULL; } return true; } static int caca_main(void __attribute__((unused)) *argument) { static caca_display_t *dp; static cucul_canvas_t *frontcv, *backcv, *mask; int demo, next = -1, next_transition = DEMO_FRAMES; unsigned int i; int tmode = cucul_rand(0, TRANSITION_COUNT); /* Set up two canvases, a mask, and attach a display to the front one */ frontcv = cucul_create_canvas(0, 0); backcv = cucul_create_canvas(0, 0); mask = cucul_create_canvas(0, 0); (void) setenv("CACA_DRIVER", "ncurses", 1); dp = caca_create_display(frontcv); if(!dp) return 1; cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv), cucul_get_canvas_height(frontcv)); cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv), cucul_get_canvas_height(frontcv)); /* Set refresh delay. 40ms corresponds to 25 FPS. */ caca_set_display_time(dp, 40000); /* Initialise all demos' lookup tables */ for(i = 0; i < DEMOS; i++) fn[i](PREPARE, frontcv); /* Choose a demo at random */ demo = cucul_rand(0, DEMOS); fn[demo](INIT, frontcv); for(;;) { if (abort_requested) goto end; /* Resize the spare canvas, just in case the main one changed */ cucul_set_canvas_size(backcv, cucul_get_canvas_width(frontcv), cucul_get_canvas_height(frontcv)); cucul_set_canvas_size(mask, cucul_get_canvas_width(frontcv), cucul_get_canvas_height(frontcv)); /* Update demo's data */ fn[demo](UPDATE, frontcv); /* Handle transitions */ if(frame == next_transition) { next = cucul_rand(0, DEMOS); if(next == demo) next = (next + 1) % DEMOS; fn[next](INIT, backcv); } else if(frame == next_transition + TRANSITION_FRAMES) { fn[demo](FREE, frontcv); demo = next; next = -1; next_transition = frame + DEMO_FRAMES; tmode = cucul_rand(0, TRANSITION_COUNT); } if(next != -1) fn[next](UPDATE, backcv); frame++; /* Render main demo's canvas */ fn[demo](RENDER, frontcv); /* If a transition is on its way, render it */ if(next != -1) { fn[next](RENDER, backcv); cucul_set_color_ansi(mask, CUCUL_LIGHTGRAY, CUCUL_BLACK); cucul_clear_canvas(mask); cucul_set_color_ansi(mask, CUCUL_WHITE, CUCUL_WHITE); transition(mask, tmode, 100 * (frame - next_transition) / TRANSITION_FRAMES); cucul_blit(frontcv, 0, 0, backcv, mask); } cucul_set_color_ansi(frontcv, CUCUL_WHITE, CUCUL_BLUE); if(frame < 100) cucul_put_str(frontcv, cucul_get_canvas_width(frontcv) - 30, cucul_get_canvas_height(frontcv) - 2, " -=[ Powered by libcaca ]=- "); caca_refresh_display(dp); } end: if(next != -1) fn[next](FREE, frontcv); fn[demo](FREE, frontcv); caca_free_display(dp); cucul_free_canvas(mask); cucul_free_canvas(backcv); cucul_free_canvas(frontcv); return 0; } /* Transitions */ void transition(cucul_canvas_t *mask, int tmode, int completed) { static float const star[] = { 0.000000, -1.000000, 0.308000, -0.349000, 0.992000, -0.244000, 0.500000, 0.266000, 0.632000, 0.998000, 0.008000, 0.659000, -0.601000, 0.995000, -0.496000, 0.275000, -0.997000, -0.244000, -0.313000, -0.349000 }; static float star_rot[sizeof(star)/sizeof(*star)]; static float const square[] = { -1, -1, 1, -1, 1, 1, -1, 1 }; static float square_rot[sizeof(square)/sizeof(*square)]; float mulx = 0.0075f * completed * cucul_get_canvas_width(mask); float muly = 0.0075f * completed * cucul_get_canvas_height(mask); int w2 = cucul_get_canvas_width(mask) / 2; int h2 = cucul_get_canvas_height(mask) / 2; float angle = (0.0075f * completed * 360) * 3.14 / 180, x, y; unsigned int i; switch(tmode) { case TRANSITION_SQUARE: /* Compute rotated coordinates */ for(i = 0; i < (sizeof(square) / sizeof(*square)) / 2; i++) { x = square[i * 2]; y = square[i * 2 + 1]; square_rot[i * 2] = x * cos(angle) - y * sin(angle); square_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle); } mulx *= 1.8; muly *= 1.8; cucul_fill_triangle(mask, square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \ square_rot[1*2] * mulx + w2, square_rot[1*2+1] * muly + h2, \ square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, '#'); cucul_fill_triangle(mask, square_rot[0*2] * mulx + w2, square_rot[0*2+1] * muly + h2, \ square_rot[2*2] * mulx + w2, square_rot[2*2+1] * muly + h2, \ square_rot[3*2] * mulx + w2, square_rot[3*2+1] * muly + h2, '#'); break; case TRANSITION_STAR: /* Compute rotated coordinates */ for(i = 0; i < (sizeof(star) / sizeof(*star)) / 2; i++) { x = star[i * 2]; y = star[i * 2 + 1]; star_rot[i * 2] = x * cos(angle) - y * sin(angle); star_rot[i * 2 + 1] = y * cos(angle) + x * sin(angle); } mulx *= 1.8; muly *= 1.8; #define DO_TRI(a, b, c) \ cucul_fill_triangle(mask, \ star_rot[(a)*2] * mulx + w2, star_rot[(a)*2+1] * muly + h2, \ star_rot[(b)*2] * mulx + w2, star_rot[(b)*2+1] * muly + h2, \ star_rot[(c)*2] * mulx + w2, star_rot[(c)*2+1] * muly + h2, '#') DO_TRI(0, 1, 9); DO_TRI(1, 2, 3); DO_TRI(3, 4, 5); DO_TRI(5, 6, 7); DO_TRI(7, 8, 9); DO_TRI(9, 1, 5); DO_TRI(9, 5, 7); DO_TRI(1, 3, 5); break; case TRANSITION_CIRCLE: cucul_fill_ellipse(mask, w2, h2, mulx, muly, '#'); break; } } /* The plasma effect */ #define TABLEX (XSIZ * 2) #define TABLEY (YSIZ * 2) static uint8_t table[TABLEX * TABLEY]; static void do_plasma(uint8_t *, double, double, double, double, double, double); void plasma(enum action action, cucul_canvas_t *cv) { static cucul_dither_t *dither; static uint8_t *screen; static unsigned int red[256], green[256], blue[256], alpha[256]; static double r[3], R[6]; int i, x, y; switch(action) { case PREPARE: /* Fill various tables */ for(i = 0 ; i < 256; i++) red[i] = green[i] = blue[i] = alpha[i] = 0; for(i = 0; i < 3; i++) r[i] = (double)(cucul_rand(1, 1000)) / 60000 * M_PI; for(i = 0; i < 6; i++) R[i] = (double)(cucul_rand(1, 1000)) / 10000; for(y = 0 ; y < TABLEY ; y++) for(x = 0 ; x < TABLEX ; x++) { double tmp = (((double)((x - (TABLEX / 2)) * (x - (TABLEX / 2)) + (y - (TABLEX / 2)) * (y - (TABLEX / 2)))) * (M_PI / (TABLEX * TABLEX + TABLEY * TABLEY))); table[x + y * TABLEX] = (1.0 + sin(12.0 * sqrt(tmp))) * 256 / 6; } break; case INIT: screen = malloc(XSIZ * YSIZ * sizeof(uint8_t)); dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0); break; case UPDATE: for(i = 0 ; i < 256; i++) { double z = ((double)i) / 256 * 6 * M_PI; red[i] = (1.0 + sin(z + r[1] * frame)) / 2 * 0xfff; blue[i] = (1.0 + cos(z + r[0] * (frame + 100))) / 2 * 0xfff; green[i] = (1.0 + cos(z + r[2] * (frame + 200))) / 2 * 0xfff; } /* Set the palette */ cucul_set_dither_palette(dither, red, green, blue, alpha); do_plasma(screen, (1.0 + sin(((double)frame) * R[0])) / 2, (1.0 + sin(((double)frame) * R[1])) / 2, (1.0 + sin(((double)frame) * R[2])) / 2, (1.0 + sin(((double)frame) * R[3])) / 2, (1.0 + sin(((double)frame) * R[4])) / 2, (1.0 + sin(((double)frame) * R[5])) / 2); break; case RENDER: cucul_dither_bitmap(cv, 0, 0, cucul_get_canvas_width(cv), cucul_get_canvas_height(cv), dither, screen); break; case FREE: free(screen); cucul_free_dither(dither); break; } } static void do_plasma(uint8_t *pixels, double x_1, double y_1, double x_2, double y_2, double x_3, double y_3) { unsigned int X1 = x_1 * (TABLEX / 2), Y1 = y_1 * (TABLEY / 2), X2 = x_2 * (TABLEX / 2), Y2 = y_2 * (TABLEY / 2), X3 = x_3 * (TABLEX / 2), Y3 = y_3 * (TABLEY / 2); unsigned int y; uint8_t * t1 = table + X1 + Y1 * TABLEX, * t2 = table + X2 + Y2 * TABLEX, * t3 = table + X3 + Y3 * TABLEX; for(y = 0; y < YSIZ; y++) { unsigned int x; uint8_t * tmp = pixels + y * YSIZ; unsigned int ty = y * TABLEX, tmax = ty + XSIZ; for(x = 0; ty < tmax; ty++, tmp++) tmp[0] = t1[ty] + t2[ty] + t3[ty]; } } /* The metaball effect */ #define METASIZE (XSIZ/2) #define METABALLS 12 #define CROPBALL 200 /* Colour index where to crop balls */ static uint8_t metaball[METASIZE * METASIZE]; static void create_ball(void); static void draw_ball(uint8_t *, unsigned int, unsigned int); void metaballs(enum action action, cucul_canvas_t *cv) { static cucul_dither_t *cucul_dither; static uint8_t *screen; static unsigned int r[256], g[256], b[256], a[256]; static float dd[METABALLS], di[METABALLS], dj[METABALLS], dk[METABALLS]; static unsigned int x[METABALLS], y[METABALLS]; static float i = 10.0, j = 17.0, k = 11.0; static double offset[360 + 80]; static unsigned int angleoff; int n, angle; switch(action) { case PREPARE: /* Make the palette eatable by libcaca */ for(n = 0; n < 256; n++) r[n] = g[n] = b[n] = a[n] = 0x0; r[255] = g[255] = b[255] = 0xfff; /* Generate ball sprite */ create_ball(); for(n = 0; n < METABALLS; n++) { dd[n] = cucul_rand(0, 100); di[n] = (float)cucul_rand(500, 4000) / 6000.0; dj[n] = (float)cucul_rand(500, 4000) / 6000.0; dk[n] = (float)cucul_rand(500, 4000) / 6000.0; } angleoff = cucul_rand(0, 360); for(n = 0; n < 360 + 80; n++) offset[n] = 1.0 + sin((double)(n * M_PI / 60)); break; case INIT: screen = malloc(XSIZ * YSIZ * sizeof(uint8_t)); /* Create a libcucul dither smaller than our pixel buffer, so that we * display only the interesting part of it */ cucul_dither = cucul_create_dither(8, XSIZ - METASIZE, YSIZ - METASIZE, XSIZ, 0, 0, 0, 0); break; case UPDATE: angle = (frame + angleoff) % 360; /* Crop the palette */ for(n = CROPBALL; n < 255; n++) { int t1, t2, t3; double c1 = offset[angle]; double c2 = offset[angle + 40]; double c3 = offset[angle + 80]; t1 = n < 0x40 ? 0 : n < 0xc0 ? (n - 0x40) * 0x20 : 0xfff; t2 = n < 0xe0 ? 0 : (n - 0xe0) * 0x80; t3 = n < 0x40 ? n * 0x40 : 0xfff; r[n] = (c1 * t1 + c2 * t2 + c3 * t3) / 4; g[n] = (c1 * t2 + c2 * t3 + c3 * t1) / 4; b[n] = (c1 * t3 + c2 * t1 + c3 * t2) / 4; } /* Set the palette */ cucul_set_dither_palette(cucul_dither, r, g, b, a); /* Silly paths for our balls */ for(n = 0; n < METABALLS; n++) { float u = di[n] * i + dj[n] * j + dk[n] * sin(di[n] * k); float v = dd[n] + di[n] * j + dj[n] * k + dk[n] * sin(dk[n] * i); u = sin(i + u * 2.1) * (1.0 + sin(u)); v = sin(j + v * 1.9) * (1.0 + sin(v)); x[n] = (XSIZ - METASIZE) / 2 + u * (XSIZ - METASIZE) / 4; y[n] = (YSIZ - METASIZE) / 2 + v * (YSIZ - METASIZE) / 4; } i += 0.011; j += 0.017; k += 0.019; memset(screen, 0, XSIZ * YSIZ); for(n = 0; n < METABALLS; n++) draw_ball(screen, x[n], y[n]); break; case RENDER: cucul_dither_bitmap(cv, 0, 0, cucul_get_canvas_width(cv), cucul_get_canvas_height(cv), cucul_dither, screen + (METASIZE / 2) * (1 + XSIZ)); break; case FREE: free(screen); cucul_free_dither(cucul_dither); break; } } static void create_ball(void) { int x, y; float distance; for(y = 0; y < METASIZE; y++) for(x = 0; x < METASIZE; x++) { distance = ((METASIZE/2) - x) * ((METASIZE/2) - x) + ((METASIZE/2) - y) * ((METASIZE/2) - y); distance = sqrt(distance) * 64 / METASIZE; metaball[x + y * METASIZE] = distance > 15 ? 0 : (255 - distance) * 15; } } static void draw_ball(uint8_t *screen, unsigned int bx, unsigned int by) { unsigned int color; unsigned int i, e = 0; unsigned int b = (by * XSIZ) + bx; for(i = 0; i < METASIZE * METASIZE; i++) { color = screen[b] + metaball[i]; if(color > 255) color = 255; screen[b] = color; if(e == METASIZE) { e = 0; b += XSIZ - METASIZE; } b++; e++; } } /* The moiré effect */ #define DISCSIZ (XSIZ*2) #define DISCTHICKNESS (XSIZ*15/40) static uint8_t disc[DISCSIZ * DISCSIZ]; static void put_disc(uint8_t *, int, int); static void draw_line(int, int, char); void moire(enum action action, cucul_canvas_t *cv) { static cucul_dither_t *dither; static uint8_t *screen; static float d[6]; static unsigned int red[256], green[256], blue[256], alpha[256]; int i, x, y; switch(action) { case PREPARE: /* Fill various tables */ for(i = 0 ; i < 256; i++) red[i] = green[i] = blue[i] = alpha[i] = 0; for(i = 0; i < 6; i++) d[i] = ((float)cucul_rand(50, 70)) / 1000.0; red[0] = green[0] = blue[0] = 0x777; red[1] = green[1] = blue[1] = 0xfff; /* Fill the circle */ for(i = DISCSIZ * 2; i > 0; i -= DISCTHICKNESS) { int t, dx, dy; for(t = 0, dx = 0, dy = i; dx <= dy; dx++) { draw_line(dx / 3, dy / 3, (i / DISCTHICKNESS) % 2); draw_line(dy / 3, dx / 3, (i / DISCTHICKNESS) % 2); t += t > 0 ? dx - dy-- : dx; } } break; case INIT: screen = malloc(XSIZ * YSIZ * sizeof(uint8_t)); dither = cucul_create_dither(8, XSIZ, YSIZ, XSIZ, 0, 0, 0, 0); break; case UPDATE: memset(screen, 0, XSIZ * YSIZ); /* Set the palette */ red[0] = 0.5 * (1 + sin(d[0] * (frame + 1000))) * 0xfff; green[0] = 0.5 * (1 + cos(d[1] * frame)) * 0xfff; blue[0] = 0.5 * (1 + cos(d[2] * (frame + 3000))) * 0xfff; red[1] = 0.5 * (1 + sin(d[3] * (frame + 2000))) * 0xfff; green[1] = 0.5 * (1 + cos(d[4] * frame + 5.0)) * 0xfff; blue[1] = 0.5 * (1 + cos(d[5] * (frame + 4000))) * 0xfff; cucul_set_dither_palette(dither, red, green, blue, alpha); /* Draw circles */ x = cos(d[0] * (frame + 1000)) * 128.0 + (XSIZ / 2); y = sin(0.11 * frame) * 128.0 + (YSIZ / 2); put_disc(screen, x, y); x = cos(0.13 * frame + 2.0) * 64.0 + (XSIZ / 2); y = sin(d[1] * (frame + 2000)) * 64.0 + (YSIZ / 2); put_disc(screen, x, y); break; case RENDER: cucul_dither_bitmap(cv, 0, 0, cucul_get_canvas_width(cv), cucul_get_canvas_height(cv), dither, screen); break; case FREE: free(screen); cucul_free_dither(dither); break; } } static void put_disc(uint8_t *screen, int x, int y) { char *src = ((char*)disc) + (DISCSIZ / 2 - x) + (DISCSIZ / 2 - y) * DISCSIZ; int i, j; for(j = 0; j < YSIZ; j++) for(i = 0; i < XSIZ; i++) { screen[i + XSIZ * j] ^= src[i + DISCSIZ * j]; } } static void draw_line(int x, int y, char color) { if(x == 0 || y == 0 || y > DISCSIZ / 2) return; if(x > DISCSIZ / 2) x = DISCSIZ / 2; memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) - y), color, 2 * x - 1); memset(disc + (DISCSIZ / 2) - x + DISCSIZ * ((DISCSIZ / 2) + y - 1), color, 2 * x - 1); } /* Matrix effect */ #define MAXDROPS 500 #define MINLEN 15 #define MAXLEN 30 void matrix(enum action action, cucul_canvas_t *cv) { static struct drop { int x, y, speed, len; char str[MAXLEN]; } drop[MAXDROPS]; int w, h, i, j; switch(action) { case PREPARE: for(i = 0; i < MAXDROPS; i++) { drop[i].x = cucul_rand(0, 1000); drop[i].y = cucul_rand(0, 1000); drop[i].speed = 5 + cucul_rand(0, 30); drop[i].len = MINLEN + cucul_rand(0, (MAXLEN - MINLEN)); for(j = 0; j < MAXLEN; j++) drop[i].str[j] = cucul_rand('0', 'z'); } break; case INIT: break; case UPDATE: w = cucul_get_canvas_width(cv); h = cucul_get_canvas_height(cv); for(i = 0; i < MAXDROPS && i < (w * h / 32); i++) { drop[i].y += drop[i].speed; if(drop[i].y > 1000) { drop[i].y -= 1000; drop[i].x = cucul_rand(0, 1000); } } break; case RENDER: w = cucul_get_canvas_width(cv); h = cucul_get_canvas_height(cv); cucul_set_color_ansi(cv, CUCUL_BLACK, CUCUL_BLACK); cucul_clear_canvas(cv); for(i = 0; i < MAXDROPS && i < (w * h / 32); i++) { int x, y; x = drop[i].x * w / 1000 / 2 * 2; y = drop[i].y * (h + MAXLEN) / 1000; for(j = 0; j < drop[i].len; j++) { unsigned int fg; if(j < 2) fg = CUCUL_WHITE; else if(j < drop[i].len / 4) fg = CUCUL_LIGHTGREEN; else if(j < drop[i].len * 4 / 5) fg = CUCUL_GREEN; else fg = CUCUL_DARKGRAY; cucul_set_color_ansi(cv, fg, CUCUL_BLACK); cucul_put_char(cv, x, y - j, drop[i].str[(y - j) % drop[i].len]); } } break; case FREE: break; } } vlock-2.2.2/modules/example_module.c000066400000000000000000000047541101377123600174620ustar00rootroot00000000000000/* example_module.c -- example module for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software. It comes without any warranty, to the extent permitted by * applicable law. You can redistribute it and/or modify it under the * terms of the Do What The Fuck You Want To Public License, Version 2, * as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING * for more details. */ #include #include #include #include #include /* Do not use any vlock specific headers here unless you intend to * submit your module for inclusion. */ /* Include this header file to make sure the types of the dependencies * and hooks are correct. */ #include "vlock_plugin.h" /* Declare dependencies. Please see PLUGINS for their meaning. Empty * dependencies can be left out. */ const char *preceeds[] = { "new", "all", NULL }; /* const char *succeeds[]; */ /* const char *requires[]; */ /* const char *needs[]; */ const char *depends[] = { "all", NULL }; /* const char *conflicts[]; */ /* Every hook has a void** argument ctx_ptr. When they are called * ctx_ptr points to the same location and *ctx_ptr is initially set to * NULL. Hook functions should pass state by defining a context struct * and saving the pointer to it in *ctx_ptr instead of using global * variables. */ struct example_context { int a; int b; }; /* Do something that should happen at vlock's start here. An error in * this hook aborts vlock. */ bool vlock_start(void **ctx_ptr) { struct example_context *ctx = malloc(sizeof *ctx); if (ctx == NULL) return false; ctx->a = 23; ctx->b = 42; /* Save the context for use by the other hooks. */ *ctx_ptr = ctx; return true; } /* Hooks that are not implemented should not be defined. */ /* Start a screensaver type action before the password prompt after a * timeout. This hook must not block! */ /* bool vlock_save(void **); */ /* Abort a screensaver type action before the password prompt after a * timeout. This hook must not block! */ /* bool vlock_save_abort(void **); */ /* Do something at the end of vlock. Error returns are ignored here. */ bool vlock_end(void **ctx_ptr) { struct example_context *ctx = *ctx_ptr; bool result = true; if (ctx != NULL) { result = (ctx->a == 23 && ctx->b == 42); free(ctx); if (!result) fprintf(stderr, "vlock-example_module: Whoops!\n"); } return result; } vlock-2.2.2/modules/new.c000066400000000000000000000137211101377123600152450ustar00rootroot00000000000000/* new.c -- console allocation plugin for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #else #include #endif #include "vlock_plugin.h" const char *preceeds[] = { "all", NULL }; const char *requires[] = { "all", NULL }; /* name of the virtual console device */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define CONSOLE "/dev/ttyv0" #else #define CONSOLE "/dev/tty0" #endif /* template for the device of a given virtual console */ #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define VTNAME "/dev/ttyv%x" #else #define VTNAME "/dev/tty%d" #endif /* Get the currently active console from the given * console file descriptor. Returns console number * (starting from 1) on success, -1 on error. */ #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) static int get_active_console(int consfd) { int n; if (ioctl(consfd, VT_GETACTIVE, &n) == 0) return n; else return -1; } #else static int get_active_console(int consfd) { struct vt_stat vtstate; /* get the virtual console status */ if (ioctl(consfd, VT_GETSTATE, &vtstate) == 0) return vtstate.v_active; else return -1; } #endif /* Get the device name for the given console number. * Returns the device name or NULL on error. */ static char *get_console_name(int n) { static char name[sizeof VTNAME + 2]; ssize_t namelen; if (n <= 0) return NULL; /* format the virtual terminal filename from the number */ #if defined(__FreeBSD__) || defined (__FreeBSD_kernel__) namelen = snprintf(name, sizeof name, VTNAME, n - 1); #else namelen = snprintf(name, sizeof name, VTNAME, n); #endif if (namelen > (ssize_t) sizeof name) { fprintf(stderr, "vlock-new: virtual terminal number too large\n"); return NULL; } else if (namelen < 0) { fprintf(stderr, "vlock-new: error calculating terminal device name: %s\n", strerror(errno)); return NULL; } else { return name; } } /* Change to the given console number using the given console * file descriptor. */ static int activate_console(int consfd, int vtno) { int c = ioctl(consfd, VT_ACTIVATE, vtno); return c < 0 ? c : ioctl(consfd, VT_WAITACTIVE, vtno); } struct new_console_context { int consfd; int old_vtno; int new_vtno; int saved_stdin; int saved_stdout; int saved_stderr; }; /* Run switch to a new console and redirect stdio there. */ bool vlock_start(void **ctx_ptr) { struct new_console_context *ctx; int vtfd; char *vtname; /* Allocate the context. */ if ((ctx = malloc(sizeof *ctx)) == NULL) return false; /* Try stdin first. */ ctx->consfd = dup(STDIN_FILENO); /* Get the number of the currently active console. */ ctx->old_vtno = get_active_console(ctx->consfd); if (ctx->old_vtno < 0) { /* stdin is does not a virtual console. */ (void) close(ctx->consfd); /* XXX: add optional PAM check here */ /* Open the virtual console directly. */ if ((ctx->consfd = open(CONSOLE, O_RDWR)) < 0) { perror("vlock-new: cannot open virtual console"); goto err; } /* Get the number of the currently active console, again. */ ctx->old_vtno = get_active_console(ctx->consfd); if (ctx->old_vtno < 0) { perror("vlock-new: could not get the currently active console"); goto err; } } /* Get a free virtual terminal number. */ if (ioctl(ctx->consfd, VT_OPENQRY, &ctx->new_vtno) < 0) { perror("vlock-new: could not find a free virtual terminal"); goto err; } /* Get the device name for the new virtual console. */ vtname = get_console_name(ctx->new_vtno); /* Open the free virtual terminal. */ if ((vtfd = open(vtname, O_RDWR)) < 0) { perror("vlock-new: cannot open new console"); goto err; } /* Work around stupid X11 bug: When switching immediately after the command * is entered, the enter button may get stuck. */ if (getenv("DISPLAY") != NULL) sleep(1); /* Switch to the new virtual terminal. */ if (activate_console(ctx->consfd, ctx->new_vtno) < 0) { perror("vlock-new: could not activate new terminal"); goto err; } /* Save the stdio file descriptors. */ ctx->saved_stdin = dup(STDIN_FILENO); ctx->saved_stdout = dup(STDOUT_FILENO); ctx->saved_stderr = dup(STDERR_FILENO); /* Redirect stdio to virtual terminal. */ (void) dup2(vtfd, STDIN_FILENO); (void) dup2(vtfd, STDOUT_FILENO); (void) dup2(vtfd, STDERR_FILENO); /* Close virtual terminal file descriptor. */ (void) close(vtfd); *ctx_ptr = ctx; return true; err: errno = 0; free(ctx); return false; } /* Redirect stdio back und switch to the previous console. */ bool vlock_end(void **ctx_ptr) { struct new_console_context *ctx = *ctx_ptr; if (ctx == NULL) return true; /* Restore saved stdio file descriptors. */ (void) dup2(ctx->saved_stdin, STDIN_FILENO); (void) dup2(ctx->saved_stdout, STDOUT_FILENO); (void) dup2(ctx->saved_stderr, STDERR_FILENO); /* Switch back to previous virtual terminal. */ if (activate_console(ctx->consfd, ctx->old_vtno) < 0) perror("vlock-new: could not activate previous console"); #if !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__) /* Deallocate virtual terminal. */ if (ioctl(ctx->consfd, VT_DISALLOCATE, ctx->new_vtno) < 0) perror("vlock-new: could not disallocate console"); #endif (void) close(ctx->consfd); free(ctx); return true; } vlock-2.2.2/modules/nosysrq.c000066400000000000000000000052701101377123600161720ustar00rootroot00000000000000/* nosysrq.c -- SysRq protection plugin for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include #include "vlock_plugin.h" const char *preceeds[] = { "new", "all", NULL }; const char *depends[] = { "all", NULL }; #define SYSRQ_PATH "/proc/sys/kernel/sysrq" #define SYSRQ_DISABLE_VALUE "0\n" struct sysrq_context { FILE *file; char value[32]; }; /* Disable SysRq and save old value in context. */ bool vlock_start(void **ctx_ptr) { struct sysrq_context *ctx; /* Allocate the context. */ if ((ctx = malloc(sizeof *ctx)) == NULL) return false; /* XXX: add optional PAM check here */ /* Open the SysRq sysctl file for reading and writing. */ if ((ctx->file = fopen(SYSRQ_PATH, "r+")) == NULL) { perror("vlock-nosysrq: could not open '" SYSRQ_PATH "'"); if (errno == ENOENT) goto nothing_to_do; else goto err; } /* Read the old value. */ if (fgets(ctx->value, sizeof ctx->value, ctx->file) == NULL) { perror("vlock-nosysrq: could not read from '" SYSRQ_PATH "'"); goto err; } /* Check whether all data was read. */ if (feof(ctx->file) != 0) { fprintf(stderr, "vlock-nosysrq: sysrq buffer to small: %zu\n", sizeof ctx->value); goto err; } /* Check if SysRq was already disabled. */ if (strcmp(SYSRQ_DISABLE_VALUE, ctx->value) == 0) goto nothing_to_do; /* Disable SysRq. */ if (fseek(ctx->file, 0, SEEK_SET) < 0 || ftruncate(fileno(ctx->file), 0) < 0 || fputs(SYSRQ_DISABLE_VALUE, ctx->file) < 0 || fflush(ctx->file) < 0) { perror("vlock-nosysrq: could not write disable value to '" SYSRQ_PATH "'"); goto err; } *ctx_ptr = ctx; return true; nothing_to_do: free(ctx); *ctx_ptr = NULL; return true; err: errno = 0; free(ctx); return false; } /* Restore old SysRq value. */ bool vlock_end(void **ctx_ptr) { struct sysrq_context *ctx = *ctx_ptr; if (ctx == NULL) return true; /* Restore SysRq. */ if (fseek(ctx->file, 0, SEEK_SET) < 0 || ftruncate(fileno(ctx->file), 0) < 0 || fputs(ctx->value, ctx->file) < 0 || fflush(ctx->file) < 0) perror("vlock-nosysrq: could not write old value to '" SYSRQ_PATH "'"); free(ctx); return true; } vlock-2.2.2/modules/ttyblank.c000066400000000000000000000017321101377123600163030ustar00rootroot00000000000000/* ttyblank.c -- a console blanking plugin for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include "vlock_plugin.h" const char *depends[] = { "all", NULL }; bool vlock_save(void __attribute__ ((__unused__)) ** ctx) { char arg[] = { TIOCL_BLANKSCREEN, 0 }; return ioctl(STDIN_FILENO, TIOCLINUX, arg) == 0; } bool vlock_save_abort(void __attribute__ ((__unused__)) ** ctx) { char arg[] = { TIOCL_UNBLANKSCREEN, 0 }; return ioctl(STDIN_FILENO, TIOCLINUX, arg) == 0; } vlock-2.2.2/modules/vesablank.c000066400000000000000000000020341101377123600164150ustar00rootroot00000000000000/* vesablank.c -- a console blanking plugin for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include "vlock_plugin.h" const char *depends[] = { "all", NULL }; const char *conflicts[] = { "blank", NULL }; bool vlock_save(void __attribute__ ((__unused__)) ** ctx) { char arg[] = { TIOCL_SETVESABLANK, 2 }; return ioctl(STDIN_FILENO, TIOCLINUX, arg) == 0; } bool vlock_save_abort(void __attribute__ ((__unused__)) ** ctx) { char arg[] = { TIOCL_SETVESABLANK, 0 }; return ioctl(STDIN_FILENO, TIOCLINUX, arg) == 0; } vlock-2.2.2/modules/vlock_plugin.h000066400000000000000000000015231101377123600171520ustar00rootroot00000000000000/* vlock_plugin.h -- header file for plugins for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include extern const char *preceeds[]; extern const char *succeeds[]; extern const char *requires[]; extern const char *needs[]; extern const char *depends[]; extern const char *conflicts[]; bool vlock_start(void **); bool vlock_end(void **); bool vlock_save(void **); bool vlock_save_abort(void **); vlock-2.2.2/scripts/000077500000000000000000000000001101377123600143235ustar00rootroot00000000000000vlock-2.2.2/scripts/Makefile000066400000000000000000000005701101377123600157650ustar00rootroot00000000000000include ../config.mk .PHONY: all all: $(SCRIPTS) .PHONY: install install: $(addprefix install-, $(SCRIPTS)) SCRIPT_GROUP = $(ROOT_GROUP) SCRIPT_MODE = 0755 install-%.sh : SCRIPT_TARGET=$(<:.sh=) install-%: % $(MKDIR_P) -m 755 $(DESTDIR)$(SCRIPTDIR) $(INSTALL) -m $(SCRIPT_MODE) -o root -g $(SCRIPT_GROUP) $< $(DESTDIR)$(SCRIPTDIR)/$(SCRIPT_TARGET) .PHONY: clean clean: vlock-2.2.2/scripts/alsa_mute.sh000066400000000000000000000021331101377123600166300ustar00rootroot00000000000000#!/bin/sh # alsa_mute.sh -- alsa muting script for vlock, # the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e DEPENDS="all" hooks() { while read hook_name ; do case "${hook_name}" in vlock_start) amixer -q set Master mute ;; vlock_end) amixer -q set Master unmute ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/amarok.sh000066400000000000000000000024411101377123600161320ustar00rootroot00000000000000#!/bin/sh # amarok.sh -- amarok pausing script for vlock, # the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e DEPENDS="all" hooks() { while read hook_name ; do case "${hook_name}" in vlock_start) amarok_status=$(dcop amarok player isPlaying 2>/dev/null || echo false) if [ "${amarok_status}" = "true" ] ; then dcop amarok player pause fi ;; vlock_end) if [ "${amarok_status}" = "true" ] ; then dcop amarok player play fi ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/example_script.sh000066400000000000000000000033131101377123600176760ustar00rootroot00000000000000#!/bin/sh # example_script.sh -- example script for vlock, # the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e # Declare dependencies. Please see PLUGINS for their meaning. Empty # dependencies can be left out. PRECEEDS="new all" # SUCCEEDS="" # REQUIRES="" # NEEDS="" DEPENDS="all" # CONFLICTS="" hooks() { # The name of the hook that should be executed is read as a string from # stdin. This function should only exit when stdin hits end-of-file. while read hook_name ; do case "${hook_name}" in vlock_start) # do something here that should happen at the start of vlock ;; vlock_end) # do something here that should happen at the end of vlock ;; vlock_save) # start a screensaver type action here ;; vlock_save_abort) # abort a screensaver type action here ;; esac done } # Everything below is boilerplate code that shouldn't need to be changed. if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/hibernate.sh000066400000000000000000000022361101377123600166230ustar00rootroot00000000000000#!/bin/sh # hibernate.sh -- hibernate script for vlock, # the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e REQUIRES="all" CONFLICTS="new" PRECEEDS="all" hooks() { oldvt=$(fgconsole) while read hook_name ; do case "${hook_name}" in vlock_start) chvt 63 ;; vlock_end) chvt "${oldvt}" ;; vlock_save) hibernate ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/mplayer.sh000066400000000000000000000027111101377123600163310ustar00rootroot00000000000000#!/bin/sh # mplayer.sh -- mplayer pausing script for vlock, # the VT locking program for linux # # To use this script do run the following command # $ mkfifo ~/.mplayer/control # and add # input=file=/home//.mplayer/control # to your ~/.mplayer/config. # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e DEPENDS="all" hooks() { while read hook_name ; do case "${hook_name}" in vlock_start) if fuser "${HOME}/.mplayer/control" > /dev/null 2>&1 ; then echo "pausing seek -4" > "${HOME}/.mplayer/control" fi ;; vlock_end) if fuser "${HOME}/.mplayer/control" > /dev/null 2>&1 ; then echo "pause" > "${HOME}/.mplayer/control" fi ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/powerbook_backlight.sh000066400000000000000000000032561101377123600207040ustar00rootroot00000000000000#!/bin/sh ########################################################################### # powerbook_backlight.sh, version 0.1.1 # 2007-10-15 # # This vlock plugin script switches off the backlight on Apple Powerbook # laptops while locking. It requires the fblevel utility to be installed. # # Copyright (C) 2007 Rene Kuettner # # 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; version 2. # # 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 # ########################################################################### set -e DEPENDS="all" hooks() { while read hook_name ; do case "${hook_name}" in vlock_save) sudo fblevel off >/dev/null ;; vlock_save_abort) sudo fblevel on >/dev/null ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/scripts/thinkpad_light.sh000066400000000000000000000025351101377123600176550ustar00rootroot00000000000000#!/bin/sh # thinkpad_light.sh -- ThinkLight script for vlock, # the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free software. It # comes without any warranty, to the extent permitted by applicable law. You # can redistribute it and/or modify it under the terms of the Do What The Fuck # You Want To Public License, Version 2, as published by Sam Hocevar. See # http://sam.zoy.org/wtfpl/COPYING for more details. set -e DEPENDS="all" hooks() { while read hook_name ; do case "${hook_name}" in vlock_save) light_status=$(awk '/^status:/ {print $2}' /proc/acpi/ibm/light) if [ "${light_status}" = "on" ] ; then echo off | sudo tee /proc/acpi/ibm/light >/dev/null fi ;; vlock_save_abort) if [ "${light_status}" = "on" ] ; then echo on | sudo tee /proc/acpi/ibm/light >/dev/null fi ;; esac done } if [ $# -ne 1 ] ; then echo >&2 "Usage: $0 " exit 1 fi case "$1" in hooks) hooks ;; preceeds) echo "${PRECEEDS}" ;; succeeds) echo "${SUCCEEDS}" ;; requires) echo "${REQUIRES}" ;; needs) echo "${NEEDS}" ;; depends) echo "${DEPENDS}" ;; conflicts) echo "${CONFLICTS}" ;; *) echo >&2 "$0: unknown command '$1'" exit 1 ;; esac vlock-2.2.2/src/000077500000000000000000000000001101377123600134235ustar00rootroot00000000000000vlock-2.2.2/src/auth-pam.c000066400000000000000000000116061101377123600153070ustar00rootroot00000000000000/* auth-pam.c -- PAM authentification routine for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * * * The conversation function (conversation) was inspired by/copied from * openpam's openpam_ttyconv.c: * * Copyright (c) 2002-2003 Networks Associates Technology, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include "auth.h" #include "prompt.h" static int conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { struct pam_response *aresp; struct timespec *timeout = appdata_ptr; if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG) return PAM_CONV_ERR; if ((aresp = calloc((size_t) num_msg, sizeof *aresp)) == NULL) return PAM_BUF_ERR; for (int i = 0; i < num_msg; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: aresp[i].resp = prompt_echo_off(msg[i]->msg, timeout); if (aresp[i].resp == NULL) goto fail; break; case PAM_PROMPT_ECHO_ON: aresp[i].resp = prompt(msg[i]->msg, timeout); if (aresp[i].resp == NULL) goto fail; break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: { size_t msg_len = strlen(msg[i]->msg); (void) fputs(msg[i]->msg, stderr); if (msg_len > 0 && msg[i]->msg[msg_len - 1] != '\n') (void) fputc('\n', stderr); } break; default: goto fail; } } *resp = aresp; return PAM_SUCCESS; fail: for (int i = 0; i < num_msg; ++i) { if (aresp[i].resp != NULL) { memset(aresp[i].resp, 0, strlen(aresp[i].resp)); free(aresp[i].resp); } } memset(aresp, 0, num_msg * sizeof *aresp); free(aresp); *resp = NULL; return PAM_CONV_ERR; } bool auth(const char *user, struct timespec *timeout) { char *pam_tty; pam_handle_t *pamh; int pam_status; int pam_end_status; struct pam_conv pamc = { .conv = conversation, .appdata_ptr = timeout, }; /* initialize pam */ pam_status = pam_start("vlock", user, &pamc, &pamh); if (pam_status != PAM_SUCCESS) { fprintf(stderr, "vlock: %s\n", pam_strerror(pamh, pam_status)); goto end; } /* get the name of stdin's tty device, if any */ pam_tty = ttyname(STDIN_FILENO); /* set PAM_TTY */ if (pam_tty != NULL) { pam_status = pam_set_item(pamh, PAM_TTY, pam_tty); if (pam_status != PAM_SUCCESS) { fprintf(stderr, "vlock: %s\n", pam_strerror(pamh, pam_status)); goto end; } } /* put the username before the password prompt */ fprintf(stderr, "%s's ", user); fflush(stderr); /* authenticate the user */ pam_status = pam_authenticate(pamh, 0); if (pam_status != PAM_SUCCESS) { fprintf(stderr, "vlock: %s\n", pam_strerror(pamh, pam_status)); } end: /* finish pam */ pam_end_status = pam_end(pamh, pam_status); if (pam_end_status != PAM_SUCCESS) { fprintf(stderr, "vlock: %s\n", pam_strerror(pamh, pam_end_status)); } return (pam_status == PAM_SUCCESS); } vlock-2.2.2/src/auth-shadow.c000066400000000000000000000032231101377123600160130ustar00rootroot00000000000000/* auth-shadow.c -- shadow authentification routine for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ /* for crypt() */ #define _XOPEN_SOURCE #ifndef __FreeBSD__ /* for asprintf() */ #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include "auth.h" #include "prompt.h" bool auth(const char *user, struct timespec *timeout) { char *pwd; char *cryptpw; char *msg; struct spwd *spw; int result = false; /* format the prompt */ if (asprintf(&msg, "%s's Password: ", user) < 0) return false; if ((pwd = prompt_echo_off(msg, timeout)) == NULL) goto out_pwd; /* get the shadow password */ if ((spw = getspnam(user)) == NULL) goto out_shadow; /* hash the password */ if ((cryptpw = crypt(pwd, spw->sp_pwdp)) == NULL) { perror("vlock: crypt()"); goto out_shadow; } result = (strcmp(cryptpw, spw->sp_pwdp) == 0); if (!result) { sleep(1); fprintf(stderr, "vlock: Authentication error\n"); } out_shadow: /* deallocate shadow resources */ endspent(); /* free the password */ free(pwd); out_pwd: /* free the prompt */ free(msg); return result; } vlock-2.2.2/src/auth.h000066400000000000000000000016521101377123600145410ustar00rootroot00000000000000/* auth.h -- generic header file for authentification routines * for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include /* forward declaration */ struct timespec; /* Try to authenticate the user. When the user is successfully authenticated * this function returns true. When the authentication fails for whatever * reason the function returns false. The timeout is passed to the prompt * functions below if they are called. */ bool auth(const char *user, struct timespec *timeout); vlock-2.2.2/src/console_switch.c000066400000000000000000000074111101377123600166150ustar00rootroot00000000000000/* console_switch.c -- console grabbing routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #if !defined(__FreeBSD__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include #else #include #endif #include "console_switch.h" /* Is console switching currently disabled? */ bool console_switch_locked = false; /* This handler is called whenever a user tries to * switch away from this virtual console. */ static void release_vt(int __attribute__ ((__unused__)) signum) { /* Deny console switch. */ (void) ioctl(STDIN_FILENO, VT_RELDISP, 0); } /* This handler is called whenever a user switches to this * virtual console. */ static void acquire_vt(int __attribute__ ((__unused__)) signum) { /* Acknowledge console switch. */ (void) ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ); } /* Console mode before switching was disabled. */ static struct vt_mode vtm; /* Signal actions before console handling was disabled. */ static struct sigaction sa_usr1; static struct sigaction sa_usr2; /* Disable virtual console switching in the kernel. If disabling fails false * is returned and errno is set. */ bool lock_console_switch(void) { /* Console mode when switching is disabled. */ struct vt_mode lock_vtm; struct sigaction sa; /* Get the virtual console mode. */ if (ioctl(STDIN_FILENO, VT_GETMODE, &vtm) < 0) { if (errno == ENOTTY || errno == EINVAL) fprintf(stderr, "vlock: this terminal is not a virtual console\n"); else perror("vlock: could not get virtual console mode"); errno = 0; return false; } /* Copy the current virtual console mode. */ lock_vtm = vtm; (void) sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; sa.sa_handler = release_vt; (void) sigaction(SIGUSR1, &sa, &sa_usr1); sa.sa_handler = acquire_vt; (void) sigaction(SIGUSR2, &sa, &sa_usr2); /* Set terminal switching to be process governed. */ lock_vtm.mode = VT_PROCESS; /* Set terminal release signal, i.e. sent when switching away. */ lock_vtm.relsig = SIGUSR1; /* Set terminal acquire signal, i.e. sent when switching here. */ lock_vtm.acqsig = SIGUSR2; /* Set terminal free signal, not implemented on either FreeBSD or Linux. */ /* Linux ignores this but FreeBSD wants a valid signal number here. */ lock_vtm.frsig = SIGHUP; /* Set virtual console mode to be process governed thus disabling console * switching through the signal handlers above. */ if (ioctl(STDIN_FILENO, VT_SETMODE, &lock_vtm) < 0) { perror("vlock: disabling console switching failed"); /* Reset signal handlers. */ (void) sigaction(SIGUSR1, &sa_usr1, NULL); (void) sigaction(SIGUSR2, &sa_usr2, NULL); errno = 0; return false; } console_switch_locked = true; return true; } /* Reenable console switching if it was previously disabled. */ bool unlock_console_switch(void) { if (ioctl(STDIN_FILENO, VT_SETMODE, &vtm) == 0) { /* Reset signal handlers. */ (void) sigaction(SIGUSR1, &sa_usr1, NULL); (void) sigaction(SIGUSR2, &sa_usr2, NULL); return true; } else { perror("vlock: reenabling console switch failed"); errno = 0; return false; } } vlock-2.2.2/src/console_switch.h000066400000000000000000000017211101377123600166200ustar00rootroot00000000000000/* console_switch.c -- header file for the console grabbing * routines for vlock, the VT locking program * for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include /* Is console switching currently disabled? */ extern bool console_switch_locked; /* Disable virtual console switching in the kernel. On failure false * is returned and errno is set. */ bool lock_console_switch(void); /* Reenable console switching if it was previously disabled. On failure false * is returned and errno is set. */ bool unlock_console_switch(void); vlock-2.2.2/src/list.c000066400000000000000000000055551101377123600145540ustar00rootroot00000000000000/* list.c -- doubly linked list routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include "util.h" #include "list.h" /* Create a new, empty list. */ struct list *list_new(void) { struct list *l = malloc(sizeof *l); if (l == NULL) return NULL; l->first = NULL; l->last = NULL; return l; } /* Create a (shallow) copy of the given list. */ struct list *list_copy(struct list *l) { struct list *new_list = list_new(); if (new_list == NULL) return NULL; list_for_each(l, item) if (!list_append(new_list, item->data)) { list_free(new_list); return NULL; } return new_list; } /* Deallocate the given list and all items. */ void list_free(struct list *l) { list_for_each_manual(l, item) { struct list_item *tmp = item->next; free(item); item = tmp; } free(l); } /* Calculate the number of items in the given list. */ size_t list_length(struct list *l) { size_t length = 0; list_for_each(l, item) length++; return length; } /* Create a new list item with the given data and add it to the end of the * list. */ bool list_append(struct list *l, void *data) { struct list_item *item = malloc(sizeof *item); if (item == NULL) return false; item->data = data; item->previous = l->last; item->next = NULL; if (l->last != NULL) l->last->next = item; l->last = item; if (l->first == NULL) l->first = item; return true; } /* Remove the given item from the list. Returns the item following the deleted * one or NULL if the given item was the last. */ struct list_item *list_delete_item(struct list *l, struct list_item *item) { struct list_item *next = item->next; if (item->previous != NULL) item->previous->next = item->next; if (item->next != NULL) item->next->previous = item->previous; if (l->first == item) l->first = item->next; if (l->last == item) l->last = item->previous; free(item); return next; } /* Remove the first item with the given data. Does nothing if no item has this * data. */ void list_delete(struct list *l, void *data) { struct list_item *item = list_find(l, data); if (item != NULL) (void) list_delete_item(l, item); } /* Find the first item with the given data. Returns NULL if no item has this * data. */ struct list_item *list_find(struct list *l, void *data) { list_for_each(l, item) if (item->data == data) return item; return NULL; } vlock-2.2.2/src/list.h000066400000000000000000000054541101377123600145570ustar00rootroot00000000000000/* list.h -- doubly linked list header file for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include /* Single list item. */ struct list_item { void *data; struct list_item *next; struct list_item *previous; }; /* Whole list. */ struct list { struct list_item *first; struct list_item *last; }; /* Create a new, empty list. */ struct list *list_new(void); /* Create a (shallow) copy of the given list. */ struct list *list_copy(struct list *l); /* Deallocate the given list and all items. */ void list_free(struct list *l); /* Calculate the number of items in the given list. */ size_t list_length(struct list *l); /* Create a new list item with the given data and add it to the end of the * list. */ bool list_append(struct list *l, void *data); /* Remove the given item from the list. Returns the item following the deleted * one or NULL if the given item was the last. */ struct list_item *list_delete_item(struct list *l, struct list_item *item); /* Remove the first item with the given data. Does nothing if no item has this * data. */ void list_delete(struct list *l, void *data); /* Find the first item with the given data. Returns NULL if no item has this * data. */ struct list_item *list_find(struct list *l, void *data); /* Generic list iteration macro. */ #define list_for_each_from_increment(list, item, start, increment) \ for (struct list_item *item = (start); item != NULL; (increment)) /* Iterate over the whole list. */ #define list_for_each(list, item) \ list_for_each_from_increment((list), item, (list)->first, item = item->next) /* Iterate over the list while deleting every item after the iteration. */ #define list_delete_for_each(list, item) \ list_for_each_from_increment((list), item, (list)->first, item = list_delete_item((list), item)) /* Iterate over the list. Incrementation must be done manually. */ #define list_for_each_manual(list, item) \ for (struct list_item *item = (list)->first; item != NULL;) /* Iterate backwards over list from the given start. */ #define list_for_each_reverse_from(list, item, start) \ list_for_each_from_increment((list), item, (start), item = item->previous) /* Iterate backwards over the whole list. */ #define list_for_each_reverse(list, item) \ list_for_each_reverse_from((list), item, (list)->last) static inline bool list_is_empty(struct list *l) { return l->first == NULL; } vlock-2.2.2/src/module.c000066400000000000000000000077211101377123600150630ustar00rootroot00000000000000/* module.c -- module routines for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ /* Modules are shared objects that are loaded into vlock's address space. */ /* They can define certain functions that are called through vlock's plugin * mechanism. They should also define dependencies if they depend on other * plugins of have to be called before or after other plugins. */ #if !defined(__FreeBSD__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include "list.h" #include "util.h" #include "plugin.h" static bool init_module(struct plugin *p); static void destroy_module(struct plugin *p); static bool call_module_hook(struct plugin *p, const char *hook_name); struct plugin_type *module = &(struct plugin_type){ .init = init_module, .destroy = destroy_module, .call_hook = call_module_hook, }; /* A hook function as defined by a module. */ typedef bool (*module_hook_function)(void **); struct module_context { /* Handle returned by dlopen(). */ void *dl_handle; /* Pointer to be used by the modules. */ void *module_data; /* Array of hook functions befined by a single module. Stored in the same * order as the global hooks. */ module_hook_function hooks[nr_hooks]; }; /* Initialize a new plugin as a module. */ bool init_module(struct plugin *p) { char *path; struct module_context *context; if (asprintf(&path, "%s/%s.so", VLOCK_MODULE_DIR, p->name) < 0) { errno = ENOMEM; return false; } /* Test for access. This must be done manually because vlock most likely * runs as a setuid executable and would otherwise override restrictions. * Also dlopen doesn't set errno on error. */ if (access(path, R_OK) < 0) { int errsv = errno; free(path); errno = errsv; return false; } context = malloc(sizeof *context); if (context == NULL) { int errsv = errno; free(path); errno = errsv; return false; } /* Open the module as a shared library. */ context->dl_handle = dlopen(path, RTLD_NOW | RTLD_LOCAL); free(path); if (context->dl_handle == NULL) { errno = 0; free(context); return false; } /* Initialisation complete. From now on cleanup is handled by destroy_module(). */ p->context = context; /* Load all the hooks. Unimplemented hooks are NULL and will not be called later. */ for (size_t i = 0; i < nr_hooks; i++) *(void **) (&context->hooks[i]) = dlsym(context->dl_handle, hooks[i].name); /* Load all dependencies. Unspecified dependencies are NULL. */ for (size_t i = 0; i < nr_dependencies; i++) { const char *(*dependency)[] = dlsym(context->dl_handle, dependency_names[i]); /* Append array elements to list. */ for (size_t j = 0; dependency != NULL && (*dependency)[j] != NULL; j++) { char *s = strdup((*dependency)[j]); if (s == NULL) return false; list_append(p->dependencies[i], s); } } return true; } static void destroy_module(struct plugin *p) { struct module_context *context = p->context; if (context != NULL) { dlclose(context->dl_handle); free(context); } } static bool call_module_hook(struct plugin *p, const char *hook_name) { struct module_context *context = p->context; /* Find the right hook index. */ for (size_t i = 0; i < nr_hooks; i++) if (strcmp(hooks[i].name, hook_name) == 0) { module_hook_function hook = context->hooks[i]; if (hook != NULL) return hook(&context->module_data); } return true; } vlock-2.2.2/src/plugin.c000066400000000000000000000034251101377123600150710ustar00rootroot00000000000000/* plugin.c -- generic plugin routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include "list.h" #include "plugin.h" #include "util.h" /* Allocate a new plugin struct. */ struct plugin *new_plugin(const char *name, struct plugin_type *type) { struct plugin *p = malloc(sizeof *p); char *last_slash; if (p == NULL) return NULL; /* For security plugin names must not contain a slash. */ last_slash = strrchr(name, '/'); if (last_slash != NULL) name = last_slash+1; p->name = strdup(name); if (p->name == NULL) { free(p); return NULL; } p->context = NULL; p->save_disabled = false; for (size_t i = 0; i < nr_dependencies; i++) p->dependencies[i] = list_new(); p->type = type; if (p->type->init(p)) { return p; } else { destroy_plugin(p); return NULL; } } /* Destroy the given plugin. */ void destroy_plugin(struct plugin *p) { /* Call destroy method. */ p->type->destroy(p); /* Destroy dependency lists. */ for (size_t i = 0; i < nr_dependencies; i++) { list_delete_for_each(p->dependencies[i], dependency_item) free(dependency_item->data); list_free(p->dependencies[i]); } free(p->name); free(p); } bool call_hook(struct plugin *p, const char *hook_name) { return p->type->call_hook(p, hook_name); } vlock-2.2.2/src/plugin.h000066400000000000000000000046641101377123600151040ustar00rootroot00000000000000/* plugin.h -- header file for the generic plugin routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include /* Names of dependencies plugins may specify. */ #define nr_dependencies 6 extern const char *dependency_names[nr_dependencies]; /* A plugin hook consists of a name and a handler function. */ struct hook { const char *name; void (*handler)(const char *); }; /* Hooks that a plugin may define. */ #define nr_hooks 4 extern const struct hook hooks[nr_hooks]; struct plugin_type; /* Struct representing a plugin instance. */ struct plugin { /* The name of the plugin. */ char *name; /* Array of dependencies. Each dependency is a (possibly empty) list of * strings. The dependencies must be stored in the same order as the * dependency names above. The strings a freed when the plugin is destroyed * thus must be stored as copies. */ struct list *dependencies[nr_dependencies]; /* Did one of the save hooks fail? */ bool save_disabled; /* The type of the plugin. */ struct plugin_type *type; /* The call_hook and close functions may use this pointer. */ void *context; }; /* Struct representing the type of a plugin. */ struct plugin_type { /* Method that is called on plugin initialization. */ bool (*init)(struct plugin *p); /* Method that is called on plugin destruction. */ void (*destroy)(struct plugin *p); /* Method that is called when a hook should be executed. */ bool (*call_hook)(struct plugin *p, const char *name); }; /* Modules. */ extern struct plugin_type *module; /* Scripts. */ extern struct plugin_type *script; /* Open a new plugin struct of the given type. On error errno is set and NULL * is returned. */ struct plugin *new_plugin(const char *name, struct plugin_type *type); /* Destroy the given plugin. This is the opposite of of __allocate_plugin. * This function should not be called directly. */ void destroy_plugin(struct plugin *p); /* Call the hook of a plugin. */ bool call_hook(struct plugin *p, const char *hook_name); vlock-2.2.2/src/plugins.c000066400000000000000000000245621101377123600152610ustar00rootroot00000000000000/* plugins.c -- plugins for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include "plugins.h" #include "list.h" #include "tsort.h" #include "plugin.h" #include "util.h" /* the list of plugins */ static struct list *plugins = &(struct list){ NULL, NULL }; /****************/ /* dependencies */ /****************/ #define SUCCEEDS 0 #define PRECEEDS 1 #define REQUIRES 2 #define NEEDS 3 #define DEPENDS 4 #define CONFLICTS 5 const char *dependency_names[nr_dependencies] = { "succeeds", "preceeds", "requires", "needs", "depends", "conflicts", }; /*********/ /* hooks */ /*********/ static void handle_vlock_start(const char * hook_name); static void handle_vlock_end(const char * hook_name); static void handle_vlock_save(const char * hook_name); static void handle_vlock_save_abort(const char * hook_name); const struct hook hooks[nr_hooks] = { { "vlock_start", handle_vlock_start }, { "vlock_end", handle_vlock_end }, { "vlock_save", handle_vlock_save }, { "vlock_save_abort", handle_vlock_save_abort }, }; /**********************/ /* exported functions */ /**********************/ /* helper declarations */ static struct plugin *__load_plugin(const char *name); static bool __resolve_depedencies(void); static bool sort_plugins(void); bool load_plugin(const char *name) { return __load_plugin(name) != NULL; } bool resolve_dependencies(void) { return __resolve_depedencies() && sort_plugins(); } void unload_plugins(void) { list_delete_for_each(plugins, plugin_item) destroy_plugin(plugin_item->data); } void plugin_hook(const char *hook_name) { for (size_t i = 0; i < nr_hooks; i++) /* Get the handler and call it. */ if (strcmp(hook_name, hooks[i].name) == 0) { hooks[i].handler(hook_name); break; } } /********************/ /* helper functions */ /********************/ static struct plugin *get_plugin(const char *name) { list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; if (strcmp(name, p->name) == 0) return p; } return NULL; } /* Load and return the named plugin. */ static struct plugin *__load_plugin(const char *name) { struct plugin *p = get_plugin(name); if (p != NULL) return p; /* Try to open a module first. */ p = new_plugin(name, module); if (p != NULL) goto success; if (errno != ENOENT) return NULL; /* Now try to open a script. */ p = new_plugin(name, script); if (p == NULL) return NULL; success: if (!list_append(plugins, p)) { GUARD_ERRNO(destroy_plugin(p)); return NULL; } return p; } /* Resolve the dependencies of the plugins. */ static bool __resolve_depedencies(void) { struct list *required_plugins = list_new(); /* Load plugins that are required. This automagically takes care of plugins * that are required by the plugins loaded here because they are appended to * the end of the list. */ list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; list_for_each(p->dependencies[REQUIRES], dependency_item) { const char *d = dependency_item->data; struct plugin *q = __load_plugin(d); if (q == NULL) { int errsv = errno; fprintf(stderr, "vlock-plugins: '%s' requires '%s' which could not be loaded\n", p->name, d); list_free(required_plugins); errno = errsv; return false; } if (!list_append(required_plugins, q)) { GUARD_ERRNO(list_free(required_plugins)); return false; } } } /* Fail if a plugins that is needed is not loaded. */ list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; list_for_each(p->dependencies[NEEDS], dependency_item) { const char *d = dependency_item->data; struct plugin *q = get_plugin(d); if (q == NULL) { fprintf(stderr, "vlock-plugins: '%s' needs '%s' which is not loaded\n", p->name, d); list_free(required_plugins); errno = 0; return false; } if (!list_append(required_plugins, q)) { GUARD_ERRNO(list_free(required_plugins)); return false; } } } /* Unload plugins whose prerequisites are not present, fail if those plugins * are required. */ list_for_each_manual(plugins, plugin_item) { struct plugin *p = plugin_item->data; bool dependencies_loaded = true; list_for_each(p->dependencies[DEPENDS], dependency_item) { const char *d = dependency_item->data; struct plugin *q = get_plugin(d); if (q == NULL) { dependencies_loaded = false; /* Abort if dependencies not met and plugin is required. */ if (list_find(required_plugins, p) != NULL) { fprintf(stderr, "vlock-plugins: '%s' is required by some other plugin\n" " but depends on '%s' which is not loaded", p->name, d); list_free(required_plugins); errno = 0; return false; } break; } } if (dependencies_loaded) { plugin_item = plugin_item->next; } else { plugin_item = list_delete_item(plugins, plugin_item); destroy_plugin(p); } } list_free(required_plugins); /* Fail if conflicting plugins are loaded. */ list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; list_for_each(p->dependencies[CONFLICTS], dependency_item) { const char *d = dependency_item->data; if (get_plugin(d) != NULL) { fprintf(stderr, "vlock-plugins: '%s' and '%s' cannot be loaded at the same time\n", p->name, d); errno = 0; return false; } } } return true; } static struct list *get_edges(void); /* Sort the list of plugins according to their "preceeds" and "succeeds" * dependencies. Fails if sorting is not possible because of circles. */ static bool sort_plugins(void) { struct list *edges = get_edges(); struct list *sorted_plugins; if (edges == NULL) return false; /* Topological sort. */ sorted_plugins = tsort(plugins, edges); if (sorted_plugins == NULL && errno != 0) return false; list_delete_for_each(edges, edge_item) { struct edge *e = edge_item->data; struct plugin *p = e->predecessor; struct plugin *s = e->successor; fprintf(stderr, "\t%s\tmust come before\t%s\n", p->name, s->name); free(e); } if (sorted_plugins != NULL) { /* Switch the global list of plugins for the sorted list. The global list * is static and cannot be freed. */ struct list_item *first = sorted_plugins->first; struct list_item *last = sorted_plugins->last; sorted_plugins->first = plugins->first; sorted_plugins->last = plugins->last; plugins->first = first; plugins->last = last; list_free(sorted_plugins); return true; } else { fprintf(stderr, "vlock-plugins: circular dependencies detected\n"); return false; } } static bool append_edge(struct list *edges, struct plugin *p, struct plugin *s) { struct edge *e = malloc(sizeof *e); if (e == NULL) return false; e->predecessor = p; e->successor = s; if (!list_append(edges, e)) { GUARD_ERRNO(free(e)); return false; } return true; } /* Get the edges of the plugin graph specified by each plugin's "preceeds" and * "succeeds" dependencies. */ static struct list *get_edges(void) { struct list *edges = list_new(); if (edges == NULL) return NULL; list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; /* p must come after these */ list_for_each(p->dependencies[SUCCEEDS], predecessor_item) { struct plugin *q = get_plugin(predecessor_item->data); if (q != NULL) if (!append_edge(edges, q, p)) goto error; } /* p must come before these */ list_for_each(p->dependencies[PRECEEDS], successor_item) { struct plugin *q = get_plugin(successor_item->data); if (q != NULL) if (!append_edge(edges, p, q)) goto error; } } return edges; error: GUARD_ERRNO(list_free(edges)); return NULL; } /************/ /* handlers */ /************/ /* Call the "vlock_start" hook of each plugin. Fails if the hook of one of the * plugins fails. In this case the "vlock_end" hooks of all plugins that were * called before are called in reverse order. */ void handle_vlock_start(const char *hook_name) { list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; if (!call_hook(p, hook_name)) { int errsv = errno; list_for_each_reverse_from(plugins, reverse_item, plugin_item->previous) { struct plugin *r = reverse_item->data; (void) call_hook(r, "vlock_end"); } if (errsv) fprintf(stderr, "vlock: plugin '%s' failed: %s\n", p->name, strerror(errsv)); exit(EXIT_FAILURE); } } } /* Call the "vlock_end" hook of each plugin in reverse order. Never fails. */ void handle_vlock_end(const char *hook_name) { list_for_each_reverse(plugins, plugin_item) { struct plugin *p = plugin_item->data; (void) call_hook(p, hook_name); } } /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a * plugin fails its "vlock_save_abort" hook is called and both hooks are never * called again afterwards. */ void handle_vlock_save(const char *hook_name) { list_for_each(plugins, plugin_item) { struct plugin *p = plugin_item->data; if (p->save_disabled) continue; if (!call_hook(p, hook_name)) { p->save_disabled = true; (void) call_hook(p, "vlock_save_abort"); } } } /* Call the "vlock_save" hook of each plugin. Never fails. If the hook of a * plugin fails both hooks "vlock_save" and "vlock_save_abort" are never called * again afterwards. */ void handle_vlock_save_abort(const char *hook_name) { list_for_each_reverse(plugins, plugin_item) { struct plugin *p = plugin_item->data; if (p->save_disabled) continue; if (!call_hook(p, hook_name)) p->save_disabled = true; } } vlock-2.2.2/src/plugins.h000066400000000000000000000016371101377123600152640ustar00rootroot00000000000000/* plugins.h -- plugins header file for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include /* Load the named plugin. */ bool load_plugin(const char *name); /* Resolve all the dependencies between all plugins. This function *must* be * called after all plugins were loaded. This function aborts on error. */ bool resolve_dependencies(void); /* Unload all plugins. */ void unload_plugins(void); /* Call the given plugin hook. */ void plugin_hook(const char *hook_name); vlock-2.2.2/src/process.c000066400000000000000000000155521101377123600152550ustar00rootroot00000000000000/* process.c -- child process routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include #include #include #include #include #include "process.h" /* Do nothing. */ static void ignore_sigalarm(int __attribute__((unused)) signum) { } bool wait_for_death(pid_t pid, long sec, long usec) { int status; struct sigaction act; struct sigaction oldact; struct itimerval timer; struct itimerval otimer; bool result; /* Ignore SIGALRM. The handler must be a real function instead of SIG_IGN * otherwise waitpid() would not get interrupted. * * There is a small window here where a previously set alarm might be * ignored. */ sigemptyset(&act.sa_mask); act.sa_handler = ignore_sigalarm; act.sa_flags = 0; sigaction(SIGALRM, &act, &oldact); /* Initialize the timer. */ timer.it_value.tv_sec = sec; timer.it_value.tv_usec = usec; /* No repetition. */ timer.it_interval.tv_sec = 0; timer.it_interval.tv_usec = 0; /* Set the timer. */ setitimer(ITIMER_REAL, &timer, &otimer); /* Wait until the child exits or the timer fires. */ result = (waitpid(pid, &status, 0) == pid); /* Possible race condition. If an alarm was set before it may get ignored. * This is probably better than getting killed by our own alarm. */ /* Restore the timer. */ setitimer(ITIMER_REAL, &otimer, NULL); /* Restore signal handler for SIGALRM. */ sigaction(SIGALRM, &oldact, NULL); return result; } /* Try hard to kill the given child process. */ void ensure_death(pid_t pid) { int status; switch (waitpid(pid, &status, WNOHANG)) { case -1: /* Not your child? */ return; case 0: /* Not dead yet. Continue. */ break; default: /* Already dead. Nothing to do. */ return; } /* Send SIGTERM. */ (void) kill(pid, SIGTERM); /* SIGTERM handler (if any) has 500ms to finish. */ if (wait_for_death(pid, 0, 500000L)) return; // Send SIGKILL. */ (void) kill(pid, SIGKILL); /* Child may be stopped. Send SIGCONT just to be sure. */ (void) kill(pid, SIGCONT); /* Wait until dead. Shouldn't take long. */ (void) waitpid(pid, &status, 0); } /* Close all possibly open file descriptors except the ones specified in the * given set. */ static void close_fds(fd_set *except_fds) { struct rlimit r; int maxfd; /* Get the maximum number of file descriptors. */ if (getrlimit(RLIMIT_NOFILE, &r) == 0) maxfd = r.rlim_cur; else /* Hopefully safe default. */ maxfd = 1024; /* Close all possibly open file descriptors except STDIN_FILENO, * STDOUT_FILENO and STDERR_FILENO. */ for (int fd = 0; fd < maxfd; fd++) if (!FD_ISSET(fd, except_fds)) (void) close(fd); } static int open_devnull(void) { static int devnull_fd = -1; if (devnull_fd < 0) devnull_fd = open("/dev/null", O_RDWR); return devnull_fd; } bool create_child(struct child_process *child) { int errsv; int child_errno = 0; int status_pipe[2]; int stdin_pipe[2]; int stdout_pipe[2]; int stderr_pipe[2]; if (pipe(status_pipe) < 0) return false; (void) fcntl(status_pipe[1], F_SETFD, FD_CLOEXEC); if (child->stdin_fd == REDIRECT_PIPE) { if (pipe(stdin_pipe) < 0) { errsv = errno; goto stdin_pipe_failed; } } if (child->stdout_fd == REDIRECT_PIPE) { if (pipe(stdout_pipe) < 0) { errsv = errno; goto stdout_pipe_failed; } } if (child->stderr_fd == REDIRECT_PIPE) { if (pipe(stderr_pipe) < 0) { errsv = errno; goto stderr_pipe_failed; } } child->pid = fork(); if (child->pid == 0) { /* Child. */ fd_set except_fds; if (child->stdin_fd == REDIRECT_PIPE) (void) dup2(stdin_pipe[0], STDIN_FILENO); else if (child->stdin_fd == REDIRECT_DEV_NULL) (void) dup2(open_devnull(), STDIN_FILENO); else if (child->stdin_fd != NO_REDIRECT) (void) dup2(child->stdin_fd, STDIN_FILENO); if (child->stdout_fd == REDIRECT_PIPE) (void) dup2(stdout_pipe[1], STDOUT_FILENO); else if (child->stdout_fd == REDIRECT_DEV_NULL) (void) dup2(open_devnull(), STDOUT_FILENO); else if (child->stdout_fd != NO_REDIRECT) (void) dup2(child->stdout_fd, STDOUT_FILENO); if (child->stderr_fd == REDIRECT_PIPE) (void) dup2(stderr_pipe[1], STDERR_FILENO); else if (child->stderr_fd == REDIRECT_DEV_NULL) (void) dup2(open_devnull(), STDERR_FILENO); else if (child->stderr_fd != NO_REDIRECT) (void) dup2(child->stderr_fd, STDERR_FILENO); FD_ZERO(&except_fds); FD_SET(STDIN_FILENO, &except_fds); FD_SET(STDOUT_FILENO, &except_fds); FD_SET(STDERR_FILENO, &except_fds); FD_SET(status_pipe[1], &except_fds); (void) close_fds(&except_fds); (void) setgid(getgid()); (void) setuid(getuid()); if (child->function != NULL) { (void) close(status_pipe[1]); _exit(child->function(child->argument)); } else { execv(child->path, (char *const*)child->argv); (void) write(status_pipe[1], &errno, sizeof errno); } _exit(1); } if (child->pid < 0) { errsv = errno; goto fork_failed; } (void) close(status_pipe[1]); /* Get the error status from the child, if any. */ if (read(status_pipe[0], &child_errno, sizeof child_errno) == sizeof child_errno) { errsv = child_errno; goto child_failed; } (void) close(status_pipe[0]); if (child->stdin_fd == REDIRECT_PIPE) { /* Write end. */ child->stdin_fd = stdin_pipe[1]; /* Read end. */ (void) close(stdin_pipe[0]); } if (child->stdout_fd == REDIRECT_PIPE) { /* Read end. */ child->stdout_fd = stdout_pipe[0]; /* Write end. */ (void) close(stdout_pipe[1]); } if (child->stderr_fd == REDIRECT_PIPE) { /* Read end. */ child->stderr_fd = stderr_pipe[0]; /* Write end. */ (void) close(stderr_pipe[1]); } return true; child_failed: fork_failed: if (child->stderr_fd == REDIRECT_PIPE) { (void) close(stderr_pipe[0]); (void) close(stderr_pipe[1]); } stderr_pipe_failed: if (child->stdout_fd == REDIRECT_PIPE) { (void) close(stdout_pipe[0]); (void) close(stdout_pipe[1]); } stdout_pipe_failed: if (child->stdin_fd == REDIRECT_PIPE) { (void) close(stdin_pipe[0]); (void) close(stdin_pipe[1]); } stdin_pipe_failed: (void) close(status_pipe[0]); (void) close(status_pipe[1]); errno = errsv; return false; } vlock-2.2.2/src/process.h000066400000000000000000000040361101377123600152550ustar00rootroot00000000000000/* process.h -- header for child process routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include /* Wait for the given amount of time for the death of the given child process. * If the child process dies in the given amount of time or already was dead * true is returned and false otherwise. */ bool wait_for_death(pid_t pid, long sec, long usec); /* Try hard to kill the given child process. */ void ensure_death(pid_t pid); #define NO_REDIRECT (-2) #define REDIRECT_DEV_NULL (-3) #define REDIRECT_PIPE (-4) struct child_process { /* Function that will be run in the child. */ int (*function)(void *argument); /* Argument for the function. */ void *argument; /* First argument to execv. */ const char *path; /* Second argument to execv. */ const char *const *argv; /* The child's stdin. */ int stdin_fd; /* The child's stdout. */ int stdout_fd; /* The child's stderr. */ int stderr_fd; /* The child's PID. */ pid_t pid; }; /* Create a new child process. All file descriptors except stdin, stdout and * stderr are closed and privileges are dropped. All fields of the child * struct except pid must be set. If a stdio file descriptor field has the * special value of REDIRECT_DEV_NULL it is redirected from or to /dev/null. * If it has the value REDIRECT_PIPE a pipe will be created and one end will be * connected to the respective descriptor of the child. The file descriptor of * the other end is stored in the field after the call. It is up to the caller * to close the pipe descriptor(s). */ bool create_child(struct child_process *child); vlock-2.2.2/src/prompt.c000066400000000000000000000155111101377123600151130ustar00rootroot00000000000000/* prompt.c -- prompt routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * * * The prompt functions (prompt and prompt_echo_off) were * inspired by/copied from openpam's openpam_ttyconv.c: * * Copyright (c) 2002-2003 Networks Associates Technology, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include "prompt.h" #define PROMPT_BUFFER_SIZE 512 /* Prompt with the given string for a single line of input. The read string is * returned in a new buffer that should be freed by the caller. If reading * fails or the timeout (if given) occurs NULL is retured. */ char *prompt(const char *msg, const struct timespec *timeout) { char buffer[PROMPT_BUFFER_SIZE]; char *result = NULL; ssize_t len; struct termios term; struct timeval *timeout_val = NULL; tcflag_t lflag; fd_set readfds; if (msg != NULL) { /* Write out the prompt. */ (void) fputs(msg, stderr); fflush(stderr); } /* Get the current terminal attributes. */ (void) tcgetattr(STDIN_FILENO, &term); /* Save the lflag value. */ lflag = term.c_lflag; /* Enable canonical mode. We're only interested in line buffering. */ term.c_lflag |= ICANON; /* Disable terminal signals. */ term.c_lflag &= ~ISIG; /* Set the terminal attributes. */ (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); /* Discard all unread input characters. */ (void) tcflush(STDIN_FILENO, TCIFLUSH); /* Initialize file descriptor set. */ FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); before_select: /* copy timeout */ if (timeout != NULL) { timeout_val = malloc(sizeof *timeout_val); if (timeout_val == NULL) return NULL; timeout_val->tv_sec = timeout->tv_sec; timeout_val->tv_usec = timeout->tv_nsec / 1000; } /* Reset errno. */ errno = 0; /* Wait until a string was entered. */ if (select(STDIN_FILENO + 1, &readfds, NULL, NULL, timeout_val) != 1) { switch (errno) { case 0: fprintf(stderr, "timeout!\n"); goto out; case EINTR: /* A signal was caught. Restart. */ goto before_select; default: perror("vlock: select() on stdin failed"); goto out; } } /* Read the string from stdin. At most buffer length - 1 bytes, to * leave room for the terminating zero byte. */ if ((len = read(STDIN_FILENO, buffer, sizeof buffer - 1)) < 0) goto out; /* Terminate the string. */ buffer[len] = '\0'; /* Strip trailing newline characters. */ for (len = strlen(buffer); len > 0; --len) if (buffer[len - 1] != '\r' && buffer[len - 1] != '\n') break; /* Terminate the string, again. */ buffer[len] = '\0'; /* Copy the string. Success and error paths are the same. */ result = strdup(buffer); /* Clear our buffer. */ memset(buffer, 0, sizeof buffer); out: free(timeout_val); /* Restore original terminal attributes. */ term.c_lflag = lflag; (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); return result; } /* Same as prompt except that the characters entered are not echoed. */ char *prompt_echo_off(const char *msg, const struct timespec *timeout) { struct termios term; tcflag_t lflag; char *result; (void) tcgetattr(STDIN_FILENO, &term); lflag = term.c_lflag; term.c_lflag &= ~ECHO; (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); result = prompt(msg, timeout); term.c_lflag = lflag; (void) tcsetattr(STDIN_FILENO, TCSAFLUSH, &term); if (result != NULL) fputc('\n', stderr); return result; } /* Read a single character from the stdin. If the timeout is reached * 0 is returned. */ char read_character(struct timespec *timeout) { char c = 0; struct timeval *timeout_val = NULL; fd_set readfds; if (timeout != NULL) { timeout_val = calloc(sizeof *timeout_val, 1); if (timeout_val != NULL) { timeout_val->tv_sec = timeout->tv_sec; timeout_val->tv_usec = timeout->tv_nsec / 1000; } } /* Initialize file descriptor set. */ FD_ZERO(&readfds); FD_SET(STDIN_FILENO, &readfds); /* Wait for a character. */ if (select(STDIN_FILENO + 1, &readfds, NULL, NULL, timeout_val) != 1) goto out; /* Read the character. */ (void) read(STDIN_FILENO, &c, 1); out: free(timeout_val); return c; } /* Wait for any of the characters in the given character set to be read from * stdin. If charset is NULL wait for any character. Returns 0 when the * timeout occurs. */ char wait_for_character(const char *charset, struct timespec *timeout) { struct termios term; tcflag_t lflag; char c; /* switch off line buffering */ (void) tcgetattr(STDIN_FILENO, &term); lflag = term.c_lflag; term.c_lflag &= ~ICANON; (void) tcsetattr(STDIN_FILENO, TCSANOW, &term); for (;;) { c = read_character(timeout); if (c == 0 || charset == NULL) break; else if (strchr(charset, c) != NULL) break; } /* restore line buffering */ term.c_lflag = lflag; (void) tcsetattr(STDIN_FILENO, TCSANOW, &term); return c; } vlock-2.2.2/src/prompt.h000066400000000000000000000060071101377123600151200ustar00rootroot00000000000000/* prompt.h -- header file for the prompt routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * * * The prompt functions (prompt and prompt_echo_off) were * inspired by/copied from openpam's openpam_ttyconv.c: * * Copyright (c) 2002-2003 Networks Associates Technology, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* Prompt for a string with the given message. The string is returned if * successfully read, otherwise NULL. The caller is responsible for freeing * the resulting buffer. If no string is read after the given timeout this * prompt() returns NULL. A timeout of NULL means no timeout, i.e. wait forever. */ char *prompt(const char *msg, const struct timespec *timeout); /* Same as prompt() above, except that characters entered are not echoed. */ char *prompt_echo_off(const char *msg, const struct timespec *timeout); /* Read a single character from the stdin. If the timeout is reached * 0 is returned. */ char read_character(struct timespec *timeout); /* Wait for any of the characters in the given character set to be read from * stdin. If charset is NULL wait for any character. Returns 0 when the * timeout occurs. */ char wait_for_character(const char *charset, struct timespec *timeout); vlock-2.2.2/src/script.c000066400000000000000000000216661101377123600151060ustar00rootroot00000000000000/* script.c -- script routines for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ /* Scripts are executables that are run as unprivileged child processes of * vlock. They communicate with vlock through stdin and stdout. * * When dependencies are retrieved they are launched once for each dependency * and should print the names of the plugins they depend on on stdout one per * line. The dependency requested is given as a single command line argument. * * In hook mode the script is called once with "hooks" as a single command line * argument. It should not exit until its stdin closes. The hook that should * be executed is written to its stdin on a single line. * * Currently there is no way for a script to communicate errors or even success * to vlock. If it exits it will linger as a zombie until the plugin is * destroyed. */ #if !defined(__FreeBSD__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include "list.h" #include "process.h" #include "util.h" #include "plugin.h" static bool init_script(struct plugin *p); static void destroy_script(struct plugin *p); static bool call_script_hook(struct plugin *p, const char *hook_name); struct plugin_type *script = &(struct plugin_type){ .init = init_script, .destroy = destroy_script, .call_hook = call_script_hook, }; struct script_context { /* The path to the script. */ char *path; /* Was the script launched? */ bool launched; /* Did the script die? */ bool dead; /* The pipe file descriptor that is connected to the script's stdin. */ int fd; /* The PID of the script. */ pid_t pid; }; /* Get the dependency from the script. */ static bool get_dependency(const char *path, const char *dependency_name, struct list *dependency_list); /* Launch the script creating a new script_context. */ static bool launch_script(struct script_context *script); bool init_script(struct plugin *p) { int errsv; struct script_context *context = malloc(sizeof *context); if (context == NULL) return false; context->dead = false; context->launched = false; if (asprintf(&context->path, "%s/%s", VLOCK_SCRIPT_DIR, p->name) < 0) { free(context); errno = ENOMEM; return false; } /* Get the dependency information. Whether the script is executable or not * is also detected here. */ for (size_t i = 0; i < nr_dependencies; i++) if (!get_dependency(context->path, dependency_names[i], p->dependencies[i])) goto error; p->context = context; return true; error: errsv = errno; free(context->path); free(context); errno = errsv; return false; } static void destroy_script(struct plugin *p) { struct script_context *context = p->context; if (context != NULL) { free(context->path); if (context->launched) { /* Close the pipe. */ (void) close(context->fd); /* Kill the child process. */ if (!wait_for_death(context->pid, 0, 500000L)) ensure_death(context->pid); } free(context); } } /* Invoke the hook by writing it on a single line to the scripts stdin. */ static bool call_script_hook(struct plugin *s, const char *hook_name) { static const char newline = '\n'; struct script_context *context = s->context; ssize_t hook_name_length = strlen(hook_name); ssize_t length; struct sigaction act; struct sigaction oldact; if (!context->launched) { /* Launch script. */ context->launched = launch_script(context); if (!context->launched) return false; } if (context->dead) /* Nothing to do. */ return false; /* When writing to a pipe when the read end is closed the kernel invariably * sends SIGPIPE. Ignore it. */ (void) sigemptyset(&(act.sa_mask)); act.sa_flags = SA_RESTART; act.sa_handler = SIG_IGN; (void) sigaction(SIGPIPE, &act, &oldact); /* Send hook name and a newline through the pipe. */ length = write(context->fd, hook_name, hook_name_length); if (length > 0) length += write(context->fd, &newline, sizeof newline); /* Restore the previous SIGPIPE handler. */ (void) sigaction(SIGPIPE, &oldact, NULL); /* If write fails the script is considered dead. */ context->dead = (length != hook_name_length + 1); return !context->dead; } static bool launch_script(struct script_context *script) { int fd_flags; const char *argv[] = { script->path, "hooks", NULL }; struct child_process child = { .path = script->path, .argv = argv, .stdin_fd = REDIRECT_PIPE, .stdout_fd = REDIRECT_DEV_NULL, .stderr_fd = REDIRECT_DEV_NULL, .function = NULL, }; if (!create_child(&child)) return false; script->fd = child.stdin_fd; script->pid = child.pid; fd_flags = fcntl(script->fd, F_GETFL, &fd_flags); if (fd_flags != -1) { fd_flags |= O_NONBLOCK; (void) fcntl(script->fd, F_SETFL, fd_flags); } return true; } static char *read_dependency(const char *path, const char *dependency_name); static bool parse_dependency(char *data, struct list *dependency_list); /* Get the dependency from the script. */ static bool get_dependency(const char *path, const char *dependency_name, struct list *dependency_list) { /* Read the dependency data. */ char *data; errno = 0; data = read_dependency(path, dependency_name); if (data == NULL) { return errno == 0; } else { /* Parse the dependency data. */ bool result = parse_dependency(data, dependency_list); int errsv = errno; free(data); errno = errsv; return result; } } /* Read the dependency data by starting the script with the name of the * dependency as a single command line argument. The script should then print * the dependencies to its stdout one on per line. */ static char *read_dependency(const char *path, const char *dependency_name) { const char *argv[] = { path, dependency_name, NULL }; struct child_process child = { .path = path, .argv = argv, .stdin_fd = REDIRECT_DEV_NULL, .stdout_fd = REDIRECT_PIPE, .stderr_fd = REDIRECT_DEV_NULL, .function = NULL, }; struct timeval timeout = {1, 0}; char *data = calloc(1, sizeof *data); size_t data_length = 0; if (data == NULL) return NULL; if (!create_child(&child)) { int errsv = errno; free(data); errno = errsv; return NULL; } /* Read the dependency from the child. Reading fails if either the timeout * elapses or more that LINE_MAX bytes are read. */ for (;;) { struct timeval t = timeout; struct timeval t1; struct timeval t2; char buffer[LINE_MAX]; ssize_t length; fd_set read_fds; FD_ZERO(&read_fds); FD_SET(child.stdout_fd, &read_fds); /* t1 is before select. */ (void) gettimeofday(&t1, NULL); if (select(child.stdout_fd+1, &read_fds, NULL, NULL, &t) != 1) { timeout: errno = ETIMEDOUT; goto error; } /* t2 is after select. */ (void) gettimeofday(&t2, NULL); /* Get the time that during select. */ timersub(&t2, &t1, &t2); /* This is very unlikely. */ if (timercmp(&t2, &timeout, >)) goto timeout; /* Reduce the timeout. */ timersub(&timeout, &t2, &timeout); /* Read dependency data from the script. */ length = read(child.stdout_fd, buffer, sizeof buffer - 1); /* Did the script close its stdin or exit? */ if (length <= 0) break; if (data_length+length+1 > LINE_MAX) { errno = EFBIG; goto error; } /* Grow the data string. */ data = realloc(data, data_length+length); if (data == NULL) goto error; /* Append the buffer to the data string. */ strncpy(data+data_length, buffer, length); data_length += length; } /* Terminate the data string. */ data[data_length] = '\0'; /* Close the read end of the pipe. */ (void) close(child.stdout_fd); /* Kill the script. */ if (!wait_for_death(child.pid, 0, 500000L)) ensure_death(child.pid); return data; error: { int errsv = errno; free(data); (void) close(child.stdout_fd); if (!wait_for_death(child.pid, 0, 500000L)) ensure_death(child.pid); errno = errsv; return NULL; } } static bool parse_dependency(char *data, struct list *dependency_list) { for (char *saveptr, *token = strtok_r(data, " \r\n", &saveptr); token != NULL; token = strtok_r(NULL, " \r\n", &saveptr)) { char *s = strdup(token); if (s == NULL || !list_append(dependency_list, s)) return false; } return true; } vlock-2.2.2/src/tsort.c000066400000000000000000000061311101377123600147430ustar00rootroot00000000000000/* tsort.c -- topological sort for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include "list.h" #include "util.h" #include "tsort.h" /* Get the zeros of the graph, i.e. nodes with no incoming edges. */ static struct list *get_zeros(struct list *nodes, struct list *edges) { struct list *zeros = list_copy(nodes); if (zeros == NULL) return NULL; list_for_each(edges, edge_item) { struct edge *e = edge_item->data; list_delete(zeros, e->successor); } return zeros; } /* Check if the given node is a zero. */ static bool is_zero(void *node, struct list *edges) { list_for_each(edges, edge_item) { struct edge *e = edge_item->data; if (e->successor == node) return false; } return true; } /* For the given directed graph, generate a topological sort of the nodes. * * Sorts the list and deletes all edges. If there are circles found in the * graph or there are edges that have no corresponding nodes the erroneous * edges are left. * * The algorithm is taken from the Wikipedia: * * http://en.wikipedia.org/w/index.php?title=Topological_sorting&oldid=153157450#Algorithms * */ struct list *tsort(struct list *nodes, struct list *edges) { struct list *sorted_nodes = list_new(); /* Retrieve all zeros. */ struct list *zeros; if (sorted_nodes == NULL) return NULL; zeros = get_zeros(nodes, edges); if (zeros == NULL) { GUARD_ERRNO(list_free(sorted_nodes)); return NULL; } /* While the list of zeros is not empty, take the first zero and remove it * and ... */ list_delete_for_each(zeros, zero_item) { void *zero = zero_item->data; /* ... add it to the list of sorted nodes. */ if (!list_append(sorted_nodes, zero)) goto error; /* Then look at each edge ... */ list_for_each_manual(edges, edge_item) { struct edge *e = edge_item->data; /* ... that has this zero as its predecessor ... */ if (e->predecessor == zero) { /* ... and remove it. */ edge_item = list_delete_item(edges, edge_item); /* If the successor has become a zero now ... */ if (is_zero(e->successor, edges)) /* ... add it to the list of zeros. */ if (!list_append(zeros, e->successor)) goto error; free(e); } else { edge_item = edge_item->next; } } } /* If all edges were deleted the algorithm was successful. */ if (!list_is_empty(edges)) { list_free(sorted_nodes); sorted_nodes = NULL; } list_free(zeros); errno = 0; return sorted_nodes;; error: { int errsv = errno; list_free(sorted_nodes); list_free(zeros); errno = errsv; return NULL; } } vlock-2.2.2/src/tsort.h000066400000000000000000000017771101377123600147630ustar00rootroot00000000000000/* tsort.h -- header file for topological sort for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include /* An edge of the graph, specifying that predecessor must come before * successor. */ struct edge { void *predecessor; void *successor; }; struct list; /* For the given directed graph, generate a topological sort of the nodes. * * Sorts the list and deletes all edges. If there are circles found in the * graph or there are edges that have no corresponding nodes the erroneous * edges are left. */ struct list *tsort(struct list *nodes, struct list *edges); vlock-2.2.2/src/util.c000066400000000000000000000034121101377123600145440ustar00rootroot00000000000000/* util.c -- utility routines for vlock, the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #if !defined(__FreeBSD__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif #include #include #include #include #include #include #include "util.h" /* Parse the given string (interpreted as seconds) into a * timespec. On error NULL is returned. The caller is responsible * to free the result. The argument may be NULL, in which case NULL * is returned, too. "0" is also parsed as NULL. */ struct timespec *parse_seconds(const char *s) { if (s == NULL) { return NULL; } else { char *n; struct timespec *t = calloc(sizeof *t, 1); if (t == NULL) return NULL; t->tv_sec = strtol(s, &n, 10); if (*n != '\0' || t->tv_sec <= 0) { free(t); t = NULL; } return t; } } void fatal_error(const char *format, ...) { char *error; va_list ap; va_start(ap, format); if (vasprintf(&error, format, ap) < 0) error = "error while formatting error message"; va_end(ap); fatal_error_free(error); } void fatal_error_free(char *error) { fputs(error, stderr); fputc('\n', stderr); free(error); exit(EXIT_FAILURE); } void fatal_perror(const char *errmsg) { if (errno != 0) fatal_error("%s: %s", errmsg, strerror(errno)); else fatal_error("%s", errmsg); } vlock-2.2.2/src/util.h000066400000000000000000000023021101377123600145460ustar00rootroot00000000000000/* util.h -- header for utility routines for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include struct timespec; /* Parse the given string (interpreted as seconds) into a * timespec. On error NULL is returned. The caller is responsible * to free the result. The string may be NULL, in which case NULL * is returned, too. */ struct timespec *parse_seconds(const char *s); void fatal_error(const char *format, ...) __attribute__((noreturn, format(printf, 1, 2))); void fatal_error_free(char *errmsg) __attribute__((noreturn)); void fatal_perror(const char *errmsg) __attribute__((noreturn)); #define STRERROR (errno ? strerror(errno) : "Unknown error") #define GUARD_ERRNO(expr) \ do { \ int __errsv = errno; expr; errno = __errsv; \ } while (0) vlock-2.2.2/src/vlock-main.c000066400000000000000000000135051101377123600156330ustar00rootroot00000000000000/* vlock-main.c -- main routine for vlock, * the VT locking program for linux * * This program is copyright (C) 2007 Frank Benkstein, and is free * software which is freely distributable under the terms of the * GNU General Public License version 2, included as the file COPYING in this * distribution. It is NOT public domain software, and any * redistribution not permitted by the GNU General Public License is * expressly forbidden without prior written permission from * the author. * */ #include #include #include #include #include #include #include #include #include #include #include "prompt.h" #include "auth.h" #include "console_switch.h" #include "util.h" #ifdef USE_PLUGINS #include "plugins.h" #endif int vlock_debug = 0; #define ensure_atexit(func) \ do { \ if (atexit(func) != 0) \ fatal_perror("vlock: atexit() failed"); \ } while (0) static char *get_username(void) { uid_t uid = getuid(); char *username = NULL; /* Get the user name from the environment if started as root. */ if (uid == 0) username = getenv("USER"); if (username == NULL) { struct passwd *pw; /* Get the password entry. */ pw = getpwuid(uid); if (pw == NULL) return NULL; username = pw->pw_name; } return strdup(username); } static void terminate(int signum) { fprintf(stderr, "vlock: Terminated!\n"); /* Call exit here to ensure atexit handlers are called. */ exit(1); } static void block_signals(void) { struct sigaction sa; /* Ignore some signals. */ /* These signals shouldn't be delivered anyway, because terminal signals are * disabled below. */ (void) sigemptyset(&(sa.sa_mask)); sa.sa_flags = SA_RESTART; sa.sa_handler = SIG_IGN; (void) sigaction(SIGINT, &sa, NULL); (void) sigaction(SIGQUIT, &sa, NULL); (void) sigaction(SIGTSTP, &sa, NULL); /* Install special handler for SIGTERM. */ sa.sa_flags = SA_RESETHAND; sa.sa_handler = terminate; (void) sigaction(SIGTERM, &sa, NULL); } static struct termios term; static tcflag_t lflag; static void secure_terminal(void) { /* Disable terminal echoing and signals. */ (void) tcgetattr(STDIN_FILENO, &term); lflag = term.c_lflag; term.c_lflag &= ~(ECHO | ISIG); (void) tcsetattr(STDIN_FILENO, TCSANOW, &term); } static void restore_terminal(void) { /* Restore the terminal. */ term.c_lflag = lflag; (void) tcsetattr(STDIN_FILENO, TCSANOW, &term); } static int auth_tries; static void auth_loop(const char *username) { struct timespec *prompt_timeout; struct timespec *wait_timeout; char *vlock_message; /* Get the vlock message from the environment. */ vlock_message = getenv("VLOCK_MESSAGE"); if (vlock_message == NULL) { if (console_switch_locked) vlock_message = getenv("VLOCK_ALL_MESSAGE"); else vlock_message = getenv("VLOCK_CURRENT_MESSAGE"); } /* Get the timeouts from the environment. */ prompt_timeout = parse_seconds(getenv("VLOCK_PROMPT_TIMEOUT")); #ifdef USE_PLUGINS wait_timeout = parse_seconds(getenv("VLOCK_TIMEOUT")); #else wait_timeout = NULL; #endif for (;;) { char c; /* Print vlock message if there is one. */ if (vlock_message && *vlock_message) { fputs(vlock_message, stderr); fputc('\n', stderr); } /* Wait for enter or escape to be pressed. */ c = wait_for_character("\n\033", wait_timeout); /* Escape was pressed or the timeout occurred. */ if (c == '\033' || c == 0) { #ifdef USE_PLUGINS plugin_hook("vlock_save"); /* Wait for any key to be pressed. */ c = wait_for_character(NULL, NULL); plugin_hook("vlock_save_abort"); /* Do not require enter to be pressed twice. */ if (c != '\n') continue; #else continue; #endif } /* Try authentication as user. */ if (auth(username, prompt_timeout)) break; else sleep(1); #ifndef NO_ROOT_PASS if (strcmp(username, "root") != 0) { /* Try authentication as root. */ if (auth("root", prompt_timeout)) break; else sleep(1); } #endif auth_tries++; } /* Free timeouts memory. */ free(wait_timeout); free(prompt_timeout); } void display_auth_tries(void) { if (auth_tries > 0) fprintf(stderr, "%d failed authentication %s.\n", auth_tries, auth_tries > 1 ? "tries" : "try"); } #ifdef USE_PLUGINS static void call_end_hook(void) { (void) plugin_hook("vlock_end"); } #endif /* Lock the current terminal until proper authentication is received. */ int main(int argc, char *const argv[]) { char *username; vlock_debug = (getenv("VLOCK_DEBUG") != NULL); block_signals(); username = get_username(); if (username == NULL) fatal_perror("vlock: could not get username"); ensure_atexit(display_auth_tries); #ifdef USE_PLUGINS for (int i = 1; i < argc; i++) if (!load_plugin(argv[i])) fatal_error("vlock: loading plugin '%s' failed: %s", argv[i], STRERROR); ensure_atexit(unload_plugins); if (!resolve_dependencies()) { if (errno == 0) exit(EXIT_FAILURE); else fatal_error("vlock: error resolving plugin dependencies: %s", STRERROR); } plugin_hook("vlock_start"); ensure_atexit(call_end_hook); #else /* !USE_PLUGINS */ /* Emulate pseudo plugin "all". */ if (argc == 2 && (strcmp(argv[1], "all") == 0)) { if (!lock_console_switch()) { if (errno) perror("vlock: could not disable console switching"); exit(EXIT_FAILURE); } ensure_atexit((void (*)(void))unlock_console_switch); } else if (argc > 1) { fatal_error("vlock: plugin support disabled"); } #endif if (!isatty(STDIN_FILENO)) fatal_error("vlock: stdin is not a terminal"); secure_terminal(); ensure_atexit(restore_terminal); auth_loop(username); free(username); exit(0); } vlock-2.2.2/src/vlock.sh000066400000000000000000000160061101377123600151000ustar00rootroot00000000000000#!%BOURNE_SHELL% # # vlock.sh -- start script for vlock, the VT locking program for linux # # This program is copyright (C) 2007 Frank Benkstein, and is free # software which is freely distributable under the terms of the # GNU General Public License version 2, included as the file COPYING in this # distribution. It is NOT public domain software, and any # redistribution not permitted by the GNU General Public License is # expressly forbidden without prior written permission from # the author. # Ignore some signals. trap : HUP INT QUIT TSTP # Exit on error. set -e # Magic characters to clear the terminal. CLEAR_SCREEN="`echo -e '\033[H\033[J'`" # Enter message that is common to different the messages. VLOCK_ENTER_PROMPT="Please press [ENTER] to unlock." # Message that is displayed when console switching is disabled. VLOCK_ALL_MESSAGE="${CLEAR_SCREEN}\ The entire console display is now completely locked. You will not be able to switch to another virtual console. ${VLOCK_ENTER_PROMPT}" # Message that is displayed when only the current terminal is locked. VLOCK_CURRENT_MESSAGE="${CLEAR_SCREEN}\ This TTY is now locked. ${VLOCK_ENTER_PROMPT}" # Read user settings. if [ -r "${HOME}/.vlockrc" ] ; then . "${HOME}/.vlockrc" fi # "Compile" time variables. VLOCK_MAIN="%PREFIX%/sbin/vlock-main" VLOCK_VERSION="%VLOCK_VERSION%" # If set to "y" plugin support is enabled in vlock-main. VLOCK_ENABLE_PLUGINS="%VLOCK_ENABLE_PLUGINS%" print_help() { echo >&2 "vlock: locks virtual consoles, saving your current session." if [ "${VLOCK_ENABLE_PLUGINS}" = "yes" ] ; then echo >&2 "Usage: vlock [options] [plugins...]" else echo >&2 "Usage: vlock [options]" fi echo >&2 " Where [options] are any of:" echo >&2 "-c or --current: lock only this virtual console, allowing user to" echo >&2 " switch to other virtual consoles." echo >&2 "-a or --all: lock all virtual consoles by preventing other users" echo >&2 " from switching virtual consoles." if [ "${VLOCK_ENABLE_PLUGINS}" = "yes" ] ; then echo >&2 "-n or --new: allocate a new virtual console before locking," echo >&2 " implies --all." echo >&2 "-s or --disable-sysrq: disable SysRq while consoles are locked to" echo >&2 " prevent killing vlock with SAK" echo >&2 "-t or --timeout : run screen saver plugins" echo >&2 " after the given amount of time." fi echo >&2 "-v or --version: Print the version number of vlock and exit." echo >&2 "-h or --help: Print this help message and exit." } # Export variables only if they are set. Some shells create an empty variable # on export even if it was previously unset. export_if_set() { while [ $# -gt 0 ] ; do if ( eval [ "\"\${$1+set}\"" = "set" ] ) ; then eval export $1 fi shift done } main() { short_options_with_arguments="t" long_options_with_arguments="timeout" # Parse command line arguments. while [ $# -gt 0 ] ; do case "$1" in -[!-]?*) # Strip "-" to get the list of option characters. options="${1#-}" shift last_option_argument="${options}" last_option_index=0 # If an option character takes an argument all characters after it # become the argument if it isn't already the last one. E.g. if "x" # takes an argument "-fooxbar" becomes "-foo -x bar". while [ -n "${last_option_argument}" ] ; do # Get first option character. option="$(expr "${last_option_argument}" : '\(.\)')" # Strip it from the list of option characters. last_option_argument="${last_option_argument#?}" last_option_index=$((${last_option_index} + 1)) if expr "${short_options_with_arguments}" : "${option}" >/dev/null ; then # Prepend "-" plus option character and rest of option string to $@. set -- "-${option}" "${last_option_argument}" "$@" # Remove all characters after the option character. if [ "${last_option_index}" -gt 1 ] ; then options="$(expr "${options}" : "\(.\{$((${last_option_index}-1))\}\)")" else options="" fi break fi done # Convert clashed arguments like "-foobar" to "-f -o -o -b -a -r". while [ -n "${options}" ] ; do # Get last option character. option="$(expr "${options}" : '.*\(.\)')" # Strip it from the list of option characters. options="${options%?}" # Prepend "-" plus option character to $@. set -- "-${option}" "$@" done ;; --?*=?*) # Extract option name and argument. option="$(expr "x$1" : 'x--\([^=]*\)=.*')" option_argument="$(expr "x$1" : 'x--[^=]*=\(.*\)')" shift compare_options="${long_options_with_arguments}" # Find the option in the list of options that take an argument. while [ -n "${compare_options}" ] ; do compare_option="${compare_options%%,*}" compare_options="${compare_options#"${compare_option}"}" compare_options="${compare_options#,}" if [ "${option}" = "${compare_option}" ] ; then set -- "--${option}" "${option_argument}" "$@" unset option option_argument break fi done if [ -n "${option}" ] ; then echo >&2 "$0: option '--${option}' does not allow an argument" exit 1 fi ;; -a|--all) plugins="${plugins} all" shift ;; -c|--current) unset plugins shift ;; -n|--new) plugins="${plugins} new" shift ;; -s|--disable-sysrq) plugins="${plugins} nosysrq" shift ;; -t|--timeout) VLOCK_TIMEOUT="$2" if ! shift 2 ; then echo >&2 "$0: option '$1' requires an argument" exit 1 fi ;; -h|--help) print_help exit ;; -v|--version) if [ "${VLOCK_ENABLE_PLUGINS}" = "yes" ] ; then echo >&2 "vlock version ${VLOCK_VERSION}" else echo >&2 "vlock version ${VLOCK_VERSION} (no plugin support)" fi exit ;; -[!-]|--?*) echo >&1 "$0: unknown option '$1'" print_help exit 1 ;; --) # End of option list. shift break ;; *) for argument ; do if [ "${argument}" = "--" ] ; then has_double_dash="yes" break fi done if [ -n "${has_double_dash}" ] ; then set -- "$@" "$1" else set -- "$@" -- "$1" fi shift ;; esac done # Export variables for vlock-main. export_if_set VLOCK_TIMEOUT VLOCK_PROMPT_TIMEOUT export_if_set VLOCK_MESSAGE VLOCK_ALL_MESSAGE VLOCK_CURRENT_MESSAGE if [ "${VLOCK_ENABLE_PLUGINS}" = "yes" ] ; then exec "${VLOCK_MAIN}" ${plugins} ${VLOCK_PLUGINS} "$@" else exec "${VLOCK_MAIN}" ${plugins} fi } main "$@" vlock-2.2.2/tests/000077500000000000000000000000001101377123600137765ustar00rootroot00000000000000vlock-2.2.2/tests/.gitignore000066400000000000000000000000411101377123600157610ustar00rootroot00000000000000/vlock-test *.gcda *.gcno *.gcov vlock-2.2.2/tests/.valgrind-supressions000066400000000000000000000000621101377123600201760ustar00rootroot00000000000000{ Linker Memcheck:Cond obj:/lib/ld-*.so } vlock-2.2.2/tests/Makefile000066400000000000000000000020161101377123600154350ustar00rootroot00000000000000include ../config.mk VPATH = ../src override CFLAGS+=-I../src export VLOCK_TEST_OUTPUT_MODE VLOCK_TEST_OUTPUT_MODE = verbose .PHONY: all all: check TESTED_SOURCES = list.c tsort.c util.c process.c TESTED_OBJECTS = $(TESTED_SOURCES:.c=.o) TEST_SOURCES = $(TESTED_SOURCES:%=test_%) TEST_OBJECTS = $(TEST_SOURCES:.c=.o) vlock-test : override LDFLAGS+=-lcunit vlock-test: vlock-test.o $(TEST_OBJECTS) $(TESTED_OBJECTS) vlock-test.o: $(TEST_SOURCES:.c=.h) ifeq ($(COVERAGE),y) vlock-test : override LDFLAGS+=--coverage $(TESTED_OBJECTS) : override CFLAGS+=--coverage endif .PHONY: check check: vlock-test @./vlock-test .PHONY: memcheck memcheck : VLOCK_TEST_OUTPUT_MODE=silent memcheck: vlock-test @valgrind \ --tool=memcheck \ --suppressions=.valgrind-supressions \ --error-exitcode=1 \ --leak-check=full \ --show-reachable=yes \ --track-fds=yes \ --child-silent-after-fork=yes \ ./vlock-test .PHONY: clean clean: $(RM) vlock-test $(wildcard *.o) $(RM) $(wildcard *.gcno) $(wildcard *.gcda) $(wildcard *.gcov) vlock-2.2.2/tests/test_list.c000066400000000000000000000077141101377123600161650ustar00rootroot00000000000000#include #include "list.h" #include "test_list.h" void test_list_new(void) { struct list *l = list_new(); CU_ASSERT_PTR_NOT_NULL_FATAL(l); CU_ASSERT_PTR_NULL(l->first); CU_ASSERT_PTR_NULL(l->last); list_free(l); } void test_list_copy(void) { struct list *l = list_new(); struct list *m; list_append(l, (void *)1); list_append(l, (void *)2); list_append(l, (void *)3); m = list_copy(l); CU_ASSERT_EQUAL(list_length(l), list_length(m)); CU_ASSERT_PTR_NOT_EQUAL(l, m); for (struct list_item *item_l = l->first, *item_m = m->first; item_l != NULL && item_m != NULL; item_l = item_l->next, item_m = item_m->next) { CU_ASSERT_PTR_EQUAL(item_l->data, item_m->data); CU_ASSERT_PTR_NOT_EQUAL(item_l, item_m); } list_free(m); list_free(l); } void test_list_free(void) { struct list *l = list_new(); list_append(l, (void *)1); list_append(l, (void *)2); list_append(l, (void *)3); list_free(l); CU_PASS("list_free() didn't crash"); } void test_list_length(void) { struct list *l = list_new(); CU_ASSERT(list_length(l) == 0); list_append(l, (void *)1); CU_ASSERT(list_length(l) == 1); list_append(l, (void *)2); CU_ASSERT(list_length(l) == 2); list_append(l, (void *)3); CU_ASSERT(list_length(l) == 3); list_append(l, (void *)4); CU_ASSERT(list_length(l) == 4); list_free(l); } void test_list_append(void) { struct list *l = list_new(); list_append(l, (void *)1); CU_ASSERT_PTR_EQUAL(l->first, l->last); CU_ASSERT_PTR_NULL(l->first->previous); CU_ASSERT_PTR_NULL(l->last->next); CU_ASSERT_PTR_EQUAL(l->first->data, (void *)1); list_append(l, (void *)2); CU_ASSERT_PTR_NOT_EQUAL(l->first, l->last); CU_ASSERT_PTR_EQUAL(l->first->next, l->last); CU_ASSERT_PTR_EQUAL(l->last->previous, l->first); CU_ASSERT_PTR_NULL(l->first->previous); CU_ASSERT_PTR_NULL(l->last->next); CU_ASSERT_PTR_EQUAL(l->last->data, (void *)2); list_append(l, (void *)3); CU_ASSERT_PTR_EQUAL(l->first->next, l->last->previous); CU_ASSERT_PTR_EQUAL(l->last->previous->previous, l->first); CU_ASSERT_PTR_NULL(l->first->previous); CU_ASSERT_PTR_NULL(l->last->next); CU_ASSERT_PTR_EQUAL(l->last->data, (void *)3); list_free(l); } void test_list_delete_item(void) { struct list *l = list_new(); list_append(l, (void *)1); list_delete_item(l, l->first); CU_ASSERT_PTR_NULL(l->first); CU_ASSERT_PTR_NULL(l->last); list_append(l, (void *)1); list_append(l, (void *)2); list_append(l, (void *)3); list_delete_item(l, l->first->next); CU_ASSERT_PTR_EQUAL(l->first->next, l->last); CU_ASSERT_PTR_EQUAL(l->last->previous, l->first); CU_ASSERT_PTR_EQUAL(l->first->data, (void *)1) CU_ASSERT_PTR_EQUAL(l->last->data, (void *)3) list_free(l); } void test_list_delete(void) { struct list *l = list_new(); list_append(l, (void *)1); list_append(l, (void *)2); list_append(l, (void *)3); list_delete(l, (void *)2); CU_ASSERT_PTR_EQUAL(l->first->next, l->last); CU_ASSERT_PTR_EQUAL(l->last->previous, l->first); CU_ASSERT_PTR_EQUAL(l->first->data, (void *)1) CU_ASSERT_PTR_EQUAL(l->last->data, (void *)3) list_delete(l, (void *)1); list_delete(l, (void *)3); CU_ASSERT(list_length(l) == 0); list_delete(l, (void *)4); list_free(l); } void test_list_find(void) { struct list *l = list_new(); list_append(l, (void *)1); list_append(l, (void *)2); list_append(l, (void *)3); CU_ASSERT_PTR_EQUAL(list_find(l, (void *)2), l->first->next); CU_ASSERT_PTR_NULL(list_find(l, (void *)4)); CU_ASSERT_PTR_NULL(list_find(l, NULL)); list_free(l); } CU_TestInfo list_tests[] = { { "test_list_new", test_list_new }, { "test_list_copy", test_list_copy }, { "test_list_free", test_list_free }, { "test_list_length", test_list_length }, { "test_list_append", test_list_append }, { "test_list_delete_item", test_list_delete_item }, { "test_list_delete", test_list_delete }, { "test_list_find", test_list_find }, CU_TEST_INFO_NULL, }; vlock-2.2.2/tests/test_list.h000066400000000000000000000000411101377123600161540ustar00rootroot00000000000000extern CU_TestInfo list_tests[]; vlock-2.2.2/tests/test_process.c000066400000000000000000000060321101377123600166600ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "process.h" #include "test_process.h" void test_wait_for_death(void) { pid_t pid = fork(); if (pid == 0) { usleep(20000); execl("/bin/true", "/bin/true", NULL); _exit(1); } CU_ASSERT(!wait_for_death(pid, 0, 2000)); CU_ASSERT(wait_for_death(pid, 0, 20000)); } void test_ensure_death(void) { pid_t pid = fork(); if (pid == 0) { signal(SIGTERM, SIG_IGN); signal(SIGHUP, SIG_IGN); execl("/bin/true", "/bin/true", NULL); _exit(0); } ensure_death(pid); CU_ASSERT(waitpid(pid, NULL, WNOHANG) < 0); CU_ASSERT(errno == ECHILD); } int child_function(void *a) { char *s = a; ssize_t l = strlen(s); char buffer[LINE_MAX]; ssize_t l_in; ssize_t l_out; if (write(STDOUT_FILENO, s, l) < l) return 1; l_in = read(STDIN_FILENO, buffer, sizeof buffer); if (l_in <= 0) return 1; l_out = write(STDOUT_FILENO, buffer, l_in); if (l_out != l_in) return 1; return 0; } void test_create_child_function(void) { char *s1 = "hello"; char *s2 = "world"; ssize_t l1 = strlen(s1); ssize_t l2 = strlen(s2); struct child_process child = { .function = child_function, .argument = s1, .stdin_fd = REDIRECT_PIPE, .stdout_fd = REDIRECT_PIPE, .stderr_fd = REDIRECT_DEV_NULL, }; int status; char buffer[LINE_MAX]; CU_ASSERT(create_child(&child)); CU_ASSERT(child.pid > 0); CU_ASSERT(read(child.stdout_fd, buffer, sizeof buffer) == l1); CU_ASSERT(strncmp(buffer, s1, l1) == 0); CU_ASSERT(waitpid(child.pid, NULL, WNOHANG) == 0); CU_ASSERT(write(child.stdin_fd, s2, l2) == l2); CU_ASSERT(read(child.stdout_fd, buffer, sizeof buffer) == l2); CU_ASSERT(strncmp(buffer, s2, l2) == 0); CU_ASSERT(waitpid(child.pid, &status, 0) == child.pid); CU_ASSERT(WIFEXITED(status)); CU_ASSERT(WEXITSTATUS(status) == 0); (void) close(child.stdin_fd); (void) close(child.stdout_fd); } void test_create_child_process(void) { const char *s1 = "hello\n"; const char *s2 = "olleh\n"; ssize_t l1 = strlen(s1); ssize_t l2 = strlen(s2); const char *argv[] = { "sh", "-c", "rev", NULL }; struct child_process child = { .path = "/bin/sh", .argv = argv, .stdin_fd = REDIRECT_PIPE, .stdout_fd = REDIRECT_PIPE, .stderr_fd = REDIRECT_DEV_NULL, .function = NULL, }; char buffer[LINE_MAX]; CU_ASSERT(create_child(&child)); CU_ASSERT(write(child.stdin_fd, s1, l1) == l1); (void) close(child.stdin_fd); CU_ASSERT(read(child.stdout_fd, buffer, sizeof buffer) == l2); (void) close(child.stdout_fd); CU_ASSERT(strncmp(buffer, s2, l2) == 0); CU_ASSERT(wait_for_death(child.pid, 0, 0)); } CU_TestInfo process_tests[] = { { "test_wait_for_death", test_wait_for_death }, { "test_ensure_death", test_ensure_death }, { "test_create_child_function", test_create_child_function }, { "test_create_child_process", test_create_child_process }, CU_TEST_INFO_NULL, }; vlock-2.2.2/tests/test_process.h000066400000000000000000000000441101377123600166620ustar00rootroot00000000000000extern CU_TestInfo process_tests[]; vlock-2.2.2/tests/test_tsort.c000066400000000000000000000054121101377123600163560ustar00rootroot00000000000000#include #include #include "list.h" #include "tsort.h" #include "test_tsort.h" #define A ((void *)1) #define B ((void *)2) #define C ((void *)3) #define D ((void *)4) #define E ((void *)5) #define F ((void *)6) #define G ((void *)7) #define H ((void *)8) struct edge *make_edge(void *p, void *s) { struct edge *e = malloc(sizeof *e); e->predecessor = p; e->successor = s; return e; } bool item_preceeds(struct list_item *first, struct list_item *second) { for (struct list_item *item = first->next; item != NULL; item = item->next) if (item == second) return true; return false; } void test_tsort(void) { struct list *list = list_new(); struct list *edges = list_new(); struct list *faulty_edges = list_new(); struct list *sorted_list; list_append(list, A); list_append(list, B); list_append(list, C); list_append(list, D); list_append(list, E); list_append(list, F); list_append(list, G); list_append(list, H); /* Check item_preceeds: */ CU_ASSERT(item_preceeds(list_find(list, A), list_find(list, H))); /* Edges: * * E * | * B C D H * \|/ | * A F G */ list_append(edges, make_edge(A, B)); list_append(edges, make_edge(A, C)); list_append(edges, make_edge(A, D)); list_append(edges, make_edge(B, E)); list_append(edges, make_edge(G, H)); sorted_list = tsort(list, edges); CU_ASSERT(list_length(edges) == 0); CU_ASSERT_PTR_NOT_NULL(sorted_list); CU_ASSERT_EQUAL(list_length(list), list_length(sorted_list)); /* Check that all items from the original list are in the * sorted list. */ list_for_each(list, item) CU_ASSERT_PTR_NOT_NULL(list_find(sorted_list, item->data)); CU_ASSERT(item_preceeds(list_find(list, A), list_find(list, B))); CU_ASSERT(item_preceeds(list_find(list, A), list_find(list, C))); CU_ASSERT(item_preceeds(list_find(list, A), list_find(list, D))); CU_ASSERT(item_preceeds(list_find(list, B), list_find(list, E))); CU_ASSERT(item_preceeds(list_find(list, G), list_find(list, H))); /* Faulty edges: same as above but F wants to be below A and above E. */ list_append(faulty_edges, make_edge(A, B)); list_append(faulty_edges, make_edge(A, C)); list_append(faulty_edges, make_edge(A, D)); list_append(faulty_edges, make_edge(B, E)); list_append(faulty_edges, make_edge(E, F)); list_append(faulty_edges, make_edge(F, A)); list_append(faulty_edges, make_edge(G, H)); CU_ASSERT_PTR_NULL(tsort(list, faulty_edges)); CU_ASSERT(list_length(faulty_edges) > 0); list_delete_for_each(faulty_edges, edge_item) free(edge_item->data); list_free(sorted_list); list_free(edges); list_free(faulty_edges); list_free(list); } CU_TestInfo tsort_tests[] = { { "test_tsort", test_tsort }, CU_TEST_INFO_NULL, }; vlock-2.2.2/tests/test_tsort.h000066400000000000000000000000421101377123600163550ustar00rootroot00000000000000extern CU_TestInfo tsort_tests[]; vlock-2.2.2/tests/test_util.c000066400000000000000000000013331101377123600161560ustar00rootroot00000000000000#include #include #include #include "util.h" #include "test_util.h" void test_parse_timespec(void) { struct timespec *t = parse_seconds("123"); CU_ASSERT_PTR_NOT_NULL(t); CU_ASSERT(t->tv_sec == 123); CU_ASSERT(t->tv_nsec == 0); free(t); #if 0 /* Fractions are not supported, yet. */ t = parse_seconds("123.4"); CU_ASSERT_PTR_NOT_NULL(t); CU_ASSERT(t->tv_sec == 123); CU_ASSERT(t->tv_nsec == 400000); free(t); #else CU_ASSERT_PTR_NULL(parse_seconds("123.4")); #endif CU_ASSERT_PTR_NULL(parse_seconds("-1")); CU_ASSERT_PTR_NULL(parse_seconds("hello")); } CU_TestInfo util_tests[] = { { "test_parse_timespec", test_parse_timespec }, CU_TEST_INFO_NULL, }; vlock-2.2.2/tests/test_util.h000066400000000000000000000000411101377123600161560ustar00rootroot00000000000000extern CU_TestInfo util_tests[]; vlock-2.2.2/tests/vlock-test.c000066400000000000000000000026701101377123600162420ustar00rootroot00000000000000#include #include #include #include #include "test_list.h" #include "test_tsort.h" #include "test_util.h" #include "test_process.h" CU_SuiteInfo vlock_test_suites[] = { { "test_list" , NULL, NULL, list_tests }, { "test_tsort", NULL, NULL, tsort_tests }, { "test_util", NULL, NULL, util_tests }, { "test_process", NULL, NULL, process_tests }, CU_SUITE_INFO_NULL, }; int main(int __attribute__((unused)) argc, const char *argv[]) { char *output_mode = getenv("VLOCK_TEST_OUTPUT_MODE"); if (CU_initialize_registry() != CUE_SUCCESS) { fprintf(stderr, "%s: CUnit initialization failed\n", argv[0]); exit(EXIT_FAILURE); } if (CU_register_suites(vlock_test_suites) != CUE_SUCCESS) { fprintf(stderr, "%s: registering test suites failed: %s\n", argv[0], CU_get_error_msg()); goto error; } if (output_mode != NULL) { if (strcmp(output_mode, "verbose") == 0) CU_basic_set_mode(CU_BRM_VERBOSE); else if (strcmp(output_mode, "normal") == 0) CU_basic_set_mode(CU_BRM_NORMAL); else if (strcmp(output_mode, "silent") == 0) CU_basic_set_mode(CU_BRM_SILENT); } if (CU_basic_run_tests() != CUE_SUCCESS) { fprintf(stderr, "%s: running tests failed\n", argv[0]); goto error; } if (CU_get_number_of_tests_failed() > 0) goto error; CU_cleanup_registry(); exit(EXIT_SUCCESS); error: CU_cleanup_registry(); exit(EXIT_FAILURE); }