pax_global_header00006660000000000000000000000064130414276040014513gustar00rootroot0000000000000052 comment=b28ef55bd3207a2878f0a2fbedf8bd5564cc764e nodm-0.13/000077500000000000000000000000001304142760400123735ustar00rootroot00000000000000nodm-0.13/AUTHORS000066400000000000000000000001121304142760400134350ustar00rootroot00000000000000Enrico Zini Joachim Breitner nodm-0.13/COPYING000066400000000000000000000431031304142760400134270ustar00rootroot00000000000000 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 Lesser 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. nodm-0.13/ChangeLog000066400000000000000000000660201304142760400141510ustar00rootroot000000000000002017-01-23 17:29:34 +0100 Mike Gabriel (1633964) * release 0.13 (HEAD, master) 2017-01-23 17:04:49 +0100 Mike Gabriel (1523d43) * de-Debianize nodm upstream project (origin/master) 2017-01-23 13:12:41 +0100 Simon McVittie (52f7f07) * Add systemd support, via NMU to unstable (debian/0.12-1.1). 2016-03-24 18:46:16 +0100 Mike Gabriel (228cf9d) * debian/: Update .po files and template.pot. 2016-03-24 18:44:15 +0100 Mike Gabriel (31a6819) * debian/: Explain about how the NODM_X_OPTIONS env var has to be populated. 2016-03-24 17:12:32 +0100 Mike Gabriel (046f3c4) * upstream: Rename symbol "basename" to "nodm_basename". This is to avoid naming conflicts with the basename() function from . 2016-03-24 14:44:55 +0100 Mike Gabriel (29da1f6) * debian/changelog: Add post-upload closure for #638601. Bug has been closed manually meanwhile (on Thu 24th Mar 2016). (Closes: #638601). 2016-03-24 14:42:57 +0100 Mike Gabriel (683b6cb) * Continue development... 2016-03-23 16:27:13 +0100 Mike Gabriel (6f0b4c0) * upload to unstable (debian/0.12-1) (tag: debian/0.12-1) 2016-03-23 16:13:05 +0100 Mike Gabriel (f196c4f) * debian/changelog: add bug closure for #633089 2016-03-23 15:58:30 +0100 Mike Gabriel (deb67fa) * another debconf-updatepo run... 2016-03-23 15:57:30 +0100 Mike Gabriel (56d55e2) * Add EOL chars for help output of the --nested cmdline option. (Closes: #766123). 2016-03-23 15:55:10 +0100 Mike Gabriel (2c8f866) * upstream: Whitespace clean-up at EOL. 2016-03-23 15:52:23 +0100 Mike Gabriel (6dbdeb0) * Reword debconf question "Start nodm at boot" to "Enable nodm". (Closes: #633498). 2016-03-23 15:50:44 +0100 Mike Gabriel (1f960fd) * debian/nodm.init: Make NODM_OPTIONS configurable via /etc/default/nodm. (Closes: #766124). 2016-03-23 15:32:17 +0100 Mike Gabriel (71acb3a) * xserver.c: Don't FTBFS when built with -Werror=unused-result. (Closes: #638600). 2016-03-23 15:23:19 +0100 Mike Gabriel (cd598ea) * debian/source/format: Add this file and set it to "1.0" for now. 2016-03-23 15:21:46 +0100 Mike Gabriel (8766795) * debian/nodm.init: Provide LSB description header. 2016-03-23 15:20:21 +0100 Mike Gabriel (cecf69d) * debian/control: Use encrypted URLs for Vcs-*: fields. 2016-03-23 15:18:21 +0100 Mike Gabriel (4732321) * Harden the build of nodm using dpkg's buildflags.mk include. 2016-03-23 15:14:53 +0100 Mike Gabriel (be3dfde) * Bump to debhelper version level 9. 2016-03-23 15:12:07 +0100 Mike Gabriel (06738e3) * debian/control: Add B-D: automake. 2016-03-23 15:10:36 +0100 Mike Gabriel (b39b7ae) * debian/control: Add B-D: autoreconf. 2016-03-23 15:07:21 +0100 Mike Gabriel (909c41f) * Makefile.am: Add various build cruft files to DISTCLEANFILES. 2016-03-23 15:06:23 +0100 Mike Gabriel (96ac160) * upstream: Makefile.am: line-wrap variable assignments with file lists. 2016-03-23 14:55:54 +0100 Mike Gabriel (f50030c) * Acknowledge previous NMUs and work-in their changes (0.11-1.1, 0.11-1.2 and 0.11-1.3). Also work through all open bugs with patches and apply them if appropriate; see below for details. (Closes: #747077). 2016-03-23 14:46:02 +0100 Mike Gabriel (4edbde8) * Update debconf translations files after we have added a new configuration item. 2016-03-23 14:39:58 +0100 Mike Gabriel (1abd007) * Drop .gitignore file. Let's rather provide a working make (dist)clean. 2016-03-23 14:33:13 +0100 Mike Gabriel (8e93257) * debian/rules: Correctly evoke ./autogen.sh script at build time. 2016-03-23 14:29:33 +0100 Mike Gabriel (1f2cb0b) * debian/copyright: Make copyright file DEP-5 compliant. 2016-03-23 14:23:47 +0100 Mike Gabriel (d27477d) * debian/nodm.docs: README is now README.md. 2016-03-23 14:19:25 +0100 Mike Gabriel (f24be1f) * debian/copyright{,.in}: Provide auto-generated draft of a DEP-5 compliant copyright file. 2016-03-23 14:15:59 +0100 Mike Gabriel (fa594cd) * debian/control: Bump Standards: to 3.9.7. Required change: DEP-5 compliant copyright file. 2016-03-23 14:13:12 +0100 Simon McVittie (6cdd17f) * debian/rules: Rely on LSB headers for dependency/sequence info. (Closes: #584179). 2016-03-23 14:08:29 +0100 Chris Lamb (0387c7b) * Make initscript less chatty. (Closes: #638293). 2016-03-23 14:00:33 +0100 Sjoerd Simons (28db839) * debian/: Add support for NOXM_X_TIMEOUT via debconf. (Closes: #704128). 2016-03-23 13:57:17 +0100 Emanuele Aina (7419b08) * debian/nodm.pam: Fix PAM based consolekit integration of sessions. (Closes: #680264). 2016-03-23 13:53:17 +0100 Mike Gabriel (5b01f20) * README.md: Fix miscounting of amount of environment variables. (Closes: #746700). Thanks to Dan Jacobson for pointing this out. 2016-03-23 13:50:07 +0100 Mike Gabriel (bfaaee6) * Makefile.*: Place libraries in LIBS, no LDFLAGS. (LP:#771123). Thanks to Ilya Barygin for providing the fix in Ubuntu. 2016-03-23 13:45:12 +0100 Chris Lamb (d212290) * debian/nodm.init: Wait for nodm to exit. (Closes: #638290). 2016-03-23 13:37:52 +0100 Mike Gabriel (a15a23f) * debian/po: Add Japanese DebConf translation file. Thanks to "victory" for providing it. (Closes: #718920). 2016-03-23 13:25:20 +0100 Niklas Fiekas (4292731) * debian/nodm.init: Support "status" argument in nodm init script. (Closes: #753304). 2016-03-23 13:22:17 +0100 Mike Gabriel (2d2e8c4) * xsession-child.c: Set PAM_XDISPLAY pam item. (Closes: #713960). Thanks to Sjoerd Simons for providing the patch. 2016-03-23 13:19:00 +0100 Emanuele Aina (9b8ec1e) * debian/control: Drop x11-xserver-utils dependency. (Closes: #680269). 2016-03-23 13:06:02 +0100 Simon McVittie (75bda7b) * debian/control: Add D (nodm): xserver-xorg. (Closes: #689703). 2016-03-23 13:04:26 +0100 Mike Gabriel (8cc5ee9) * debian/control: Process with wrap-and-sort. 2016-03-23 13:02:03 +0100 Mike Gabriel (b21274d) * upstream: Bump upstream version to 0.12. Become new co-maintainer of nodm in Debian. (Closes: #813210). 2014-08-27 11:22:22 +0200 Mike Gabriel (2a7f721) * import NMU upload unstable (0.11-1.3) 2014-08-27 11:22:02 +0200 Mike Gabriel (1e4287e) * import NMU upload unstable (0.11-1.2) 2014-08-27 11:21:50 +0200 Mike Gabriel (a7421de) * import NMU upload unstable (0.11-1.1) 2016-02-27 16:34:23 +0100 Enrico Zini (921db7e) * Reformatted README as markdown 2011-11-08 17:09:55 +0100 Enrico Zini (cf5a58b) * Provides x-display-manager 2011-08-17 14:44:56 +0200 Enrico Zini (58fd03b) * Close both duplicates (tag: debian/0.11-1, with-nmu) 2011-08-17 14:38:24 +0200 Enrico Zini (e28adca) * Fixed typo 2011-08-17 13:59:30 +0200 Enrico Zini (6d00951) * Updated potfiles 2011-08-17 13:57:53 +0200 Enrico Zini (8ebb1d8) * Updated version number 2011-08-17 13:56:14 +0200 Enrico Zini (2f593f3) * Removed nodm/xinit form debconf and updated debconf templates 2011-08-17 13:54:46 +0200 Enrico Zini (db829a5) * do not force "-nolisten tcp". Closes: #635992 2011-08-17 13:37:27 +0200 Enrico Zini (0e78a64) * Do not require $NODM_XINIT to exist in order to start: that env var is now ignored. Closes: #634901 2011-07-19 23:38:55 +0200 Enrico Zini (5417138) * default X start timeout increased to 30 seconds. Closes: #633089; X start timeout configurable via NODM_X_TIMEOUT (tag: debian/0.10-1) 2011-07-09 11:20:45 +0100 Enrico Zini (3ad8f4d) * Reenabled restart previously disabled during testing and getting committed by mistake (tag: debian/0.9-1) 2011-07-09 10:36:09 +0100 Enrico Zini (71c15d0) * Merge branch 'master' of git://anonscm.debian.org/pkg-fso/nodm 2011-07-09 11:35:13 +0200 Enrico Zini (1e82cc3) * Updated version number and changelog 2011-07-09 10:31:55 +0100 Enrico Zini (0946e75) * Merge branch 'master' of git://anonscm.debian.org/pkg-fso/nodm 2011-07-09 10:31:35 +0100 Enrico Zini (2c4dcd3) * Some cargoculting from xinit 2011-07-09 11:30:43 +0200 Enrico Zini (2ebe57e) * Keep a connection open when the X server is started 2011-07-09 10:39:21 +0200 Enrico Zini (1e42ea5) * Updated tests to account for '-nolisten tcp' injection 2011-07-09 09:15:29 +0100 Enrico Zini (14c36fe) * Fix exit reporting messages 2011-07-09 09:15:10 +0100 Enrico Zini (bdff56b) * Add -nolisten tcp to X options if missing 2011-07-09 01:01:45 +0100 Enrico Zini (387dda5) * More log improvements 2011-07-09 01:00:36 +0100 Enrico Zini (104d4e3) * Log timestamps on stderr 2011-07-09 01:10:50 +0200 Enrico Zini (b645dd1) * Fixed log level enforcement 2011-07-09 00:09:12 +0100 Enrico Zini (27046d4) * Verbose logging tweaks 2011-07-09 00:55:21 +0200 Enrico Zini (a22b3b9) * Cleaned up log interface and exposed more logging options on command line 2011-07-08 19:47:10 +0200 Enrico Zini (a30dbc0) * Better server and session quit report 2011-07-08 18:46:33 +0100 Enrico Zini (89bba9b) * Start the right X according to $DISPLAY 2011-07-08 17:40:17 +0100 Enrico Zini (77771d9) * Removed leftovers from root check 2011-07-08 17:37:35 +0100 Enrico Zini (1b49a85) * Adapt tests according to what is possible to run 2011-07-08 17:25:44 +0100 Enrico Zini (bed348e) * Log when terminating X because we got killed 2011-07-08 17:25:19 +0100 Enrico Zini (5d019bd) * Added missing header 2011-07-08 16:38:19 +0100 Enrico Zini (59711ec) * Fixed a bogus compiler warning with older gccs 2011-07-07 17:04:36 +0200 Enrico Zini (e76eaf5) * Added pkg-config dependency (tag: debian/0.8-1) 2011-07-07 16:51:55 +0200 Enrico Zini (ef10b73) * Removed obsolete TODO 2011-07-07 16:47:24 +0200 Enrico Zini (86b8037) * Aehm, fixed version typo 2011-07-07 16:44:01 +0200 Enrico Zini (97cd630) * Read to upload 2011-07-07 01:56:12 +0200 Enrico Zini (37997df) * New version number and updated changelog 2011-07-07 01:42:40 +0200 Enrico Zini (b192f37) * Merge branch 'bug540201' 2011-07-07 01:39:58 +0200 Enrico Zini (bd547c0) * Revert "Included upstart job (Closes: #602511). Thanks to Luigi Capriotti for providing it." 2011-07-07 01:35:29 +0200 Enrico Zini (b93172e) * Dropped xinit dependency: we don't need it anymore 2011-07-06 21:13:28 +0200 Enrico Zini (7a268a7) * Disable tests for now 2011-07-06 20:57:21 +0200 Enrico Zini (0d097f7) * Block all signals in the monitor process by default, unblocking quit signals when we can catch it and exit 2011-07-06 18:07:16 +0200 Enrico Zini (4d5ceee) * Deal with killing signals arriving between the fork and when we set up the catching machinery in the parent 2011-07-06 17:57:37 +0200 Enrico Zini (fe92042) * Wait for X server and X session after stopping them 2011-07-06 17:46:15 +0200 Enrico Zini (361289e) * Implemented --nested to be a user session manager for nested X servers 2011-07-06 17:20:40 +0200 Enrico Zini (df7e033) * Updated README 2011-07-06 17:20:35 +0200 Enrico Zini (bac4dd3) * Cleaned up wait/restart loop 2011-07-06 15:36:56 +0200 Enrico Zini (e1626ac) * Moved connection to X server to child initialization code 2011-07-06 15:01:27 +0200 Enrico Zini (ee24a9d) * Do not set DISPLAY on xserver, as it inteferes with what is needed by Xnest 2011-07-06 13:00:56 +0200 Enrico Zini (57ac67b) * Cleaned up VT allocation code 2011-07-06 11:43:16 +0200 Enrico Zini (6c46219) * More session test cases 2011-07-06 11:32:51 +0200 Enrico Zini (a92acb4) * Fixed two small memory leaks 2011-07-06 11:25:57 +0200 Enrico Zini (b43f2d0) * Prevent killing unstarted processed (marked by (pid_d)-1, ouch) 2011-07-06 11:15:51 +0200 Enrico Zini (86710bf) * Also set server name when parsing x server command line 2011-07-06 10:38:12 +0200 Enrico Zini (11bdc2d) * Collected common test code in a test module 2011-07-06 10:18:25 +0200 Enrico Zini (8de41af) * Added strerror-like function for nodm error codes 2011-07-06 01:58:24 +0200 Enrico Zini (85bf84f) * Run a small xnest 2011-07-06 01:58:04 +0200 Enrico Zini (939d92e) * Default to current user if conf_run_as is empty 2011-07-06 01:46:38 +0200 Enrico Zini (a20ec82) * Display manager start/stop/wait functions 2011-07-05 19:45:55 +0200 Enrico Zini (f7b8979) * Made child session body overridable for tests 2011-07-05 19:42:03 +0200 Enrico Zini (6c296c1) * Move session setup code to child process 2011-07-05 19:29:25 +0200 Enrico Zini (284d64e) * More code cleanup, split child code from server code 2011-07-05 14:37:27 +0200 Enrico Zini (d799d46) * Read session command as config 2011-07-05 14:23:47 +0200 Enrico Zini (2eaffc8) * Make parse_cmdline testable, test it and fix it 2011-07-05 13:39:37 +0200 Enrico Zini (d316d6f) * More session refactoring, unified error codes 2011-07-05 10:57:49 +0200 Enrico Zini (dedf4ab) * Started testing internals and fixed getenv_with_default 2011-07-05 01:33:12 +0200 Enrico Zini (60374b8) * Refactored as a proper display manager (testing still needed) 2011-07-05 00:11:34 +0200 Enrico Zini (af0f171) * Added functions to stop the X server 2011-07-05 00:02:57 +0200 Enrico Zini (bfcbf16) * Implement reading window path 2011-07-04 22:32:38 +0200 Enrico Zini (5bf5cd0) * renamed sstart in server 2011-07-04 22:26:09 +0200 Enrico Zini (7950d98) * Try connecting to the server and set DISPLAY 2011-07-04 21:37:12 +0200 Enrico Zini (d106da6) * Use a struct to preserve the pid of the X server 2011-07-04 20:01:29 +0200 Enrico Zini (59dd50e) * New function to start X server and wait for it to be ready 2011-07-04 16:07:37 +0200 Enrico Zini (811d1fc) * Started cleanup/rewrite towards 540201 2011-02-11 11:18:54 +0100 Marco Amadori (5cc4b68) * Check if plymouth is available before using it. 2011-02-11 11:21:09 +0100 Marco Amadori (4156de2) * Removed mixed tab and spaces in debian scripts. 2011-02-11 14:58:18 +0100 Marco Amadori (b2b8d58) * Build ./configure if missing. 2011-02-11 12:36:39 +0100 Marco Amadori (489759f) * Upgraded debian/rules to debhelper >= 7.0.50. 2011-02-11 11:58:34 +0100 Marco Amadori (485a548) * Update Standards-Version to 3.9.1 (no changes required). 2011-02-08 23:27:12 +0530 Joachim Breitner (b7c982a) * Include l10n changes from previous NMU 2011-02-08 23:18:37 +0530 Joachim Breitner (8848717) * Included upstart job (Closes: #602511). Thanks to Luigi Capriotti for providing it. 2010-05-23 15:17:19 +0100 Enrico Zini (7ff6a1a) * Updated Swedish translation; thanks to Arne Anka and Martin Bagge. Closes: #539075 2010-05-23 15:13:31 +0100 Enrico Zini (4d35a41) * Do not depend on a terminal emulator. Closes: #561409. 2010-05-23 15:12:43 +0100 Enrico Zini (c058a98) * Actually releasing 0.7 2010-05-23 15:08:54 +0100 Enrico Zini (2df269e) * Added dependency on x11-xserver-utils. Closes: #559173. 2010-05-23 15:07:50 +0100 Enrico Zini (7cb7c0e) * Do not build on freebsd until someone comes up with a patch (see #575265). 2010-05-23 15:01:51 +0100 Enrico Zini (3ec3d90) * Fix configure.ac PAM bits. Thanks to Mathieu Bridon and Sebastian Dziallas. 2009-11-11 00:04:15 +0000 Enrico Zini (0af8c7a) * Updated .gitignore 2009-11-11 00:03:54 +0000 Enrico Zini (76ce13e) * Added /etc/insserv.conf.d/nodm. Closes: #554840. 2009-09-29 11:30:13 +0100 Enrico Zini (c36f1c7) * Updated translations. 2009-09-29 11:19:29 +0100 Enrico Zini (399962b) * Added hal and bluetooth to initscript Should-Start. Closes: #548776. 2009-09-29 11:17:07 +0100 Enrico Zini (4b005e9) * Added kdb to initscript Should-Start:. Closes: #548104. Thanks to Michael Schutte. 2009-07-26 20:40:26 +0200 Enrico Zini (672bb50) * Try harder to open the console device. Closes: #538640. (tag: debian/0.6-1) 2009-07-24 01:57:06 +0200 Enrico Zini (e2224ea) * Release to unstable (tag: debian/0.5-1) 2009-07-24 01:53:08 +0200 Enrico Zini (13e570f) * Updated standards-version 2009-07-24 01:51:33 +0200 Enrico Zini (8d5ff0c) * Ran debconf-updatepo 2009-07-24 01:31:16 +0200 Enrico Zini (6b59ed6) * Also export NODM_FIRST_VT 2009-07-24 01:20:17 +0200 Enrico Zini (cb3641e) * Add the new config file entry to /etc/default/nodm 2009-07-24 01:11:15 +0200 Enrico Zini (5724c98) * Add missing space 2009-07-24 01:02:56 +0200 Enrico Zini (86e1c10) * Merged changes from master 2009-07-24 01:02:15 +0200 Enrico Zini (afd2e86) * Removed conflicts with the other X display managers 2009-07-24 01:01:32 +0200 Enrico Zini (d360c4c) * Provide a migration path from older nodm 2009-07-24 00:15:42 +0200 Enrico Zini (9e8bb51) * Added changelog entry 2009-07-24 00:14:31 +0200 Enrico Zini (c5471b1) * Add adding first_vt to the migration path 2009-07-24 00:11:06 +0200 Enrico Zini (4d6645e) * Fixed undo artifact 2009-07-24 00:08:02 +0200 Enrico Zini (e75cd83) * Make the starting VT configurable 2009-07-23 23:32:54 +0200 Enrico Zini (75a6944) * Removed useless check 2009-07-23 22:31:51 +0200 Enrico Zini (6695887) * Do not write to strings recursively 2009-07-23 21:09:25 +0200 Enrico Zini (ebb4698) * First attempt at vt allocation 2009-07-06 19:54:22 +0100 Enrico Zini (d18f5eb) * Merged branch with cleanup of ~/.xsession-error 2009-07-06 19:51:57 +0100 Enrico Zini (1f3d735) * Stricter permissions on ~/.xsession-errors 2009-07-06 10:24:47 +0200 Enrico Zini (959a759) * Clean up ~/.xsession-errors 2009-05-14 14:57:40 +0100 Enrico Zini (317a201) * Added autogen.sh to EXTRA_DIST 2009-05-14 14:41:52 +0100 Enrico Zini (4a5fe85) * Update version number in configure.ac 2009-05-14 14:41:07 +0100 Enrico Zini (e4626bc) * Release to unstable 2009-05-14 13:57:30 +0100 Enrico Zini (57d071a) * Remove config file after the daemon is shut down 2009-05-14 13:56:14 +0100 Enrico Zini (b4d25ad) * Fixed version number 2009-05-14 13:41:01 +0100 Enrico Zini (64f50bf) * Remove /etc/default/nodm on purge. Closes: #527379. 2009-05-14 13:20:57 +0100 Enrico Zini (aadb013) * The session starter is now nodm itself: fix test script 2009-05-14 13:16:10 +0100 Enrico Zini (a35e997) * Set USERNAME, PWD and SHELL 2009-05-14 10:36:26 +0100 Enrico Zini (7dd44ec) * Merged translation changes from bubulle 2009-04-29 22:04:54 +0200 Joachim Breitner (c197411) * Tag for release (tag: nodm/0.3-2) 2009-04-29 22:00:07 +0200 Joachim Breitner (9157e40) * fix changelog entry 2009-04-29 21:59:11 +0200 Martin Bagge (becbd86) * Updated Swedish translation for debconf messages 2009-04-29 21:57:29 +0200 Traduz - Portuguese Translation Team (7824f35) * Updated Portuguese translation for debconf messages 2009-04-29 21:55:31 +0200 Christian Perrier (968e121) * Debconf templates and debian/control review 2009-04-29 21:51:00 +0200 Joachim Breitner (1a6c8c4) * Merge branch 'master'; commit 'alioth/master' 2009-04-16 14:18:12 +0100 Enrico Zini (9178830) * Set the maintainer to be the pkg-fso list 2009-04-14 10:25:30 +0200 Joachim Breitner (951d195) * Tag for release (tag: nodm/0.3-1) 2009-04-14 10:20:04 +0200 Joachim Breitner (37a0e09) * bump version number in configure.ac 2009-04-10 20:37:19 +0200 Joachim Breitner (2406af8) * Tag for release (tag: nodm/0.3) 2009-04-10 20:36:27 +0200 Joachim Breitner (f131f4c) * Bump standards version, no change 2009-04-10 20:33:17 +0200 Joachim Breitner (b413551) * Include Swedish strings for nodm debconf, thanks to Martin Bagge for the translation. (Closes: #522976) 2009-04-10 20:31:31 +0200 Joachim Breitner (1623ee4) * Fix "initscript ignores NODM_ENABLED=false" by removing bashism, thanks to Justin B Rye for the patch (Closes: #523004) 2009-02-25 09:38:27 +0000 Enrico Zini (9b91fa4) * Added autogen.sh 2009-02-23 20:58:10 +0000 Enrico Zini (ac36099) * Ran debconf-updatepo 2009-02-23 20:56:46 +0000 Enrico Zini (99b1a95) * Fixed template descriptions after lintian suggestions 2009-02-23 20:51:45 +0000 Enrico Zini (12ee186) * Upload to unstable 2009-02-23 20:51:22 +0000 Enrico Zini (00b0660) * Added {shlibs,misc}:Depends 2009-02-23 20:45:50 +0000 Enrico Zini (11d905c) * Build and install a manpage 2009-02-23 20:44:32 +0000 Enrico Zini (3e5831b) * Allow to run --help and --version as non-root 2009-02-23 20:34:38 +0000 Enrico Zini (d5e1123) * Added manpage 2009-02-23 17:51:47 +0000 Enrico Zini (76526e2) * Provide an upgrade path for the openmoko (tag: nodm/0.2) 2009-02-23 17:31:11 +0000 Enrico Zini (da9f172) * Added pam configuration 2009-02-23 16:45:34 +0000 Enrico Zini (3795be1) * Updated TODO 2009-02-23 16:42:23 +0000 Enrico Zini (86faae2) * Added .gitignore 2009-02-23 16:40:16 +0000 Enrico Zini (fb92bff) * Added the usual paperwork 2009-02-23 16:39:12 +0000 Enrico Zini (255a133) * Run self as session 2009-02-23 16:32:25 +0000 Enrico Zini (212f2b3) * Documented implementation details 2009-02-23 16:32:01 +0000 Enrico Zini (0786442) * Install documentation, let autotools install the rest 2009-02-23 14:52:54 +0000 Enrico Zini (10b16b0) * Autotoolized 2009-02-22 22:21:56 +0000 Enrico Zini (eb91e39) * Build-depend on libpam0g-dev 2009-02-22 14:03:05 +0000 Enrico Zini (75789f1) * Make tests work with nodm using exec 2009-02-22 13:54:44 +0000 Enrico Zini (0270b18) * Exec the command in the subshell 2009-02-22 13:48:46 +0000 Enrico Zini (bedb722) * Updates to debian packaging 2009-02-20 18:06:50 +0000 Enrico Zini (f8c0a98) * More bits of #d-devel 2009-02-20 17:48:11 +0000 Enrico Zini (8ab57e9) * Complete redesign 2009-02-20 15:54:20 +0000 Enrico Zini (07844ad) * More notes on why X doesn't start for users 2009-02-20 14:41:23 +0000 Enrico Zini (9aa0ed2) * Read $NODM_COMMAND before setting up environment 2009-02-20 14:40:37 +0000 Enrico Zini (a752fe4) * Fixed path on executable check 2009-02-20 14:32:42 +0000 Enrico Zini (7a26a2c) * Adapted init script to NODM_COMMAND 2009-02-20 14:24:11 +0000 Enrico Zini (6d9a615) * Removed test examples from TODO now that we have a test script 2009-02-20 14:12:44 +0000 Enrico Zini (33b29c5) * Added a test script 2009-02-20 14:10:36 +0000 Enrico Zini (e62b9bb) * Setup the right environment 2009-02-20 13:39:38 +0000 Enrico Zini (0398daf) * unblock signals while waiting for retry time 2009-02-20 13:33:11 +0000 Enrico Zini (4f58fd3) * Run a single command, via the shell 2009-02-20 11:12:56 +0000 Enrico Zini (232eb01) * Do not clean up the environment 2009-02-19 14:56:38 +0000 Enrico Zini (9163f09) * TODO updated 2009-02-19 12:04:32 +0000 Enrico Zini (ff36fff) * Redone signal handling code 2009-02-19 12:04:07 +0000 Enrico Zini (28bb839) * Added some debugging infrastructure 2009-02-19 11:43:38 +0000 Enrico Zini (3939a77) * Compare properly 2009-02-19 11:39:28 +0000 Enrico Zini (6f64d08) * Log the command being run 2009-02-17 12:33:31 +0000 Enrico Zini (f4c6c7c) * Added a note with useful testing preseeds 2009-02-17 12:26:36 +0000 Enrico Zini (2dabf52) * Better error message 2009-02-17 12:02:19 +0000 Enrico Zini (0d3be51) * Don't ship the /etc/default file, but generate it 2009-02-16 19:00:07 +0000 Enrico Zini (b5b34b4) * Log when the session is restarted 2009-02-16 18:56:36 +0000 Enrico Zini (f5cafed) * Smarter retry times 2009-02-13 15:04:32 +0000 Enrico Zini (1429a73) * More work on debconf 2009-02-13 11:32:26 +0000 Enrico Zini (683f945) * It's /etc/default, not /etc/defaults 2009-02-02 19:10:13 +0000 Enrico Zini (3d8db15) * We are in 2009. 2009-02-02 19:06:07 +0000 Enrico Zini (afc5624) * Moved restart logic to C 2009-02-02 18:37:31 +0000 Enrico Zini (088ad8d) * Prefer xterm 2009-02-02 17:59:16 +0000 Enrico Zini (b3b9523) * Arch: any, and give a preferred x-terminal-emulator 2009-02-02 17:58:59 +0000 Enrico Zini (df034c5) * Added missing quotes 2009-02-02 14:51:54 +0000 Enrico Zini (f88b032) * Debianised using the new system 2009-02-02 13:50:52 +0000 Enrico Zini (2c1f2f8) * Added nodm script with restart logic 2009-02-02 13:36:05 +0000 Enrico Zini (0a92ccf) * Removed useless define 2009-02-02 13:29:52 +0000 Enrico Zini (5698a4f) * Collapsed a function ran only once 2009-02-02 12:44:36 +0000 Enrico Zini (5ef75af) * Simplify more since we are always run by root 2009-02-02 12:38:28 +0000 Enrico Zini (437807d) * Removed SYSLOG macro 2009-02-02 12:35:10 +0000 Enrico Zini (a71dd29) * Simplified by enforcing to be only run by root 2009-02-02 12:13:24 +0000 Enrico Zini (38d6446) * Started pam-helper as a fork of su 2008-10-08 20:22:48 +0200 Joachim Breitner (2eba48d) * Depend on x-terminal-emulator (tag: nodm/0.1) 2008-10-07 22:47:41 +0200 Joachim Breitner (8717aaf) * TODO list 2008-10-07 22:45:20 +0200 Joachim Breitner (3103a55) * Conflict with zhone-session (tag: debian/0.1) 2008-10-07 22:44:09 +0200 Joachim Breitner (c1e1747) * tag for release 2008-10-07 22:43:02 +0200 Joachim Breitner (7a6e7e5) * Do not complain at start when it’s already started 2008-10-07 22:37:53 +0200 Joachim Breitner (ca4fc17) * Install at runlevel 30/01, as gdm does 2008-10-07 20:31:56 +0200 Joachim Breitner (70c5c81) * Package change to nodm 2008-10-07 20:23:17 +0200 Joachim Breitner (fe92490) * remove matchbox-keyboard-toggle 2008-09-20 23:32:23 +0200 Joachim Breitner (ac99d41) * tag for release (tag: debian/0.4) 2008-09-04 16:52:15 +0200 Joachim Breitner (78ecbe5) * Start xinit inside su 2008-09-04 16:51:53 +0200 Joachim Breitner (adb349a) * Add comment about /etc/X11/Xwrapper.config 2008-09-04 16:26:44 +0200 Joachim Breitner (f301a78) * Remove pidfile after stopping 2008-09-04 16:25:44 +0200 Joachim Breitner (1483281) * Use su -l to start session 2008-09-03 22:21:35 +0200 Joachim Breitner (34312f5) * tag for release (tag: debian/0.3) 2008-09-01 14:19:45 +0200 Luca Capello (57a987c) * debian/control: Depends: on xinit and x11-common for the init script 2008-09-01 14:17:19 +0200 Luca Capello (1eecc78) * zhone-session.init: (Closes: #496344) check for all binaries available 2008-09-01 14:09:24 +0200 Luca Capello (53258ad) * zhone-session.init: fix indentation for 'case' statement 2008-09-01 14:08:11 +0200 Luca Capello (e8f222d) * Merge branch 'master' into gismo-master-fixes 2008-08-20 23:14:43 +0200 Joachim Breitner (a16f3aa) * Disable tcp listening for the X server 2008-08-19 00:27:25 +0200 Luca Capello (6f9b1de) * zhone-session: do not show cursor 2008-08-19 00:26:39 +0200 Luca Capello (b39ea47) * Merge branch 'master' into gismo-master-fixes 2008-08-16 15:47:33 -0300 Joachim Breitner (6c2985c) * Do not restat zhone-session on restart, to not kill X sessions 2008-08-16 13:44:47 -0300 Joachim Breitner (f9f0dca) * remove duplicated sentence 2008-08-16 13:36:33 -0300 Joachim Breitner (2bf7641) * copyright file 2008-08-16 13:35:17 -0300 Joachim Breitner (1705b3b) * common description footer 2008-08-12 20:17:27 -0300 Joachim Breitner (efc9918) * Depend on fso-frameworkd (tag: debian/0.2) 2008-08-11 03:43:06 +0200 Luca Capello (c5fdb8f) * debian/control: add Vcs-* fields 2008-08-10 19:46:51 -0300 Joachim Breitner (aaba60a) * tag for release 2008-08-10 19:46:31 -0300 Joachim Breitner (ea4c010) * Adjust paths 2008-08-10 19:38:58 -0300 Joachim Breitner (baf1905) * Symlink trick to make debhelper happy 2008-08-10 19:22:10 -0300 Joachim Breitner (eebb441) * initial commit nodm-0.13/INSTALL000066400000000000000000000224501304142760400134270ustar00rootroot00000000000000Installation Instructions ************************* Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is free documentation; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. Basic Installation ================== Briefly, the shell commands `./configure; make; make install' should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. The `configure' shell script attempts to guess correct values for various system-dependent variables used during compilation. It uses those values to create a `Makefile' in each directory of the package. It may also create one or more `.h' files containing system-dependent definitions. Finally, it creates a shell script `config.status' that you can run in the future to recreate the current configuration, and a file `config.log' containing compiler output (useful mainly for debugging `configure'). It can also use an optional file (typically called `config.cache' and enabled with `--cache-file=config.cache' or simply `-C') that saves the results of its tests to speed up reconfiguring. Caching is disabled by default to prevent problems with accidental use of stale cache files. If you need to do unusual things to compile the package, please try to figure out how `configure' could check whether to do them, and mail diffs or instructions to the address given in the `README' so they can be considered for the next release. If you are using the cache, and at some point `config.cache' contains results you don't want to keep, you may remove or edit it. The file `configure.ac' (or `configure.in') is used to create `configure' by a program called `autoconf'. You need `configure.ac' if you want to change it or regenerate `configure' using a newer version of `autoconf'. The simplest way to compile this package is: 1. `cd' to the directory containing the package's source code and type `./configure' to configure the package for your system. Running `configure' might take a while. While running, it prints some messages telling which features it is checking for. 2. Type `make' to compile the package. 3. Optionally, type `make check' to run any self-tests that come with the package. 4. Type `make install' to install the programs and any data files and documentation. 5. You can remove the program binaries and object files from the source code directory by typing `make clean'. To also remove the files that `configure' created (so you can compile the package for a different kind of computer), type `make distclean'. There is also a `make maintainer-clean' target, but that is intended mainly for the package's developers. If you use it, you may have to get all sorts of other programs in order to regenerate files that came with the distribution. 6. Often, you can also type `make uninstall' to remove the installed files again. Compilers and Options ===================== Some systems require unusual options for compilation or linking that the `configure' script does not know about. Run `./configure --help' for details on some of the pertinent environment variables. You can give `configure' initial values for configuration parameters by setting variables in the command line or in the environment. Here is an example: ./configure CC=c99 CFLAGS=-g LIBS=-lposix *Note Defining Variables::, for more details. Compiling For Multiple Architectures ==================================== You can compile the package for more than one kind of computer at the same time, by placing the object files for each architecture in their own directory. To do this, you can use GNU `make'. `cd' to the directory where you want the object files and executables to go and run the `configure' script. `configure' automatically checks for the source code in the directory that `configure' is in and in `..'. With a non-GNU `make', it is safer to compile the package for one architecture at a time in the source code directory. After you have installed the package for one architecture, use `make distclean' before reconfiguring for another architecture. Installation Names ================== By default, `make install' installs the package's commands under `/usr/local/bin', include files under `/usr/local/include', etc. You can specify an installation prefix other than `/usr/local' by giving `configure' the option `--prefix=PREFIX'. You can specify separate installation prefixes for architecture-specific files and architecture-independent files. If you pass the option `--exec-prefix=PREFIX' to `configure', the package uses PREFIX as the prefix for installing programs and libraries. Documentation and other data files still use the regular prefix. In addition, if you use an unusual directory layout you can give options like `--bindir=DIR' to specify different values for particular kinds of files. Run `configure --help' for a list of the directories you can set and what kinds of files go in them. If the package supports it, you can cause programs to be installed with an extra prefix or suffix on their names by giving `configure' the option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. Optional Features ================= Some packages pay attention to `--enable-FEATURE' options to `configure', where FEATURE indicates an optional part of the package. They may also pay attention to `--with-PACKAGE' options, where PACKAGE is something like `gnu-as' or `x' (for the X Window System). The `README' should mention any `--enable-' and `--with-' options that the package recognizes. For packages that use the X Window System, `configure' can usually find the X include and library files automatically, but if it doesn't, you can use the `configure' options `--x-includes=DIR' and `--x-libraries=DIR' to specify their locations. Specifying the System Type ========================== There may be some features `configure' cannot figure out automatically, but needs to determine by the type of machine the package will run on. Usually, assuming the package is built to be run on the _same_ architectures, `configure' can figure that out, but if it prints a message saying it cannot guess the machine type, give it the `--build=TYPE' option. TYPE can either be a short name for the system type, such as `sun4', or a canonical name which has the form: CPU-COMPANY-SYSTEM where SYSTEM can have one of these forms: OS KERNEL-OS See the file `config.sub' for the possible values of each field. If `config.sub' isn't included in this package, then this package doesn't need to know the machine type. If you are _building_ compiler tools for cross-compiling, you should use the option `--target=TYPE' to select the type of system they will produce code for. If you want to _use_ a cross compiler, that generates code for a platform different from the build platform, you should specify the "host" platform (i.e., that on which the generated programs will eventually be run) with `--host=TYPE'. Sharing Defaults ================ If you want to set default values for `configure' scripts to share, you can create a site shell script called `config.site' that gives default values for variables like `CC', `cache_file', and `prefix'. `configure' looks for `PREFIX/share/config.site' if it exists, then `PREFIX/etc/config.site' if it exists. Or, you can set the `CONFIG_SITE' environment variable to the location of the site script. A warning: not all `configure' scripts look for a site script. Defining Variables ================== Variables not defined in a site shell script can be set in the environment passed to `configure'. However, some packages may run configure again during the build, and the customized values of these variables may be lost. In order to avoid this problem, you should set them in the `configure' command line, using `VAR=value'. For example: ./configure CC=/usr/local2/bin/gcc causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to an Autoconf bug. Until the bug is fixed you can use this workaround: CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== `configure' recognizes the following options to control how it operates. `--help' `-h' Print a summary of the options to `configure', and exit. `--version' `-V' Print the version of Autoconf used to generate the `configure' script, and exit. `--cache-file=FILE' Enable the cache: use and save the results of the tests in FILE, traditionally `config.cache'. FILE defaults to `/dev/null' to disable caching. `--config-cache' `-C' Alias for `--cache-file=config.cache'. `--quiet' `--silent' `-q' Do not print messages saying which checks are being made. To suppress all normal output, redirect it to `/dev/null' (any error messages will still be shown). `--srcdir=DIR' Look for the package's source code in directory DIR. Usually `configure' can determine that directory automatically. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. nodm-0.13/Makefile.am000066400000000000000000000041351304142760400144320ustar00rootroot00000000000000## Process this file with automake to produce Makefile.in NULL = CFLAGS += -Wall -Werror ACLOCAL_AMFLAGS = -I m4 sbin_PROGRAMS = nodm dist_noinst_HEADERS = common.h \ dm.h \ log.h \ test.h \ vt.h \ xserver.h \ xsession.h \ xsession-child.h \ $(NULL) libsources = common.c \ log.c \ vt.c \ xsession-child.c \ xserver.c \ xsession.c dm.c \ $(NULL) testlibsources = $(libsources) \ test.c \ $(NULL) AM_CPPFLAGS = $(X11_CFLAGS) LIBS = $(PAM_LIBS) $(X11_LIBS) nodm_SOURCES = $(libsources) \ nodm.c \ $(NULL) nodm_CFLAGS = -DNODM_SESSION='"$(sbindir)/nodm"' nodm.8: nodm help2man --section=8 --name="X display manager for automatic logins" \ --no-info --include=nodm-man-extras ./$< > $@ man_MANS = nodm.8 \ $(NULL) TESTS = test-internals test-xstart test-xsession check_PROGRAMS = test-internals test-xstart test-xsession test_xstart_SOURCES = $(testlibsources) \ test-xstart.c \ $(NULL) test_xsession_SOURCES = $(testlibsources) \ test-xsession.c \ $(NULL) test_internals_SOURCES = $(testlibsources) \ test-internals.c \ $(NULL) EXTRA_DIST = test_nodm \ nodm-man-extras \ autogen.sh \ nodm.service.in \ $(NULL) CLEANFILES = $(man_MANS) \ $(systemdsystemunit_DATA) \ $(NULL) DISTCLEANFILES = Makefile.in \ aclocal.m4 \ config.h.in \ config/* \ configure \ $(NULL) systemdsystemunitdir = $(prefix)/lib/systemd/system systemdsystemunit_DATA = nodm.service nodm.service: nodm.service.in sed \ -e 's![@]sbindir[@]!${sbindir}!g' \ < $< > $@ nodm-0.13/README.md000066400000000000000000000074451304142760400136640ustar00rootroot00000000000000nodm is a minimal display manager that simply logs in as a given user and starts an X session, without asking for username or password. On a normal computer, using nodm is a big security issue because it would give anyone access to the computer. However, there are cases where automatic login is needed: for example in an embedded system such as a mobile phone, or in a kiosk setup, or in a control panel for industrial machinery. For those cases, nodm is simple to setup, lightweight, and it should do exactly the right thing. ## Features nodm is as small as it could be, and tries to provide the minimum amount of features needed to do a good job, following as much as possible the principle of least surprise. This is what is offered: - Automatic login with a fixed user, doing all that needs to be done like setting up the session via PAM, updating lastlog, logging to syslog. - nodm performs VT allocation, looking for a free virtual terminal in which to run X and keeping it allocated across X restarts. - X is started (by default, /usr/bin/X) - once the X esrver is ready to accept connections, the X session is set up: - the DISPLAY and WINDOWPATH environment variables are set - the session is wrapped in a PAM session, which sets up the user environment - ~/.xsession-error is truncated if it exists - The session script is run (by default, /etc/X11/Xsession) using "sh -l" - If the X server or the X session exit, the other is killed and then both are restarted. - If a session exits too soon, nodm will wait a bit before restarting. The waiting times go as follow: - The first time the session exits too soon, restart immediately - The second and third time, wait 30 seconds - All remaining times, wait 1 minute. Once a session lasts long enough, the waiting time goes back to zero. nodm does NOT currently fork and run in the background like a proper daemon: most distributions have tools that do that, and nodm plays just fine with them. This is not a particular design choice: quite simply, so far no one has felt the need to implement it. ## Configuration Configuration is made via these environment variables: * `NODM_USER`: Controls the user that is used to automatically log in. * `NODM_X_OPTIONS`: X server command line (for example: "vt7 -nolisten tcp"). It is expanded using wordexp, with tilde expansion, variable substitution, arithmetic expansion, wildcard expansion and quote removal, but no command substitution. If command substitution is needed, please get in touch providing a real-life use case for it. If the first optiom starts with '/' or '.', it is used as the X server, else "X" is used as the server. If the second option (or the first if the first was not recognised as a path to the X server) looks like ":", it is used as the display name, else ":0" is used. If the command line contains a "vt" virtual terminal indicator, automatic VT allocation is switched off. Otherwise, the appropriate vt option is appended to the X command line according to the virtual terminal that has been allocated. * `NODM_MIN_SESSION_TIME`: Minimum time (in seconds) that a session should last in order for nodm to decide that it has not quit too soon. If an X session runs for less than this time, nodm will wait an increasing amount of time before restarting it (default: 60). * `NODM_XSESSION`: X session command (default: /etc/X11/Xsession). It is run using the shell, so it can be any shell command. * `NODM_XINIT` Was used by older versions of nodm as the path to the xinit program, but it is now ignored. * `NODM_X_TIMEOUT` Timeout (in seconds) to wait for X to be ready to accept connections. If X is not ready before this timeout, it is killed and restarted. nodm-0.13/autogen.sh000077500000000000000000000000321304142760400143670ustar00rootroot00000000000000#!/bin/sh autoreconf -if nodm-0.13/common.c000066400000000000000000000103411304142760400140260ustar00rootroot00000000000000/* * common - common nodm definitions and utility functions * * Copyright 2011 Enrico Zini * * 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. */ #include "common.h" #include "log.h" #include #include #include #include const char* nodm_basename (const char* str) { const char *cp = strrchr (str, '/'); return cp ? cp + 1 : str; } const char* getenv_with_default(const char* envname, const char* def) { const char* res = getenv(envname); if (res != NULL) return res; else return def; } const char* nodm_strerror(int code) { switch (code) { case E_SUCCESS: return "success"; case E_NOPERM: return "permission denied"; case E_USAGE: return "invalid command syntax"; case E_BAD_ARG: return "invalid argument to option"; case E_PASSWD_NOTFOUND: return "not found password file"; case E_SHADOW_NOTFOUND: return "not found shadow password file"; case E_GROUP_NOTFOUND: return "not found group file"; case E_GSHADOW_NOTFOUND: return "not found shadow group file"; case E_CMD_NOEXEC: return "can't run command/shell"; case E_CMD_NOTFOUND: return "can't find command/shell to run"; case E_PROGRAMMING: return "programming error"; case E_PAM_ERROR: return "something wrong talking with PAM"; case E_OS_ERROR: return "something wrong talking with the Operating System"; case E_XLIB_ERROR: return "Xlib error"; case E_X_SERVER_DIED: return "server died"; case E_X_SERVER_TIMEOUT: return "server not ready before timeout"; case E_X_SERVER_CONNECT: return "could not connect to X server"; case E_SESSION_DIED: return "X session died"; case E_USER_QUIT: return "quit requested"; default: return "unknown error"; } } int child_has_quit(pid_t pid, int* quit, int* status) { pid_t res = waitpid(pid, status, WNOHANG); if (res == -1) { if (errno == ECHILD) *quit = 2; else { log_err("error checking status of child process %d: %m", (int)pid); return E_OS_ERROR; } } else if (res == 0) *quit = 0; else *quit = 1; return E_SUCCESS; } int child_must_exit(pid_t pid, const char* procdesc) { int res = E_SUCCESS; if (pid > 0) { // Check what is the child status int quit, status; res = child_has_quit(pid, &quit, &status); if (res != E_SUCCESS) return res; switch (quit) { case 0: // still running, we must kill it log_info("sending %s %d the TERM signal", procdesc, (int)pid); kill(pid, SIGTERM); kill(pid, SIGCONT); while (true) { int status; if (waitpid(pid, &status, 0) == -1) { if (errno == EINTR) continue; if (errno != ECHILD) return E_OS_ERROR; } break; } break; case 1: // has just quit log_info("%s %d quit with status %d", procdesc, (int)pid, status); break; case 2: // Was not there break; } } return E_SUCCESS; } nodm-0.13/common.h000066400000000000000000000066521304142760400140450ustar00rootroot00000000000000/* * common - common nodm definitions and utility functions * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_DEFS_H #define NODM_DEFS_H #include // Exit codes used by shadow programs #define E_SUCCESS 0 ///< success #define E_NOPERM 1 ///< permission denied #define E_USAGE 2 ///< invalid command syntax #define E_BAD_ARG 3 ///< invalid argument to option #define E_PASSWD_NOTFOUND 14 ///< not found password file #define E_SHADOW_NOTFOUND 15 ///< not found shadow password file #define E_GROUP_NOTFOUND 16 ///< not found group file #define E_GSHADOW_NOTFOUND 17 ///< not found shadow group file #define E_CMD_NOEXEC 126 ///< can't run command/shell #define E_CMD_NOTFOUND 127 ///< can't find command/shell to run // Other nodm-specific exit codes #define E_PROGRAMMING 200 ///< Programming error #define E_PAM_ERROR 201 ///< something wrong talking with PAM #define E_OS_ERROR 202 ///< something wrong talking with the Operating System #define E_XLIB_ERROR 203 ///< Xlib error #define E_VT_ALLOC_ERROR 204 ///< VT allocation error #define E_X_SERVER_DIED 210 ///< Server died #define E_X_SERVER_TIMEOUT 211 ///< Server not ready before timeout #define E_X_SERVER_CONNECT 212 ///< Could not connect to X server #define E_SESSION_DIED 220 ///< X session died #define E_USER_QUIT 221 ///< Quit requested /// Return the basename of a path, as a pointer inside \a str const char* nodm_basename (const char* str); /** * Like getenv, but if the variable is not defined it returns \a def */ const char* getenv_with_default(const char* envname, const char* def); /** * Like strcpy but: * * * it works only for sized character arrays (it expects sizeof on them) * * it always null-terminates the destination string * * it returns false if the string was truncated, else true */ #define bounded_strcpy(dst, src) (snprintf(dst, sizeof(dst), "%s", (src)) < sizeof(dst)) /// Return the string description of an exit code const char* nodm_strerror(int code); /** * Check if a child has died. * * @param pid * process ID to check * @retval quit * 0 if it is still running * 1 if it has just quit and we got its exit status * 2 if the pid does not exist * @retval status * the exit status if it has quit */ int child_has_quit(pid_t pid, int* quit, int* status); /** * Kill a child process if it still running and wait for it to end * * @param pid * The child pid * @param procdesc * Child process description to use in the logs */ int child_must_exit(pid_t pid, const char* procdesc); #endif nodm-0.13/configure.ac000066400000000000000000000014321304142760400146610ustar00rootroot00000000000000dnl Process this file with autoconf to produce a configure script. AC_INIT(nodm, 0.13, [enrico@enricozini.org, nomeata@debian.org, sunweaver@debian.org]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_AUX_DIR([config]) AM_INIT_AUTOMAKE([-Wall std-options foreign]) AM_CONFIG_HEADER(config.h) AC_CONFIG_MACRO_DIR([m4]) dnl AC_GNU_SOURCE dnl AM_MAINTAINER_MODE dnl Checks for programs. AC_PROG_CC AC_ISC_POSIX dnl We use C99 AC_PROG_CC_C99 dnl Checks for header files. AC_HEADER_STDC dnl Checks for libraries. PKG_CHECK_MODULES(X11, x11) AC_CHECK_LIB(pam, pam_start, [ PAM_LIBS="-lpam" ], AC_MSG_ERROR(libpam is missing) ) AC_CHECK_LIB(pam_misc, main, [ PAM_LIBS="$PAM_LIBS -lpam_misc" ], AC_MSG_ERROR(libpam_misc is missing) ) AC_SUBST(PAM_LIBS) AC_CONFIG_FILES([ Makefile ]) AC_OUTPUT nodm-0.13/dm.c000066400000000000000000000250751304142760400131500ustar00rootroot00000000000000/* * dm - nodm X display manager * * Copyright 2011 Enrico Zini * * 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. */ #include "dm.h" #include "common.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include void nodm_display_manager_init(struct nodm_display_manager* dm) { nodm_xserver_init(&dm->srv); nodm_xsession_init(&dm->session); nodm_vt_init(&dm->vt); dm->conf_minimum_session_time = atoi(getenv_with_default("NODM_MIN_SESSION_TIME", "60")); dm->_srv_split_args = NULL; dm->_srv_split_argv = NULL; // Save original signal mask if (sigprocmask(SIG_BLOCK, NULL, &dm->orig_signal_mask) == -1) log_err("sigprocmask error: %m"); dm->srv.orig_signal_mask = dm->orig_signal_mask; dm->session.orig_signal_mask = dm->orig_signal_mask; } void nodm_display_manager_cleanup(struct nodm_display_manager* dm) { // Restore original signal mask if (sigprocmask(SIG_SETMASK, &dm->orig_signal_mask, NULL) == -1) log_err("sigprocmask error: %m"); nodm_vt_stop(&dm->vt); // Deallocate parsed arguments, if used if (dm->_srv_split_args) { wordexp_t* we = (wordexp_t*)dm->_srv_split_args; wordfree(we); free(we); dm->_srv_split_args = NULL; } if (dm->_srv_split_argv) { free(dm->_srv_split_argv); dm->_srv_split_argv = NULL; } } int nodm_display_manager_start(struct nodm_display_manager* dm) { int res = nodm_vt_start(&dm->vt); if (res != E_SUCCESS) return res; if (dm->vt.num != -1) { // Create the vtN argument snprintf(dm->_vtarg, sizeof(dm->_vtarg), "vt%d", dm->vt.num); // Append it to srv args const char** s = dm->srv.argv; while (*s) ++s; *s++ = dm->_vtarg; *s = NULL; log_verb("allocated VT %d", dm->vt.num); } else log_verb("skipped VT allocation"); // Block all signals sigset_t blockmask; if (sigfillset(&blockmask) == -1) { log_err("sigfillset error: %m"); return E_PROGRAMMING; } if (sigprocmask(SIG_BLOCK, &blockmask, NULL) == -1) { log_err("sigprocmask error: %m"); return E_PROGRAMMING; } res = nodm_display_manager_restart(dm); if (res != E_SUCCESS) return res; return E_SUCCESS; } int nodm_display_manager_restart(struct nodm_display_manager* dm) { dm->last_session_start = time(NULL); int res = nodm_xserver_start(&dm->srv); if (res != E_SUCCESS) return res; log_verb("X server is ready for connections"); res = nodm_xsession_start(&dm->session, &dm->srv); if (res != E_SUCCESS) return res; log_verb("X session has started"); return E_SUCCESS; } int nodm_display_manager_stop(struct nodm_display_manager* dm) { int res = nodm_xsession_stop(&dm->session); if (res != E_SUCCESS) return res; res = nodm_xserver_stop(&dm->srv); if (res != E_SUCCESS) return res; return E_SUCCESS; } // Signal handler for wait loop static int quit_signal_caught = 0; static void catch_signals (int sig) { ++quit_signal_caught; } static int setup_quit_notification(sigset_t* origset) { /* Reset caught signal flag */ quit_signal_caught = 0; struct sigaction action; action.sa_handler = catch_signals; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigset_t ourset; if (sigemptyset(&ourset) || sigaddset(&ourset, SIGTERM) || sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT) || sigaction(SIGTERM, &action, NULL) || sigaction(SIGINT, &action, NULL) || sigaction(SIGQUIT, &action, NULL) || sigprocmask(SIG_UNBLOCK, &ourset, origset) ) { log_err("signal operations error: %m"); return E_PROGRAMMING; } return E_SUCCESS; } static void shutdown_quit_notification(const sigset_t* origset) { if (sigprocmask(SIG_SETMASK, origset, NULL) == -1) log_err("sigprocmask error: %m"); } int nodm_display_manager_wait(struct nodm_display_manager* dm, int* session_status) { int res = E_SUCCESS; // Catch the normal termination signals using 'catch_signals' sigset_t origset; res = setup_quit_notification(&origset); if (res != E_SUCCESS) return res; *session_status = -1; while (true) { // Wait for one child to exit int status; pid_t child = waitpid(-1, &status, 0); if (child == -1) { if (errno == EINTR) { if (quit_signal_caught) { log_info("shutdown signal received"); res = E_USER_QUIT; goto cleanup; } else continue; } else { log_warn("waitpid error: %m"); res = E_OS_ERROR; goto cleanup; } } if (child == dm->srv.pid) { // Server died nodm_xserver_report_exit(&dm->srv, status); res = E_X_SERVER_DIED; goto cleanup; } else if (child == dm->session.pid) { // Session died nodm_xsession_report_exit(&dm->session, status); *session_status = status; res = E_SESSION_DIED; goto cleanup; } } cleanup: shutdown_quit_notification(&origset); return res; } int nodm_display_manager_parse_xcmdline(struct nodm_display_manager* s, const char* xcmdline) { int return_code = E_SUCCESS; char **argv = NULL; // tokenize xoptions wordexp_t* toks = (wordexp_t*)calloc(1, sizeof(wordexp_t)); switch (wordexp(xcmdline, toks, WRDE_NOCMD)) { case 0: break; case WRDE_NOSPACE: return_code = E_OS_ERROR; goto cleanup; default: toks->we_wordv = NULL; return_code = E_BAD_ARG; goto cleanup; } unsigned in_arg = 0; unsigned argc = 0; // +1 for the X server pathname, +1 for the display name, // +1 for the VT number, +1 for the trailing NULL argv =(char**)malloc((toks->we_wordc + 4) * sizeof(char*)); if (argv == NULL) { return_code = E_OS_ERROR; goto cleanup; } // Server command if (in_arg < toks->we_wordc && (toks->we_wordv[in_arg][0] == '/' || toks->we_wordv[in_arg][0] == '.')) argv[argc++] = toks->we_wordv[in_arg++]; else argv[argc++] = "/usr/bin/X"; // Server name if (in_arg < toks->we_wordc && toks->we_wordv[in_arg][0] == ':' && isdigit(toks->we_wordv[in_arg][1])) { argv[argc] = toks->we_wordv[in_arg++]; s->srv.name = argv[argc]; ++argc; } else { argv[argc] = ":0"; s->srv.name = argv[argc]; ++argc; } // Copy other args while (in_arg < toks->we_wordc) { int vtn; if (sscanf(toks->we_wordv[in_arg], "vt%d", &vtn) == 1) // if vtN has been provided by the caller, disable VT allocation s->vt.conf_initial_vt = -1; argv[argc++] = toks->we_wordv[in_arg++]; } argv[argc] = NULL; s->srv.argv = (const char**)argv; s->_srv_split_argv = argv; s->_srv_split_args = toks; argv = NULL; toks = NULL; cleanup: if (toks != NULL) { if (toks->we_wordv) wordfree(toks); free(toks); } if (argv != NULL) free(argv); return return_code; } void nodm_display_manager_dump_status(struct nodm_display_manager* dm) { nodm_xserver_dump_status(&dm->srv); nodm_xsession_dump_status(&dm->session); } static int interruptible_sleep(int seconds) { int res = E_SUCCESS; // Catch the normal termination signals using 'catch_signals' sigset_t origset; res = setup_quit_notification(&origset); if (res != E_SUCCESS) return res; struct timespec tosleep = { .tv_sec = seconds, .tv_nsec = 0 }; struct timespec remaining; while (true) { int r = nanosleep(&tosleep, &remaining); if (r != -1) break; else if (errno == EINTR) { if (quit_signal_caught) { res = E_USER_QUIT; break; } else tosleep = remaining; } else { log_warn("sleep aborted: %m (ignoring error"); break; } } shutdown_quit_notification(&origset); return res; } int nodm_display_manager_wait_restart_loop(struct nodm_display_manager* dm) { static int retry_times[] = { 0, 0, 30, 30, 60, 60, -1 }; int restart_count = 0; int res; while (1) { int sstatus; res = nodm_display_manager_wait(dm, &sstatus); time_t end = time(NULL); nodm_display_manager_stop(dm); switch (res) { case E_X_SERVER_DIED: break; case E_SESSION_DIED: break; default: return res; } /* Check if the session was too short */ if (end - dm->last_session_start < dm->conf_minimum_session_time) { if (retry_times[restart_count+1] != -1) ++restart_count; } else restart_count = 0; /* Sleep a bit if the session was too short */ if (retry_times[restart_count] > 0) { log_warn("session lasted less than %d seconds: sleeping %d seconds before restarting it", dm->conf_minimum_session_time, retry_times[restart_count]); res = interruptible_sleep(retry_times[restart_count]); if (res != E_SUCCESS) return res; } log_info("restarting session"); res = nodm_display_manager_restart(dm); if (res != E_SUCCESS) return res; } } nodm-0.13/dm.h000066400000000000000000000065051304142760400131520ustar00rootroot00000000000000/* * dm - nodm X display manager * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_DISPLAY_MANAGER_H #define NODM_DISPLAY_MANAGER_H #include "xserver.h" #include "xsession.h" #include "vt.h" #include #include struct nodm_display_manager { /// X server supervision struct nodm_xserver srv; /// X session supervision struct nodm_xsession session; /// VT allocation struct nodm_vt vt; /** * The minimum time (in seconds) that a session should last to be * considered successful */ int conf_minimum_session_time; /// Time the last session started time_t last_session_start; /// Original signal mask at program startup sigset_t orig_signal_mask; /// Storage for split server arguments used by nodm_x_cmdline_split char** _srv_split_argv; void* _srv_split_args; /// Storage for vtN argument from dynamic VT allocation char _vtarg[10]; }; /// Initialise a display_manager structure with default values void nodm_display_manager_init(struct nodm_display_manager* dm); /// Cleanup at the end of the display manager void nodm_display_manager_cleanup(struct nodm_display_manager* dm); /** * Start X and the X session * * This function sets the signal mask to block all signals. The original signal * mask is restored by nodm_display_manager_cleanup(). */ int nodm_display_manager_start(struct nodm_display_manager* dm); /// Restart X and the X session after they died int nodm_display_manager_restart(struct nodm_display_manager* dm); /// Wait for X or the X session to end int nodm_display_manager_wait(struct nodm_display_manager* dm, int* session_status); /// Stop X and the X session int nodm_display_manager_stop(struct nodm_display_manager* dm); /** * nodm wait/restart loop. * * Wait for the X server or session to terminate and restart them. * * If the session was very short-lived, it wants for an incremental amount of * time before restarting it. */ int nodm_display_manager_wait_restart_loop(struct nodm_display_manager* dm); /** * Split xcmdline using wordexp shell-like expansion and set dm->srv.argv. * * If the first token starts with '/' or '.', it is used as the X server, else * "X" is used as the server. * * If the second token (or the first if the first was not recognised as a path * to the X server) looks like ":", it is used as the display name, * else ":0" is used. */ int nodm_display_manager_parse_xcmdline(struct nodm_display_manager* dm, const char* xcmdline); /// Dump all internal status to stderr void nodm_display_manager_dump_status(struct nodm_display_manager* dm); #endif nodm-0.13/log.c000066400000000000000000000055461304142760400133320ustar00rootroot00000000000000/* * log - print and/or log messages * * Copyright 2011 Enrico Zini * * 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. */ #include "log.h" #include #include #include #include static const struct log_config* config = 0; void log_config_init(struct log_config* conf) { conf->program_name = 0; conf->log_to_syslog = true; conf->log_to_stderr = true; conf->log_level = LOG_INFO; } void log_start(const struct log_config* conf) { config = conf; if (config->log_to_syslog) openlog("nodm", LOG_PID, LOG_AUTHPRIV); } void log_end() { if (config->log_to_syslog) closelog(); } static void log_common(int prio, const char* fmt, va_list ap) { if (config->log_to_stderr) { va_list loc; va_copy(loc, ap); struct timeval now; gettimeofday(&now, NULL); fprintf(stderr, "%u.%u %s:", (unsigned)now.tv_sec, (unsigned)now.tv_usec, config->program_name); vfprintf(stderr, fmt, loc); va_end(loc); fputc('\n', stderr); } if (config->log_to_syslog) { va_list loc; va_copy(loc, ap); vsyslog(prio, fmt, loc); va_end(loc); } } bool log_verb(const char* fmt, ...) { if (config->log_level < NODM_LL_VERB) return false; if (fmt == NULL) return true; va_list ap; va_start(ap, fmt); log_common(LOG_INFO, fmt, ap); va_end(ap); return true; } bool log_info(const char* fmt, ...) { if (config->log_level < NODM_LL_INFO) return false; if (fmt == NULL) return true; va_list ap; va_start(ap, fmt); log_common(LOG_NOTICE, fmt, ap); va_end(ap); return true; } bool log_warn(const char* fmt, ...) { if (config->log_level < NODM_LL_WARN) return false; if (fmt == NULL) return true; va_list ap; va_start(ap, fmt); log_common(LOG_WARNING, fmt, ap); va_end(ap); return true; } bool log_err(const char* fmt, ...) { if (config->log_level < NODM_LL_ERR) return false; if (fmt == NULL) return true; va_list ap; va_start(ap, fmt); log_common(LOG_ERR, fmt, ap); va_end(ap); return true; } nodm-0.13/log.h000066400000000000000000000045341304142760400133330ustar00rootroot00000000000000/* * log - print and/or log messages * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_LOG_H #define NODM_LOG_H #include /// Logging configuration struct log_config { /// The program name to use in error messages const char* program_name; /// Log to syslog bool log_to_syslog; /// Log to stderr bool log_to_stderr; /** * Log level: * 1. errors * 2. warnings * 3. info * 4. verbose */ enum { NODM_LL_ERR = 1, NODM_LL_WARN = 2, NODM_LL_INFO = 3, NODM_LL_VERB = 4 } log_level; }; /** * Initialise log configuration with default values */ void log_config_init(struct log_config* conf); /** * Start the logging system * * @param conf * The logging configuration. Note that it only stores a copy of the pointer, * but does not make a copy of the structure, so you need to make sure that * the string remains valid until log_end() is called. */ void log_start(const struct log_config* conf); /** * Shut down the logging system. * * This should also be called after each fork. */ void log_end(); // Note: all log functions return true if the message was logged, false // otherwise. This can be used to test for log levels: if (log_verb(NULL)) { /* // complex debugging code */ } /// Log a message about the trivial normal progress of things bool log_verb(const char* fmt, ...); /// Log a message about the relevant normal progress of things bool log_info(const char* fmt, ...); /// Log a warning message bool log_warn(const char* fmt, ...); /// Log an error message bool log_err(const char* fmt, ...); #endif nodm-0.13/m4/000077500000000000000000000000001304142760400127135ustar00rootroot00000000000000nodm-0.13/m4/pkg.m4000066400000000000000000000121411304142760400137350ustar00rootroot00000000000000# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # # Check to see whether a particular set of modules exists. Similar # to PKG_CHECK_MODULES(), but does not set variables or print errors. # # # Similar to PKG_CHECK_MODULES, make sure that the first instance of # this or PKG_CHECK_MODULES is called, or make sure to call # PKG_CHECK_EXISTS manually # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_ifval([$2], [$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) # _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) # --------------------------------------------- m4_define([_PKG_CONFIG], [if test -n "$PKG_CONFIG"; then if test -n "$$1"; then pkg_cv_[]$1="$$1" else PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`], [pkg_failed=yes]) fi else pkg_failed=untried fi[]dnl ])# _PKG_CONFIG # _PKG_SHORT_ERRORS_SUPPORTED # ----------------------------- AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])# _PKG_SHORT_ERRORS_SUPPORTED # PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], # [ACTION-IF-NOT-FOUND]) # # # Note that if there is a possibility the first call to # PKG_CHECK_MODULES might not happen, you should be sure to include an # explicit call to PKG_PROG_PKG_CONFIG in your configure.ac # # # -------------------------------------------------------------- AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"` else $1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD ifelse([$4], , [AC_MSG_ERROR(dnl [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT ])], [AC_MSG_RESULT([no]) $4]) elif test $pkg_failed = untried; then ifelse([$4], , [AC_MSG_FAILURE(dnl [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])], [$4]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) ifelse([$3], , :, [$3]) fi[]dnl ])# PKG_CHECK_MODULES nodm-0.13/nodm-man-extras000066400000000000000000000013731304142760400153340ustar00rootroot00000000000000[DESCRIPTION] \fBnodm\fP is a minimal display manager that simply logs in as a given user and starts an X session, without asking for username or password. .P On a normal computer, using \fBnodm\fP is a big security issue because it would give anyone access to the computer. .P However, there are cases where automatic login is needed: for example in an embedded system such as a mobile phone, or in a kiosk setup, or in a control panel for industrial machinery. For those cases, \fBnodm\fP is simple to setup, lightweight, and it should do exactly the right thing. [AUTHOR] \fBnodm\fP is written and maintained by Enrico Zini and Joachim Breitner . [SEE ALSO] .BR xinit (1), .BR xdm (1), .BR gdm (1), .BR kdm (1). nodm-0.13/nodm.c000066400000000000000000000131251304142760400134760ustar00rootroot00000000000000/* * nodm - nodm X display manager * * Copyright 2009--2011 Enrico Zini * * 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. */ #define NAME "nodm" #ifndef NODM_SESSION #define NODM_SESSION "/usr/sbin/nodm" #endif /* #define DEBUG_NODM */ #include "config.h" #include "common.h" #include "dm.h" #include "log.h" #include #include #include #include #include #include #include #include #include static void do_help(int argc, char** argv, FILE* out) { fprintf(out, "Usage: %s [options]\n\n", argv[0]); fprintf(out, "Options:\n"); fprintf(out, " --help print this help message\n"); fprintf(out, " --version print %s's version number\n", NAME); fprintf(out, " --verbose verbose outpout or logging\n"); fprintf(out, " --quiet only log warnings and errors\n"); fprintf(out, " --nested run a nested X server, does not require root.\n"); fprintf(out, " The server defaults to \"/usr/bin/Xnest :1\",\n"); fprintf(out, " override with NODM_X_OPTIONS\n"); fprintf(out, " --[no-]syslog enable/disable logging to syslog\n"); fprintf(out, " --[no-]stderr enable/disable logging to stderr\n"); } /* * nodm - start X with autologin to a given user * * First, X is started as root, and nodm itself is used as the session. * * When run as the session, nodm performs a proper login to a given user and * starts the X session. */ int main (int argc, char **argv) { static int opt_help = 0; static int opt_version = 0; static int opt_verbose = 0; static int opt_quiet = 0; static int opt_nested = 0; static int opt_log_syslog = -1; // -1 for 'default' static int opt_log_stderr = -1; // -1 for 'default' static struct option options[] = { /* These options set a flag. */ {"help", no_argument, &opt_help, 1}, {"version", no_argument, &opt_version, 1}, {"verbose", no_argument, &opt_verbose, 1}, {"quiet", no_argument, &opt_quiet, 1}, {"nested", no_argument, &opt_nested, 1}, {"syslog", no_argument, &opt_log_syslog, 1}, {"stderr", no_argument, &opt_log_stderr, 1}, {"no-syslog", no_argument, &opt_log_syslog, 0}, {"no-stderr", no_argument, &opt_log_stderr, 0}, {0, 0, 0, 0} }; // Parse command line options while (1) { int option_index = 0; int c = getopt_long(argc, argv, ":", options, &option_index); if (c == -1) break; switch (c) { case 0: break; default: fprintf(stderr, "Invalid command line option\n"); do_help(argc, argv, stderr); return E_USAGE; } } if (opt_help) { do_help(argc, argv, stdout); return E_SUCCESS; } if (opt_version) { printf("%s version %s\n", NAME, VERSION); return E_SUCCESS; } // We only run if we are root if (!opt_nested && getuid() != 0) { fprintf(stderr, "%s: can only be run by root\n", nodm_basename(argv[0])); return E_NOPERM; } // Setup logging struct log_config cfg = { .program_name = nodm_basename(argv[0]), }; if (opt_quiet) cfg.log_level = NODM_LL_WARN; else if (opt_verbose) cfg.log_level = NODM_LL_VERB; else cfg.log_level = NODM_LL_INFO; if (opt_nested) { if (opt_log_syslog == -1) opt_log_syslog = 0; if (opt_log_stderr == -1) opt_log_stderr = 1; } else { if (opt_log_syslog == -1) opt_log_syslog = 1; if (opt_log_stderr == -1) opt_log_stderr = 0; } cfg.log_to_syslog = opt_log_syslog ? true : false; cfg.log_to_stderr = opt_log_stderr ? true : false; log_start(&cfg); log_info("starting nodm"); // Setup the display manager struct nodm_display_manager dm; nodm_display_manager_init(&dm); // Choose the default X server const char* default_x_server = opt_nested ? "/usr/bin/Xnest :1" : ""; // Parse X server command line int res = nodm_display_manager_parse_xcmdline(&dm, getenv_with_default("NODM_X_OPTIONS", default_x_server)); if (res != E_SUCCESS) goto cleanup; if (opt_nested) { // For nested servers, disable PAM, user change, ~/.xsession-error // cleanup and VT allocation dm.session.conf_use_pam = false; dm.session.conf_cleanup_xse = false; dm.session.conf_run_as[0] = 0; dm.vt.conf_initial_vt = -1; } // Start the first session res = nodm_display_manager_start(&dm); if (res != E_SUCCESS) goto cleanup; // Enter the wait/restart loop res = nodm_display_manager_wait_restart_loop(&dm); if (res != E_SUCCESS) goto cleanup; cleanup: nodm_display_manager_cleanup(&dm); log_end(); return res; } nodm-0.13/nodm.init-script000066400000000000000000000040101304142760400155120ustar00rootroot00000000000000#!/bin/sh ### BEGIN INIT INFO # Provides: nodm # Should-Start: bluetooth # Required-Start: $remote_fs # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: # Description: Display Manager for automatic session logins # Short-Description: No Display Manager ### END INIT INFO set -e PATH=/bin:/usr/bin:/sbin:/usr/sbin DESC="Automatic Display Manager" NAME=nodm PIDDIR=/var/run/ PIDFILE=${PIDDIR}/${NAME}.pid NODM_ENABLED=no NODM_XINIT=/usr/bin/xinit NODM_FIRST_VT=7 NODM_XSESSION=/etc/X11/Xsession NODM_OPTIONS= NODM_X_OPTIONS="-nolisten tcp" NODM_USER=root NODM_MIN_SESSION_TIME=60 NODM_X_TIMEOUT=300 if [ -f /etc/default/$NAME ] then . /etc/default/$NAME fi export NODM_XINIT NODM_XSESSION NODM_X_OPTIONS NODM_USER NODM_MIN_SESSION_TIME NODM_FIRST_VT NODM_X_TIMEOUT # If you change the user to a non-root user, make sure you # set allowed_users=anybody in /etc/X11/Xwrapper.config # Gracefully exit if the package or its dependencies have been removed (but not purged). [ -x /usr/sbin/nodm ] || exit 0 # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. . /lib/lsb/init-functions case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" if [ "$NODM_ENABLED" = "no" ] || [ "$NODM_ENABLED" = "false" ] then log_warning_msg "Not starting $NAME because NODM_ENABLED is '$NODM_ENABLED' in /etc/default/$NAME" else start-stop-daemon --start --quiet --oknodo --pidfile ${PIDFILE} --make-pidfile --background --exec /usr/sbin/nodm -- ${NODM_OPTIONS} fi [ "$VERBOSE" != no ] && log_end_msg $? ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" start-stop-daemon --stop --quiet --retry 10 --pidfile ${PIDFILE} --oknodo rm -f ${PIDFILE} [ "$VERBOSE" != no ] && log_end_msg $? ;; restart|force-reload) $0 stop $0 start ;; status) status_of_proc -p ${PIDFILE} /usr/sbin/nodm nodm && exit 0 || exit $? ;; *) echo "Usage: $0 {start|stop|restart|force-reload|status}" >&2 exit 1 ;; esac exit 0 nodm-0.13/nodm.pam000066400000000000000000000016271304142760400140350ustar00rootroot00000000000000# # /etc/pam.d/nodm - specify the PAM behaviour of nodm # # No authentication, as nodm skips by design # This module parses environment configuration file(s) # and also allows you to use an extended config # file /etc/security/pam_env.conf. # # parsing /etc/environment needs "readenv=1" session required pam_env.so readenv=1 # locale variables are also kept into /etc/default/locale in etch # reading this file *in addition to /etc/environment* does not hurt session required pam_env.so readenv=1 envfile=/etc/default/locale # Sets up user limits according to /etc/security/limits.conf # # (Replaces the use of /etc/limits in old login) session required pam_limits.so # Standard Un*x account @include common-account @include common-password # Enable ConsoleKit integration session optional pam_loginuid.so session optional pam_ck_connector.so # Standard Un*x session @include common-session nodm-0.13/nodm.service.in000066400000000000000000000004571304142760400153250ustar00rootroot00000000000000[Unit] Description=Display manager for automatic session logins Documentation=man:nodm(8) After=systemd-user-sessions.service [Service] EnvironmentFile=-/etc/default/nodm ExecStartPre=/usr/bin/test ${NODM_ENABLED} != no -a ${NODM_ENABLED} != false ExecStart=@sbindir@/nodm $NODM_OPTIONS Restart=always nodm-0.13/test-internals.c000066400000000000000000000156401304142760400155210ustar00rootroot00000000000000/* * test-internals - test nodm internals and helper functions * * Copyright 2011 Enrico Zini * * 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. */ #include "log.h" #include "common.h" #include "dm.h" #include "test.h" #include #include #include int main(int argc, char* argv[]) { test_start("test-internals", false); // Test getenv_with_default setenv("FOO", "foo", 1); ensure_equals(getenv_with_default("FOO", "bar"), "foo"); unsetenv("FOO"); ensure_equals(getenv_with_default("FOO", "bar"), "bar"); struct nodm_display_manager s; nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, ""); ensure_equals(s.srv.argv[0], "/usr/bin/X"); ensure_equals(s.srv.argv[1], ":0"); ensure_equals(s.srv.argv[2], "-nolisten"); ensure_equals(s.srv.argv[3], "tcp"); ensure_equals(s.srv.argv[4], NULL); ensure_equals(s.srv.name, ":0"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "foo"); ensure_equals(s.srv.argv[0], "/usr/bin/X"); ensure_equals(s.srv.argv[1], ":0"); ensure_equals(s.srv.argv[2], "foo"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], NULL); ensure_equals(s.srv.name, ":0"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":0"); ensure_equals(s.srv.argv[2], "-nolisten"); ensure_equals(s.srv.argv[3], "tcp"); ensure_equals(s.srv.argv[4], NULL); ensure_equals(s.srv.name, ":0"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, ":1"); ensure_equals(s.srv.argv[0], "/usr/bin/X"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "-nolisten"); ensure_equals(s.srv.argv[3], "tcp"); ensure_equals(s.srv.argv[4], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest :1"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "-nolisten"); ensure_equals(s.srv.argv[3], "tcp"); ensure_equals(s.srv.argv[4], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest foo"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":0"); ensure_equals(s.srv.argv[2], "foo"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], NULL); ensure_equals(s.srv.name, ":0"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, ":1 foo"); ensure_equals(s.srv.argv[0], "/usr/bin/X"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "foo"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest :1 foo"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "foo"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, 7); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "vt2"); ensure_equals(s.srv.argv[0], "/usr/bin/X"); ensure_equals(s.srv.argv[1], ":0"); ensure_equals(s.srv.argv[2], "vt2"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], NULL); ensure_equals(s.srv.name, ":0"); ensure_equali(s.vt.conf_initial_vt, -1); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest :1 vt42 foo"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "vt42"); ensure_equals(s.srv.argv[3], "foo"); ensure_equals(s.srv.argv[4], "-nolisten"); ensure_equals(s.srv.argv[5], "tcp"); ensure_equals(s.srv.argv[6], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, -1); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest :1 vt42 -nolisten foo"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "vt42"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "foo"); ensure_equals(s.srv.argv[5], "-nolisten"); ensure_equals(s.srv.argv[6], "tcp"); ensure_equals(s.srv.argv[7], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, -1); nodm_display_manager_cleanup(&s); nodm_display_manager_init(&s); nodm_display_manager_parse_xcmdline(&s, "/usr/bin/Xnest :1 vt42 -nolisten tcp foo"); ensure_equals(s.srv.argv[0], "/usr/bin/Xnest"); ensure_equals(s.srv.argv[1], ":1"); ensure_equals(s.srv.argv[2], "vt42"); ensure_equals(s.srv.argv[3], "-nolisten"); ensure_equals(s.srv.argv[4], "tcp"); ensure_equals(s.srv.argv[5], "foo"); ensure_equals(s.srv.argv[6], NULL); ensure_equals(s.srv.name, ":1"); ensure_equali(s.vt.conf_initial_vt, -1); nodm_display_manager_cleanup(&s); test_ok(); } nodm-0.13/test-xsession.c000066400000000000000000000106671304142760400154010ustar00rootroot00000000000000/* * test-xsession - test that we are able to run a X session * * Copyright 2011 Enrico Zini * * 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. */ #include "log.h" #include "common.h" #include "dm.h" #include "xserver.h" #include "xsession-child.h" #include "test.h" #include #include #include #include #include #include static char orig_windowpath[1024]; int test_session(struct nodm_xsession_child* s) { int res = nodm_xsession_child_common_env(s); if (res != E_SUCCESS) return res; // Check environment ensure_not_equals(orig_windowpath, getenv_with_default("WINDOWPATH", "")); return E_SUCCESS; } int test_session_bad(struct nodm_xsession_child* s) { int res = nodm_xsession_child_common_env(s); if (res != E_SUCCESS) return res; return E_USAGE; } int test_session_x_killer(struct nodm_xsession_child* s) { int res = nodm_xsession_child_common_env(s); if (res != E_SUCCESS) return res; if (s->srv->pid == -1) { fprintf(stderr, "server PID has not been set\n"); return E_BAD_ARG; } kill(s->srv->pid, SIGTERM); sleep(10); return E_SUCCESS; } // X server starts, X session quits with success void test_trivial_session() { log_verb("test_trivial_session"); struct nodm_display_manager dm; test_setup_dm(&dm, NULL); dm.session.child_body = test_session; ensure_succeeds(nodm_display_manager_start(&dm)); int sstatus; ensure_equali(nodm_display_manager_wait(&dm, &sstatus), E_SESSION_DIED); ensure_equali(sstatus, E_SUCCESS); ensure_succeeds(nodm_display_manager_stop(&dm)); nodm_display_manager_cleanup(&dm); } // X server does not start void test_bad_x_server() { log_verb("test_bad_x_server"); struct nodm_display_manager dm; test_setup_dm(&dm, "/bin/false"); dm.session.child_body = test_session; ensure_equali(nodm_display_manager_start(&dm), E_X_SERVER_DIED); // If xserver died before being ready for connections, it should reflect on // the tracked pid ensure_equali(dm.srv.pid, -1); // Session must not have been started ensure_equali(dm.session.pid, -1); ensure_succeeds(nodm_display_manager_stop(&dm)); nodm_display_manager_cleanup(&dm); } // X server starts, X session quits with error void test_failing_x_session() { log_verb("test_failing_x_session"); struct nodm_display_manager dm; test_setup_dm(&dm, NULL); dm.session.child_body = test_session_bad; ensure_succeeds(nodm_display_manager_start(&dm)); int sstatus; ensure_equali(nodm_display_manager_wait(&dm, &sstatus), E_SESSION_DIED); ensure_equali(WIFEXITED(sstatus) ? 1 : 0, 1); ensure_equali(WEXITSTATUS(sstatus), E_USAGE); ensure_succeeds(nodm_display_manager_stop(&dm)); nodm_display_manager_cleanup(&dm); } // X server starts, X session starts, then server dies void test_dying_x_server() { log_verb("test_dying_x_server"); struct nodm_display_manager dm; test_setup_dm(&dm, NULL); dm.session.child_body = test_session_x_killer; ensure_succeeds(nodm_display_manager_start(&dm)); int sstatus; ensure_equali(nodm_display_manager_wait(&dm, &sstatus), E_X_SERVER_DIED); ensure_equali(sstatus, -1); ensure_succeeds(nodm_display_manager_stop(&dm)); nodm_display_manager_cleanup(&dm); } int main(int argc, char* argv[]) { test_start("test-xsession", true); // Save original window path to check if it changed in the X session (void)bounded_strcpy(orig_windowpath, getenv_with_default("WINDOWPATH", "")); test_trivial_session(); test_bad_x_server(); test_failing_x_session(); test_dying_x_server(); // TODO: // - test a wrong username (error before starting X session) test_ok(); } nodm-0.13/test-xstart.c000066400000000000000000000031511304142760400150410ustar00rootroot00000000000000/* * test-xstart - test that we are able to start X * * Copyright 2011 Enrico Zini * * 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. */ #include "log.h" #include "xserver.h" #include "test.h" #include "common.h" #include #include #include int main(int argc, char* argv[]) { test_start("test-xstart", false); struct nodm_xserver srv; nodm_xserver_init(&srv); const char* server_argv[] = { getenv("DISPLAY") == NULL ? "/usr/bin/X" : "/usr/bin/Xnest", ":1", NULL }; srv.argv = server_argv; srv.name = ":1"; int res = nodm_xserver_start(&srv); if (res != E_SUCCESS) { fprintf(stderr, "nodm_xserver_start return code: %d\n", res); return res; } res = nodm_xserver_stop(&srv); if (res != E_SUCCESS) { fprintf(stderr, "nodm_xserver_stop return code: %d\n", res); return res; } test_ok(); } nodm-0.13/test.c000066400000000000000000000056321304142760400135240ustar00rootroot00000000000000/* * test - nodm test utilities * * Copyright 2011 Enrico Zini * * 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. */ #include "test.h" #include "common.h" #include "dm.h" #include #include #include #include #include void test_setup_dm(struct nodm_display_manager* dm, const char* xcmdline) { bool run_nested = getenv("DISPLAY") != NULL; bool is_root = getuid() == 0; nodm_display_manager_init(dm); if (xcmdline == NULL) { if (run_nested) xcmdline = "/usr/bin/Xnest :1 -geometry 1x1+0+0"; else xcmdline = ""; } ensure_succeeds(nodm_display_manager_parse_xcmdline(dm, xcmdline)); if (is_root) strcpy(dm->session.conf_run_as, "root"); else { dm->session.conf_use_pam = false; dm->session.conf_cleanup_xse = false; dm->session.conf_run_as[0] = 0; dm->vt.conf_initial_vt = -1; } } void test_start(const char* testname, bool verbose) { static struct log_config cfg; cfg.program_name = testname, cfg.log_to_syslog = false, cfg.log_to_stderr = true, cfg.log_level = NODM_LL_INFO; cfg.log_level = NODM_LL_VERB; log_start(&cfg); } void test_fail() { log_end(); exit(E_PROGRAMMING); } void test_ok() { log_end(); exit(0); } void ensure_equals(const char* a, const char* b) { if (a == NULL && b == NULL) return; if (a == NULL || b == NULL || strcmp(a, b) != 0) { log_warn("strings differ: \"%s\" != \"%s\"", a, b); test_fail(); } } void ensure_not_equals(const char* a, const char* b) { if (a == NULL && b == NULL) { log_warn("strings are both NULL"); test_fail(); } if (a != NULL && b != NULL && strcmp(a, b) == 0) { log_warn("strings are both \"%s\"", a); test_fail(); } } void ensure_equali(int a, int b) { if (a != b) { log_warn("values differ: %d != %d", a, b); test_fail(); } } void _ensure_succeeds(int code, const char* file, int line, const char* desc) { if (code != E_SUCCESS) { log_err("%s:%d:%s failed with code %d (%s)", file, line, desc, code, nodm_strerror(code)); test_fail(); } } nodm-0.13/test.h000066400000000000000000000036111304142760400135240ustar00rootroot00000000000000/* * test - nodm test utilities * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_TEST_H #define NODM_TEST_H #include "log.h" #include #include struct nodm_display_manager; /// Setup the logging system for a test script void test_start(const char* testname, bool verbose); /// exit() the program reporting a test failure void test_fail() __attribute__((noreturn)); /// exit() the program reporting a success void test_ok() __attribute__((noreturn)); /** * Setup DM for tests * * If running as root and no $DISPLAY is set, tries to start X, else tries to * start xnest. * * @param xcmdline * Can be NULL for the appropriate default. */ void test_setup_dm(struct nodm_display_manager* dm, const char* xcmdline); /// Ensure that two strings are the same void ensure_equals(const char* a, const char* b); /// Ensure that two strings are different void ensure_not_equals(const char* a, const char* b); /// Ensure that two integers are the same void ensure_equali(int a, int b); #define ensure_succeeds(val) _ensure_succeeds((val), __FILE__, __LINE__, #val) void _ensure_succeeds(int code, const char* file, int line, const char* desc); #endif nodm-0.13/test_nodm000077500000000000000000000012011304142760400143070ustar00rootroot00000000000000#!/bin/sh case "$1" in '') # test "$2" != "a" && echo "\$3 is '$2' instead of 'a'" # test "$3" != "b" && echo "\$4 is '$3' instead of 'b'" # test "$4" != "c" && echo "\$5 is '$4' instead of 'c'" echo "PATH is: $PATH" echo "USER is: $USER" echo "HOME is: $HOME" echo "pwd: `pwd`" echo "id: `id`" pstree `pgrep nodm|head -1` # set > test_nodm.env.tmp exit 0 ;; *) export NODM_USER=$1 export NODM_XINIT="time" export NODM_XSESSION="`pwd`/$0" # export NODM_X_OPTIONS="runtest a b c" export NODM_MIN_SESSION_TIME=60 exec ./nodm --session `pwd`/nodm ;; # '') # echo "Use: $0 username" >&2 # exit 1 # ;; esac nodm-0.13/vt.c000066400000000000000000000055071304142760400131770ustar00rootroot00000000000000/* * vt - VT allocation * * Copyright 2009--2011 Enrico Zini * * 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. */ #include "vt.h" #include "common.h" #include "log.h" #include #include #include #include #include #include #include #include #include /** * Issue a VT_GETSTATE ioctl on the given device, if it supports it. * * @return true if it succeeded, false otherwise */ static bool try_vtstate(const char* dev, struct vt_stat* vtstat) { bool res = false; int fd = open(dev, O_WRONLY | O_NOCTTY, 0); if (fd < 0) goto cleanup; if (ioctl (fd, VT_GETSTATE, vtstat) < 0) goto cleanup; res = true; cleanup: if (fd >= 0) close(fd); return res; } // Find a device that supports the VT_GETSTATE ioctl static bool get_vtstate(struct vt_stat* vtstat) { if (try_vtstate("/dev/tty", vtstat)) return true; if (try_vtstate("/dev/tty0", vtstat)) return true; if (try_vtstate("/dev/console", vtstat)) return true; return false; } void nodm_vt_init(struct nodm_vt* vt) { vt->conf_initial_vt = strtol(getenv_with_default("NODM_FIRST_VT", "7"), NULL, 10); vt->fd = -1; vt->num = -1; } int nodm_vt_start(struct nodm_vt* vt) { if (vt->conf_initial_vt == -1) return E_SUCCESS; int vtnum = vt->conf_initial_vt; struct vt_stat vtstat; if (!get_vtstate(&vtstat)) { log_err("cannot find or open the console"); return E_VT_ALLOC_ERROR; } unsigned short vtmask; for (vtmask = 1 << vtnum; vtstat.v_state & vtmask; ++vtnum, vtmask <<= 1) ; if (!vtmask) { log_err("all VTs seem to be busy"); return E_VT_ALLOC_ERROR; } char vtname[15]; snprintf(vtname, 15, "/dev/tty%d", vtnum); vt->fd = open(vtname, O_RDWR | O_NOCTTY, 0); if (vt->fd < 0) { log_err("cannot open %s: %m", vtname); return E_OS_ERROR; } vt->num = vtnum; return E_SUCCESS; } void nodm_vt_stop(struct nodm_vt* vt) { if (vt->fd != -1) { close(vt->fd); vt->fd = -1; vt->num = -1; } } nodm-0.13/vt.h000066400000000000000000000026031304142760400131760ustar00rootroot00000000000000/* * vt - VT allocation * * Copyright 2009--2011 Enrico Zini * * 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. */ #ifndef NODM_VT_H #define NODM_VT_H /// VT allocation state struct nodm_vt { /** * First VT to try to allocate. -1 means 'no VT allocation' */ int conf_initial_vt; /// Number of the VT that has been allocated (-1 for none) int num; /// File decriptor pointing to the open VT (-1 for none) int fd; }; /// Initialise a vt structure with default values void nodm_vt_init(struct nodm_vt* vt); /// Allocate a virtual terminal and keep it open int nodm_vt_start(struct nodm_vt* vt); /// Release the virtual terminal void nodm_vt_stop(struct nodm_vt* vt); #endif nodm-0.13/xserver.c000066400000000000000000000324671304142760400142510ustar00rootroot00000000000000/* * xserver - X server startup functions * * Copyright 2011 Enrico Zini * * 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. */ /* * nodm_xserver_read_window_path is taken from xdm's dm.c which is: * * Copyright 1988, 1998 The Open Group * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation. * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of The Open Group shall * not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization * from The Open Group. */ #define _GNU_SOURCE #include "xserver.h" #include "common.h" #include "log.h" #include #include #include #include #include #include #include #include #include #include #include #include // Signal handlers static bool server_started = false; static void on_sigusr1(int sig) { server_started = true; } static void on_sigchld(int sig) {} void nodm_xserver_init(struct nodm_xserver* srv) { // Get the user we should run the session for srv->conf_timeout = atoi(getenv_with_default("NODM_X_TIMEOUT", "30")); srv->argv = 0; srv->name = 0; srv->pid = -1; srv->dpy = NULL; srv->windowpath = NULL; if (sigemptyset(&srv->orig_signal_mask) == -1) log_err("sigemptyset error: %m"); } int nodm_xserver_start(struct nodm_xserver* srv) { // Function return code int return_code = E_SUCCESS; // True if we should restore the signal mask at exit bool signal_mask_altered = false; // Initialise common signal handling machinery struct sigaction sa; sa.sa_flags = 0; if (sigemptyset(&sa.sa_mask) < 0) { log_err("sigemptyset failed: %m"); return E_PROGRAMMING; } // Take note of the old SIGCHLD handler struct sigaction sa_sigchld_old; if (sigaction(SIGCHLD, NULL, &sa_sigchld_old) == -1) { log_err("sigaction failed: %m"); return E_PROGRAMMING; } // Catch server ready notifications via SIGUSR1 struct sigaction sa_usr1_old; server_started = false; sa.sa_handler = on_sigusr1; if (sigaction(SIGUSR1, &sa, &sa_usr1_old) == -1) { log_err("sigaction failed: %m"); return E_PROGRAMMING; } // From now on we need to perform cleanup before returning if (log_verb(NULL)) { // Log the concatenated command line char buf[4096]; int pos = 0; const char** s = srv->argv; for ( ; *s && pos < 4096; ++s) { int r = snprintf(buf + pos, 4096 - pos, " %s", *s); if (r < 0) break; pos += r; } log_verb("starting X server %s", buf); } // fork/exec the X server srv->pid = fork (); if (srv->pid == 0) { // child // Restore the original signal mask if (sigprocmask(SIG_SETMASK, &srv->orig_signal_mask, NULL) == -1) log_err("sigprocmask failed: %m"); // Stop the logging subsystem before we quit via exec log_end(); // don't hang on read/write to control tty (from xinit) signal(SIGTTIN, SIG_IGN); signal(SIGTTOU, SIG_IGN); // Ignore SIGUSR1 to signal the X server that it should send us SIGUSR1 // when ready signal(SIGUSR1, SIG_IGN); // prevent the server from getting sighup from vhangup() (from xinit) setpgid(0, getpid()); execv(srv->argv[0], (char *const*)srv->argv); log_err("cannot start %s: %m", srv->argv[0]); exit(errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC); } else if (srv->pid == -1) { log_err("cannot fork to run %s: %m", srv->argv[0]); return_code = E_OS_ERROR; goto cleanup; } // Get notified on sigchld, so nanosleep later will exit with EINTR if the // X server dies. If the server died before we set this signal handler, // that's fine, since waitpid will notice it anyway sa.sa_handler = on_sigchld; if (sigaction(SIGCHLD, &sa, NULL) == -1) { log_err("sigaction failed: %m"); return_code = E_PROGRAMMING; goto cleanup; } // Unblock SIGCHLD and SIGUSR1 sigset_t orig_set; sigset_t cur_set; if (sigemptyset(&cur_set) == -1) { log_err("sigemptyset failed: %m"); return_code = E_PROGRAMMING; goto cleanup; } if (sigaddset(&cur_set, SIGCHLD) == -1 || sigaddset(&cur_set, SIGUSR1) == -1) { log_err("sigaddset failed: %m"); return_code = E_PROGRAMMING; goto cleanup; } if (sigprocmask(SIG_UNBLOCK, &cur_set, &orig_set) == -1) { log_err("sigprocmask failed: %m"); return_code = E_PROGRAMMING; goto cleanup; } signal_mask_altered = true; // Wait for SIGUSR1, for the server to die or for a timeout struct timespec timeout = { .tv_sec = srv->conf_timeout, .tv_nsec = 0 }; while (!server_started) { // Check if the server has died int status; pid_t res = waitpid(srv->pid, &status, WNOHANG); if (res == -1) { if (errno == EINTR) continue; log_err("waitpid on %s failed: %m", srv->argv[0]); return_code = E_OS_ERROR; goto cleanup; } if (res == srv->pid) { nodm_xserver_report_exit(srv, status); srv->pid = -1; return_code = E_X_SERVER_DIED; goto cleanup; } // Wait some time for something to happen struct timespec rem; if (nanosleep(&timeout, &rem) == -1) { if (errno == EINTR) { timeout = rem; continue; } log_err("nanosleep failed: %m"); return_code = E_OS_ERROR; goto cleanup; } else { log_err("X server did not respond after %u seconds", srv->conf_timeout); return_code = E_X_SERVER_TIMEOUT; goto cleanup; } } log_verb("X is ready to accept connections"); return_code = nodm_xserver_connect(srv); if (return_code != E_SUCCESS) goto cleanup; cleanup: // Restore signal mask if (signal_mask_altered) if (sigprocmask(SIG_SETMASK, &orig_set, NULL) == -1) log_err("sigprocmask failed: %m"); // Kill the X server if an error happened if (srv->pid > 0 && return_code != E_SUCCESS) nodm_xserver_stop(srv); // Restore signal handlers sigaction(SIGCHLD, NULL, &sa_sigchld_old); sigaction(SIGUSR1, NULL, &sa_usr1_old); return return_code; } int nodm_xserver_stop(struct nodm_xserver* srv) { nodm_xserver_disconnect(srv); int res = child_must_exit(srv->pid, "X server"); srv->pid = -1; if (srv->windowpath != NULL) { free(srv->windowpath); srv->windowpath = NULL; } return res; } static int xopendisplay_error_handler(Display* dpy) { log_err("I/O error in XOpenDisplay"); log_end(); exit(E_XLIB_ERROR); } /* static int x_error_handler(Display* dpy, XErrorEvent* e) { log_err("X error"); return 0; } */ int nodm_xserver_connect(struct nodm_xserver* srv) { log_verb("connecting to X server"); //XSetErrorHandler(x_error_handler); for (int i = 0; i < 5; ++i) { if (i > 0) log_info("connecting to X server, attempt #%d", i+1); XSetIOErrorHandler(xopendisplay_error_handler); srv->dpy = XOpenDisplay(srv->name); XSetIOErrorHandler((int (*)(Display *))0); if (srv->dpy == NULL) { log_err("could not connect to X server on \"%s\"", srv->name); sleep(1); } else break; } // from xdm: remove close-on-exec and register to close at the next fork, why? We'll find out // RegisterCloseOnFork (ConnectionNumber (d->dpy)); // fcntl (ConnectionNumber (d->dpy), F_SETFD, 0); return srv->dpy == NULL ? E_X_SERVER_CONNECT : E_SUCCESS; } static jmp_buf close_env; static int ignorexio(Display *dpy) { longjmp(close_env, 1); // Not reached return 0; } int nodm_xserver_disconnect(struct nodm_xserver* srv) { log_verb("disconnecting from X server"); // TODO: get/check pending errors (how?) if (srv->dpy != NULL) { XSetIOErrorHandler(ignorexio); if (! setjmp(close_env)) XCloseDisplay(srv->dpy); else log_warn("I/O error on display close"); XSetIOErrorHandler(NULL); srv->dpy = NULL; } return E_SUCCESS; } int nodm_xserver_read_window_path(struct nodm_xserver* srv) { /* setting WINDOWPATH for clients */ Atom prop; Atom actualtype; int actualformat; unsigned long nitems; unsigned long bytes_after; unsigned char *buf; const char *windowpath; char *newwindowpath; unsigned long num; log_verb("reading WINDOWPATH value from server"); prop = XInternAtom(srv->dpy, "XFree86_VT", False); if (prop == None) { log_err("no XFree86_VT atom"); return E_XLIB_ERROR; } if (XGetWindowProperty(srv->dpy, DefaultRootWindow(srv->dpy), prop, 0, 1, False, AnyPropertyType, &actualtype, &actualformat, &nitems, &bytes_after, &buf)) { log_err("no XFree86 VT property"); return E_XLIB_ERROR; } if (nitems == 0) num = DefaultRootWindow(srv->dpy); else { if (nitems != 1) { log_err("%lu!=1 items in XFree86_VT property", nitems); XFree(buf); return E_XLIB_ERROR; } switch (actualtype) { case XA_CARDINAL: case XA_INTEGER: case XA_WINDOW: switch (actualformat) { case 8: num = (*(uint8_t *)(void *)buf); break; case 16: num = (*(uint16_t *)(void *)buf); break; case 32: num = (*(uint32_t *)(void *)buf); break; default: log_err("unsupported format %d in XFree86_VT property", actualformat); XFree(buf); return E_XLIB_ERROR; } break; default: log_err("unsupported type %lx in XFree86_VT property", actualtype); XFree(buf); return E_XLIB_ERROR; } } XFree(buf); windowpath = getenv("WINDOWPATH"); int path_size; if (!windowpath) path_size = asprintf(&newwindowpath, "%lu", num); else path_size = asprintf(&newwindowpath, "%s:%lu", windowpath, num); if (path_size > 0) { if (srv->windowpath) free(srv->windowpath); srv->windowpath = newwindowpath; log_verb("WINDOWPATH: %s", srv->windowpath); } return E_SUCCESS; } void nodm_xserver_dump_status(struct nodm_xserver* srv) { fprintf(stderr, "xserver start timeout: %d\n", srv->conf_timeout); fprintf(stderr, "xserver command line:"); for (const char** s = srv->argv; *s; ++s) fprintf(stderr, " %s", *s); fputc('\n', stderr); fprintf(stderr, "xserver name: %s\n", srv->name); fprintf(stderr, "xserver window path: %s\n", srv->windowpath); fprintf(stderr, "xserver PID: %d\n", (int)srv->pid); fprintf(stderr, "xserver connected: %s\n", (srv->dpy != NULL) ? "yes" : "no"); } void nodm_xserver_report_exit(struct nodm_xserver* s, int status) { if (WIFEXITED(status)) log_warn("X server %d quit with status %d", (int)s->pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) log_err("X server %d was killed with signal %d", (int)s->pid, WTERMSIG(status)); else log_err("X server %d terminated with unknown status %d", (int)s->pid, status); } nodm-0.13/xserver.h000066400000000000000000000053061304142760400142460ustar00rootroot00000000000000/* * xserver - X server startup functions * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_SERVER_H #define NODM_SERVER_H #include #include /// Supervise an X server struct nodm_xserver { /// Timeout (in seconds) to use waiting for X to start int conf_timeout; /// X server command line const char **argv; /// X display name const char *name; /// X window path (dynamically allocated and owned by this structure) char *windowpath; /// X server pid pid_t pid; /// xlib Display connected to the server Display *dpy; /// Original signal mask at program startup sigset_t orig_signal_mask; }; /** * Initialise a struct nodm_xserver with NULL values */ void nodm_xserver_init(struct nodm_xserver* srv); /** * Start the X server and wait until it is ready to accept connections. * * @param srv * The struct nodm_xserver with X server information. argv and name are expected to * be filled, pid is filled. * @return * Exit status as described by the E_* constants */ int nodm_xserver_start(struct nodm_xserver* srv); /// Stop the X server int nodm_xserver_stop(struct nodm_xserver* srv); /// Dump all internal status to stderr void nodm_xserver_dump_status(struct nodm_xserver* srv); /** * Connect to the X server * * Uses srv->name, sets srv->dpy. * * @return * Exit status as described by the E_* constants */ int nodm_xserver_connect(struct nodm_xserver* srv); /** * Close connection to the X server * * Uses srv->dpy, sets it to NULL. * * @return * Exit status as described by the E_* constants */ int nodm_xserver_disconnect(struct nodm_xserver* srv); /** * Get the WINDOWPATH value for the server * * Uses srv->dpy, sets srv->windowpath * * @return * Exit status as described by the E_* constants */ int nodm_xserver_read_window_path(struct nodm_xserver* srv); /// Report that the X session has quit void nodm_xserver_report_exit(struct nodm_xserver* s, int status); #endif nodm-0.13/xsession-child.c000066400000000000000000000364431304142760400155050ustar00rootroot00000000000000/* * xsession-child - child side of X session * * Copyright 2009--2011 Enrico Zini * * 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. */ /* * Some parts are taken from su(1) which is: * Copyright 1989 - 1994, Julianne Frances Haugh * All rights reserved. * * 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. Neither the name of Julianne F. Haugh nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH 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 JULIE HAUGH 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. * * With some parts substantially derived from an ancestor of: * su for GNU. Run a shell with substitute user and group IDs. * Copyright (C) 1992-2003 Free Software Foundation, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "xsession-child.h" #include "xserver.h" #include "common.h" #include "log.h" #include #include #include #include #include #include #include #include #include /* compatibility with different versions of Linux-PAM */ #ifndef PAM_ESTABLISH_CRED #define PAM_ESTABLISH_CRED PAM_CRED_ESTABLISH #endif #ifndef PAM_DELETE_CRED #define PAM_DELETE_CRED PAM_CRED_DELETE #endif /* * Truncate ~/.xsession-errors if it is longer than \a maxsize. * * The function looks for .xsession-errors in the current directory, so when it * is called the current directory must be the user's homedir. * * The function also assumes that we are running as the user. As a consequence * it does not worry about symlink attacks, because they would only be possible * if the user's home directory is group or world writable. * * curdirname is the name of the current directory, and it is only used when * logging error messages. * * The function returns true on success, false on failure. */ static int cleanup_xse(off_t maxsize, const char* curdirname) { int ret = E_OS_ERROR; int xse_fd = -1; struct stat xse_st; xse_fd = open(".xsession-errors", O_WRONLY | O_CREAT, 0600); if (xse_fd < 0) { log_err("cannot open `%s/%s': %m", curdirname, ".xsession-errors"); goto cleanup; } if (fstat(xse_fd, &xse_st) < 0) { log_err("cannot stat `%s/%s': %m", curdirname, ".xsession-errors"); goto cleanup; } if (xse_st.st_size > maxsize) { if (ftruncate(xse_fd, 0) < 0) { log_err("cannot truncate `%s/%s': %m", curdirname, ".xsession-errors"); goto cleanup; } } /* If we made it so far, we succeeded */ ret = E_SUCCESS; cleanup: if (xse_fd >= 0) close(xse_fd); return ret; } /* * setup_uid_gid() split in two functions for PAM support - * pam_setcred() needs to be called after initgroups(), but * before setuid(). */ static int setup_groups(const struct passwd *info) { /* * Set the real group ID to the primary group ID in the password * file. */ if (setgid (info->pw_gid) == -1) { log_err("bad group ID `%d' for user `%s': %m\n", info->pw_gid, info->pw_name); return E_OS_ERROR; } /* * For systems which support multiple concurrent groups, go get * the group set from the /etc/group file. */ if (initgroups (info->pw_name, info->pw_gid) == -1) { log_err("initgroups failed for user `%s': %m\n", info->pw_name); return E_OS_ERROR; } return E_SUCCESS; } static int change_uid (const struct passwd *info) { /* * Set the real UID to the UID value in the password file. */ if (setuid(info->pw_uid)) { log_err("bad user ID `%d' for user `%s': %m\n", (int)info->pw_uid, info->pw_name); return E_OS_ERROR; } return E_SUCCESS; } static int setup_pam(struct nodm_xsession_child* s) { static struct pam_conv conv = { misc_conv, NULL }; char *cp; const char *tty = 0; // Name of tty SU is run from /* We only run if we are root */ if (getuid() != 0) { log_err("can only be run by root"); return E_NOPERM; } // Set up the nodm_xsession_child structure s->pamh = NULL; s->pam_status = PAM_SUCCESS; /* * Get the tty name. Entries will be logged indicating that the user * tried to change to the named new user from the current terminal. */ if (isatty (0) && (cp = ttyname (0))) { if (strncmp (cp, "/dev/", 5) == 0) tty = cp + 5; else tty = cp; } else { tty = "???"; } s->pam_status = pam_start("nodm", s->pwent.pw_name, &conv, &s->pamh); if (s->pam_status != PAM_SUCCESS) { log_err("pam_start: error %d", s->pam_status); return E_PAM_ERROR; } s->pam_status = pam_set_item(s->pamh, PAM_TTY, (const void *) tty); if (s->pam_status == PAM_SUCCESS) s->pam_status = pam_set_item(s->pamh, PAM_RUSER, (const void *) "root"); if (s->pam_status == PAM_SUCCESS) s->pam_status = pam_set_item(s->pamh, PAM_XDISPLAY, (const void *) s->srv->name); if (s->pam_status != PAM_SUCCESS) { log_err("pam_set_item: %s", pam_strerror(s->pamh, s->pam_status)); return E_PAM_ERROR; } signal (SIGINT, SIG_IGN); signal (SIGQUIT, SIG_IGN); /* FIXME: should we ignore this, or honour it? * this can fail if the current user's account is invalid. "This * includes checking for password and account expiration, as well as * verifying access hour restrictions." */ s->pam_status = pam_acct_mgmt(s->pamh, 0); if (s->pam_status != PAM_SUCCESS) log_warn("%s (Ignored)", pam_strerror(s->pamh, s->pam_status)); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); /* save SU information */ log_info("Successful su on %s for %s by %s", tty, s->pwent.pw_name, "root"); /* set primary group id and supplementary groups */ if (setup_groups(&(s->pwent))) { s->pam_status = PAM_ABORT; return E_OS_ERROR; } /* * pam_setcred() may do things like resource limits, console groups, * and much more, depending on the configured modules */ s->pam_status = pam_setcred(s->pamh, PAM_ESTABLISH_CRED); if (s->pam_status != PAM_SUCCESS) { log_err("pam_setcred: %s", pam_strerror(s->pamh, s->pam_status)); return E_PAM_ERROR; } s->pam_status = pam_open_session(s->pamh, 0); if (s->pam_status != PAM_SUCCESS) { log_err("pam_open_session: %s", pam_strerror(s->pamh, s->pam_status)); pam_setcred(s->pamh, PAM_DELETE_CRED); return E_PAM_ERROR; } /* update environment with all pam set variables */ char **envcp = pam_getenvlist(s->pamh); if (envcp) { while (*envcp) { putenv(*envcp); envcp++; } } /* become the new user */ if (change_uid(&(s->pwent)) != 0) { pam_close_session(s->pamh, 0); pam_setcred(s->pamh, PAM_DELETE_CRED); s->pam_status = PAM_ABORT; return E_OS_ERROR; } return E_SUCCESS; } static void shutdown_pam(struct nodm_xsession_child* s) { if (s->pam_status == PAM_SUCCESS) { s->pam_status = pam_close_session(s->pamh, 0); if (s->pam_status != PAM_SUCCESS) log_err("pam_close_session: %s", pam_strerror(s->pamh, s->pam_status)); } pam_end(s->pamh, s->pam_status); s->pamh = 0; } int nodm_xsession_child_common_env(struct nodm_xsession_child* s) { int return_code = E_SUCCESS; // Setup environment setenv("HOME", s->pwent.pw_dir, 1); setenv("USER", s->pwent.pw_name, 1); setenv("USERNAME", s->pwent.pw_name, 1); setenv("LOGNAME", s->pwent.pw_name, 1); setenv("PWD", s->pwent.pw_dir, 1); setenv("SHELL", s->pwent.pw_shell, 1); setenv("DISPLAY", s->srv->name, 1); // Read the WINDOWPATH value from the X server return_code = nodm_xserver_connect(s->srv); if (return_code != E_SUCCESS) goto cleanup; return_code = nodm_xserver_read_window_path(s->srv); if (return_code != E_SUCCESS) goto cleanup; return_code = nodm_xserver_disconnect(s->srv); if (return_code != E_SUCCESS) goto cleanup; setenv("WINDOWPATH", s->srv->windowpath, 1); // Clear the NODM_* environment variables unsetenv("NODM_USER"); unsetenv("NODM_XINIT"); unsetenv("NODM_XSESSION"); unsetenv("NODM_X_OPTIONS"); unsetenv("NODM_MIN_SESSION_TIME"); // Move to home directory if (chdir(s->pwent.pw_dir) == 0) { // Truncate ~/.xsession-errors if (s->conf_cleanup_xse) cleanup_xse(0, s->pwent.pw_dir); } cleanup: if (s->srv->dpy != NULL) nodm_xserver_disconnect(s->srv); return return_code; } int nodm_xsession_child(struct nodm_xsession_child* s) { int res = nodm_xsession_child_common_env(s); if (res != E_SUCCESS) return res; /* * This is a workaround for Linux libc bug/feature (?) - the * /dev/log file descriptor is open without the close-on-exec flag * and used to be passed to the new shell. There is "fcntl(LogFile, * F_SETFD, 1)" in libc/misc/syslog.c, but it is commented out (at * least in 5.4.33). Why? --marekm */ log_end(); /* * PAM_DATA_SILENT is not supported by some modules, and * there is no strong need to clean up the process space's * memory since we will either call exec or exit. pam_end (pamh, PAM_SUCCESS | PAM_DATA_SILENT); */ (void)execv(s->argv[0], (char **)s->argv); exit(errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC); } /* Signal handler for parent process later */ static int caught = 0; static void catch_signals (int sig) { ++caught; } /* This I ripped out of su.c from sh-utils after the Mandrake pam patch * have been applied. Some work was needed to get it integrated into * su.c from shadow. */ int nodm_xsession_child_pam(struct nodm_xsession_child* s) { int child = -1; sigset_t ourset; struct sigaction action; int res = setup_pam(s); if (res != E_SUCCESS) return res; // Read current signal mask sigset_t origmask; // Block all signals sigfillset (&ourset); if (sigprocmask (SIG_BLOCK, &ourset, &origmask)) { log_err("sigprocmask malfunction"); goto killed; } child = fork (); if (child == 0) { /* child shell */ // Restore original signal mask if (sigprocmask (SIG_SETMASK, &origmask, NULL)) { log_err("sigprocmask malfunction"); goto killed; } int res = nodm_xsession_child_common_env(s); if (res != E_SUCCESS) return res; /* * This is a workaround for Linux libc bug/feature (?) - the * /dev/log file descriptor is open without the close-on-exec flag * and used to be passed to the new shell. There is "fcntl(LogFile, * F_SETFD, 1)" in libc/misc/syslog.c, but it is commented out (at * least in 5.4.33). Why? --marekm */ log_end(); /* * PAM_DATA_SILENT is not supported by some modules, and * there is no strong need to clean up the process space's * memory since we will either call exec or exit. pam_end (pamh, PAM_SUCCESS | PAM_DATA_SILENT); */ (void) execv(s->argv[0], (char **)s->argv); exit (errno == ENOENT ? E_CMD_NOTFOUND : E_CMD_NOEXEC); } else if (child == -1) { log_err("cannot fork user shell: %m"); return E_OS_ERROR; } /* parent only */ /* Reset caught signal flag */ caught = 0; /* Catch SIGTERM and SIGALRM using 'catch_signals' */ action.sa_handler = catch_signals; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigemptyset (&ourset); if (sigaddset (&ourset, SIGTERM) #ifdef DEBUG_NODM || sigaddset (&ourset, SIGINT) || sigaddset (&ourset, SIGQUIT) #endif || sigaddset (&ourset, SIGALRM) || sigaction (SIGTERM, &action, NULL) || sigprocmask (SIG_UNBLOCK, &ourset, NULL) ) { log_err("signal masking malfunction"); goto killed; } do { int pid; pid = waitpid (-1, &(s->exit_status), WUNTRACED); if (WIFSTOPPED (s->exit_status)) { kill (getpid (), SIGSTOP); /* once we get here, we must have resumed */ kill (pid, SIGCONT); } } while (WIFSTOPPED (s->exit_status)); /* Unblock signals */ sigfillset (&ourset); if (sigprocmask (SIG_UNBLOCK, &ourset, NULL)) { log_err("signal malfunction"); goto killed; } if (caught) goto killed; shutdown_pam(s); return E_SUCCESS; killed: if (child != -1) { log_warn("session terminated, killing shell..."); kill (child, SIGTERM); sleep (2); kill (child, SIGKILL); log_warn(" ...shell killed."); } shutdown_pam(s); return E_SESSION_DIED; } nodm-0.13/xsession-child.h000066400000000000000000000034261304142760400155050ustar00rootroot00000000000000/* * xsession-child - child side of X session * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_XSESSION_CHILD_H #define NODM_XSESSION_CHILD_H #include #include #include struct nodm_xserver; struct nodm_xsession_child { /// X server we connect to struct nodm_xserver* srv; /// If set to true, perform ~/.xsession-errors cleanup bool conf_cleanup_xse; /// Information about the user we run the session for struct passwd pwent; /// PAM session handle (or NULL if not used) pam_handle_t *pamh; /// Return code of the last PAM function called int pam_status; /// Command line to run const char** argv; /// Child exit status int exit_status; }; /// Setup common environment bits in the child process int nodm_xsession_child_common_env(struct nodm_xsession_child* s); /// Just exec the session int nodm_xsession_child(struct nodm_xsession_child* s); /// Run a child process inside a PAM session int nodm_xsession_child_pam(struct nodm_xsession_child* s); #endif nodm-0.13/xsession.c000066400000000000000000000124551304142760400144210ustar00rootroot00000000000000/* * xsession - nodm X session * * Copyright 2011 Enrico Zini * * 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. */ #include "xsession.h" #include "xsession-child.h" #include "xserver.h" #include "log.h" #include "common.h" #include #include #include #include #include #include #include #include #include int nodm_xsession_init(struct nodm_xsession* s) { s->child_body = NULL; s->conf_use_pam = true; s->conf_cleanup_xse = true; if (sigemptyset(&s->orig_signal_mask) == -1) log_err("sigemptyset error: %m"); // Get the user we should run the session for if (!bounded_strcpy(s->conf_run_as, getenv_with_default("NODM_USER", "root"))) log_warn("username has been truncated"); // Get the X session command if (!bounded_strcpy(s->conf_session_command, getenv_with_default("NODM_XSESSION", "/etc/X11/Xsession"))) log_warn("session command has been truncated"); s->pid = -1; return E_SUCCESS; } int nodm_xsession_start(struct nodm_xsession* s, struct nodm_xserver* srv) { struct nodm_xsession_child child; child.srv = srv; child.conf_cleanup_xse = s->conf_cleanup_xse; // Validate the user using the normal system user database struct passwd *pw = 0; if (s->conf_run_as[0] == 0) pw = getpwuid(getuid()); else pw = getpwnam(s->conf_run_as); if (pw == NULL) { log_err("unknown username: %s", s->conf_run_as); return E_OS_ERROR; } child.pwent = *pw; // Create the argument list const char* args[5]; args[0] = "/bin/sh"; args[1] = "-l"; args[2] = "-c"; args[3] = s->conf_session_command; args[4] = NULL; child.argv = args; log_verb("starting X session \"%s\"", s->conf_session_command); // Variables that gdm sets but we do not: // // This is something that we should see how to handle. // What I know so far is: // - It should point to ~/.Xauthority, which should exist. // - 'xauth generate' should be able to create it if missing, but I // have not tested it yet. // g_setenv ("XAUTHORITY", d->userauth, TRUE); // // This is 'gnome', 'kde' and so on, and should probably be set by the // X session script: // g_setenv ("DESKTOP_SESSION", session, TRUE); // // This looks gdm specific: // g_setenv ("GDMSESSION", session, TRUE); // Variables that gdm sets but we delegate other tools to set: // // This is set by the pam_getenvlist loop above // g_setenv ("XDG_SESSION_COOKIE", ck_session_cookie, TRUE); // // This is set by "sh -l" from /etc/profile // if (pwent->pw_uid == 0) // g_setenv ("PATH", gdm_daemon_config_get_value_string (GDM_KEY_ROOT_PATH), TRUE); // else // g_setenv ("PATH", gdm_daemon_config_get_value_string (GDM_KEY_PATH), TRUE); // s->pid = fork(); if (s->pid == 0) { // Restore the original signal mask if (sigprocmask(SIG_SETMASK, &s->orig_signal_mask, NULL) == -1) log_err("sigprocmask failed: %m"); // cargogulted from xinit setpgid(0, getpid()); // child shell */ if (s->child_body) exit(s->child_body(&child)); else if (s->conf_use_pam) exit(nodm_xsession_child_pam(&child)); else exit(nodm_xsession_child(&child)); } else if (s->pid == -1) { log_err("cannot fork user shell: %m"); return E_OS_ERROR; } return E_SUCCESS; } int nodm_xsession_stop(struct nodm_xsession* s) { int res = child_must_exit(s->pid, "X session"); s->pid = -1; return res; } void nodm_xsession_dump_status(struct nodm_xsession* s) { fprintf(stderr, "xsession command: %s\n", s->conf_session_command); fprintf(stderr, "xsession user: %s\n", s->conf_run_as); fprintf(stderr, "xsession use PAM: %s\n", s->conf_use_pam ? "yes" : "no"); fprintf(stderr, "xsession cleanup ~/.xsession-errors: %s\n", s->conf_cleanup_xse ? "yes" : "no"); fprintf(stderr, "xsession pid: %d\n", (int)s->pid); fprintf(stderr, "xsession body overridden by test: %s\n", (s->child_body != NULL) ? "yes" : "no"); } void nodm_xsession_report_exit(struct nodm_xsession* s, int status) { if (WIFEXITED(status)) log_warn("X session %d quit with status %d", (int)s->pid, WEXITSTATUS(status)); else if (WIFSIGNALED(status)) log_warn("X session %d was killed with signal %d", (int)s->pid, WTERMSIG(status)); else log_warn("X session %d terminated with unknown status %d", (int)s->pid, status); } nodm-0.13/xsession.h000066400000000000000000000041161304142760400144210ustar00rootroot00000000000000/* * xsession - nodm X session * * Copyright 2011 Enrico Zini * * 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. */ #ifndef NODM_SESSION_H #define NODM_SESSION_H #include #include struct nodm_xserver; struct nodm_xsession_child; /// Supervise an X session struct nodm_xsession { /// Command to run as the X session char conf_session_command[1024]; /** * Username to use for the X session. * * Empty string means 'do not change user' */ char conf_run_as[128]; /// If true, wrap the session in a PAM session bool conf_use_pam; /// If set to true, perform ~/.xsession-errors cleanup bool conf_cleanup_xse; /// X session pid pid_t pid; /// If non-NULL, use as child process main body (used for tests) int (*child_body)(struct nodm_xsession_child* s); /// Original signal mask at program startup sigset_t orig_signal_mask; }; /// Initialise a struct nodm_session with default values int nodm_xsession_init(struct nodm_xsession* s); /// Start the X session int nodm_xsession_start(struct nodm_xsession* s, struct nodm_xserver* srv); /// Stop the X session int nodm_xsession_stop(struct nodm_xsession* s); /// Dump all internal status to stderr void nodm_xsession_dump_status(struct nodm_xsession* s); /// Report that the X session has quit void nodm_xsession_report_exit(struct nodm_xsession* s, int status); #endif