--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ 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.)
+\f
+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.
+\f
+ 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.
+\f
+ 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
+\f
+ Appendix: How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+* Thu Dec 15 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.14 release
+ - Fixed brace style. (inotify_handler.c acpid.c) (Ted Felix)
+ - Added support for a "K" suffix on event strings to indicate that they
+ may have originated from a keyboard input layer device. This can be
+ used to differentiate between a power switch on the keyboard, and a power
+ switch on the computer's case.
+
+ (connection_list.h inotify_handler.c input_layer.c netlink.c proc.c
+ sock.c) (Ted Felix)
+
+ - Added a pathname to connection along with a find_connection_name().
+ Modifications to process_inotify() to log IN_DELETE events. Additional
+ debugging output.
+
+ These changes were experimentation related to dealing with a ThinkPad
+ suspend problem. They should have no effect on acpid's behavior. They are
+ mainly noticeable as a change in the logging.
+
+ The Problem: When resuming from suspend, the lid switch and power button's
+ /dev/input event files do not trigger an IN_CREATE, so acpid doesn't
+ reconnect to them. Restarting acpid fixes this.
+
+ Tried using IN_DELETE instead of ENODEV to detect the drivers going away.
+ Worked fine for disconnecting/reconnecting a USB keyboard, but not for the
+ ThinkPad suspend problem. I've given up on fixing this as it appears to be
+ a kernel or driver issue.
+
+ (connection_list.c connection_list.h inotify_handler.c input_layer.c
+ netlink.c proc.c sock.c) (Ted Felix)
+
+* Tue Nov 15 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.13 release
+ - Fix for socket name buffer overflow. (ud_socket.c) (Ted Felix)
+ - Moved acpid_log() out of acpid.h/.c and into log.h/.c to make it easier
+ for all parts of acpid to use. (Makefile acpid.h acpid.c connection_list.c
+ event.c inotify_handler.c input_layer.c log.h log.c netlink.c proc.c
+ sock.c ud_socket.c) (Ted Felix)
+ - Cleaned up #includes and comment style. (libnetlink.c libnetlink.h)
+ (Ted Felix)
+
+* Mon Aug 15 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.12 release
+ - Changed exit() to _exit() if execl() fails. (event.c) (Ted Felix)
+
+* Sat Jul 30 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.11 release
+ - Set umask to 0077 for scripts run by acpid. (event.c) (Ted Felix)
+
+* Tue May 17 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.10 release
+ - Fixed compiler warnings in kacpimon. (kacpimon/libnetlink.c)
+ (Michael Meskes)
+ - systemd support. The socket acpid creates to connect with clients can now
+ be passed as stdin (fd 0) at startup. (acpid.c sock.h sock.c acpid.8)
+ (Reiner Klussmeier)
+ - man page improvements (acpid.8) (Ted Felix)
+
+* Fri Apr 15 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.9 release
+ - Removed newlines from acpid_log() calls and modified acpid_log() to
+ no longer need newlines. This change to acpid prevents blank lines in
+ the log for versions of syslogd that preserve newlines. (acpid.c
+ connection_list.c event.c inotify_handler.c input_layer.c netlink.c
+ proc.c sock.c) (Michael Meskes, Ted Felix)
+ - Added fcntl() for O_NONBLOCK on the client sockets. This prevents acpid
+ from hanging if a client behaves badly. (sock.c) (Vasiliy Kulikov)
+ From: http://www.openwall.com/lists/oss-security/2011/01/19/4
+ - Improvements to error handling. (sock.c) (Ted Felix)
+
+* Tue Feb 15 2011 Ted Felix <http://www.tedfelix.com>
+ - 2.0.8 release
+ - Fixed "comparison between signed and unsigned integer expressions"
+ error with gcc 4.6. (libnetlink.c) (Eugeni Dodonov)
+ - Fixed unused variable "type" with gcc 4.6. (libnetlink.c) (Jiri Skala)
+
+* Mon Nov 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.7 release
+ - Reduced the startup logging and skipped processing of "." and ".."
+ in the config files directory. Debian #598198. (event.c proc.c)
+ (Ted Felix)
+ - Added CD-related buttons. Debian #600564. (input_layer.c)
+ (Stanislav Maslovski)
+ - Removed the "getppid() == 1" hack from daemonize().
+ Red Hat #629740. "While forking is ugly in theory, this has the advantage
+ that it is clear that the acpid unix socket is properly installed before
+ systemd continues starting other units that want to be started after
+ acpid." - Lennart Poettering
+ For upstart, specify "expect fork" in your upstart .conf file for acpid.
+ For systemd, specify "Type=forking" in your systemd service file for
+ acpid.
+ (acpid.c) (Lennart Poettering)
+ - Added FD_CLOEXEC to the input layer fd's. Red Hat #648221.
+ (input_layer.c) (Daniel Walsh)
+
+* Tue Jun 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.6 release
+ - Added getXconsole() to samples/powerbtn/powerbtn.sh to eliminate
+ dependency on other parts of Debian. (samples/powerbtn/powerbtn.sh)
+ (Debian and Timo Gurr)
+
+* Sat May 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.5 release
+ - Moved powerbtn.sh to powerbtn.sh.old and brought in the latest
+ powerbtn.sh from Debian which handles KDE4.
+ (samples/powerbtn/powerbtn.sh samples/powerbtn/powerbtn.sh.old)
+ (Ted Felix)
+
+* Thu Apr 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.4 release
+ - Replaced all the mandb code in the makefile with a comment. (Makefile)
+ (Ted Felix)
+ - Revamped logging. (acpid.c event.c inotify_handler.c input_layer.c
+ netlink.c proc.c sock.c) (Ted Felix)
+ - Removed CR's (\r) from files. (Changelog connection_list.c sock.c)
+ (Ted Felix)
+ - Cleaned up the samples directory a bit. Also added powerbtn samples
+ taken from Debian. (samples/*) (Ted Felix)
+ - Removed the %changelog token from the top of the Changelog. This
+ appears to be a Red Hat-ism. (Changelog) (Ted Felix)
+
+* Mon Mar 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.3 release
+ - Fixed problem in makefile with mandb line when DESTDIR is not empty.
+ (Makefile) (Jiri Skala)
+ - Added missing headers needed by Red Hat/Fedora. (acpid.c sock.c)
+ (Jiri Skala)
+ - Updated daemonize() to be in keeping with Doug Potter's "How to Daemonize
+ in Linux". (acpid.c) (Ted Felix)
+ - Removed the test for bad kernels. (acpid.c) (Ted Felix)
+
+* Mon Feb 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.2 release
+ - Increased MAX_CONNECTIONS to 20. (connection_list.c)
+ - Recalculate highestfd in delete_connection(). (connection_list.c)
+ (Willi Mann)
+ - Removed old naming convention code for configuration files. (event.c)
+ - Fixed unescaped dashes in manpage. (acpid.8) (Michael Meskes)
+ - Added fix for mandb issue. (Makefile) (Robby Workman)
+ - Moved README-NETLINK into README. (README-NETLINK README)
+
+* Fri Jan 15 2010 Ted Felix <http://www.tedfelix.com>
+ - 2.0.1 release
+ - Makefile improvements for packagers. (Makefile) (Robby Workman)
+ * Use DESTDIR instead of INSTPREFIX
+ * Allow custom compiler optimizations
+ * Allow alternative prefix
+ * Allow custom manpage directory
+ * Add DOCDIR and install docs
+ * Remove reference to "mandb -q" - this doesn't exist everywhere
+
+ After this patch, packagers can do e.g.:
+
+ make install \
+ OPT="-O3" \
+ PREFIX=/opt \
+ MANDIR=/opt/man \
+ DOCDIR=/opt/doc/apcid \
+ DESTDIR=/tmp/package
+
+ - run-parts(8) naming convention for configuration files. (event.c
+ acpid.8) (Debian)
+
+* Tue Dec 15 2009 Ted Felix <http://www.tedfelix.com>
+ - 2.0.0 release
+ - Fixed gcc 4.4 warnings for strict aliasing. (kacpimon/acpi_ids.c)
+ (Debian)
+ - Fixed a printf() warning. (kacpimon/input_layer.c) (Debian)
+ - Fixed kacpimon makefile for a release build. (kacpimon/makefile)
+ (Ted Felix)
+ - Added dist and install targets to kacpimon makefile. Added
+ a set of CFLAGS for Debian-style (-g -O2) builds. Fixed error
+ messages in clean target with a "-f" to rm. (kacpimon/makefile)
+ (Ted Felix)
+ - Increased MAX_CONNECTIONS to 20 for kacpimon.
+ (kacpimon/connection_list.c) (Ted Felix)
+ - Improved "dist" target with DISTNAME in makefile. (Makefile)
+ (Ted Felix)
+ - Added mandb to the install target so that the man database will get
+ updated after the man pages are installed. (Makefile)
+ (Ted Felix)
+ - Updated the "event=" line in sample.conf to be more modern.
+ (samples/sample.conf) (Ted Felix)
+ - Improved the Example in the man page. (acpid.8) (Ted Felix)
+
+* Thu Nov 13 2009 Ted Felix <http://www.tedfelix.com/>
+ - 1.0.10-netlink6 release
+ - Implemented discovery of new input layer devices using inotify(7).
+ (inotify_handler.h inotify_handler.c acpid.c input_layer.h input_layer.c)
+ (Ted Felix)
+ - Updated input layer event table to incorporate more events and
+ to support a format compatible with older event configuration
+ files. (input_layer.c) (Harald Braumann and Ted Felix)
+ - Cleanup and move of input layer constants. (acpid.h input_layer.c
+ inotify_handler.c) (Ted Felix)
+ - kacpimon now opens all event sources and reports where each event
+ comes from. This makes it more useful for discovering events.
+ (kacpimon/kacpimon.c kacpimon/input_layer.c kacpimon/netlink.c)
+ (Ted Felix)
+ - Turned off strict aliasing optimizations as the netlink code is
+ filled with strict aliasing problems. (Makefile) (Ted Felix)
+
+* Fri Nov 6 2009 Ted Felix <http://www.tedfelix.com/>
+ - 1.0.10-netlink5 release
+ - Fixed exit on device removal. (input_layer.c connection_list.h
+ connection_list.c) (Mikhail Krivtsov)
+
+* Sun Jul 19 2009 Ted Felix <http://www.tedfelix.com/>
+ - 1.0.10-netlink4 release
+ - Added events to input_layer.c to cover more buttons on the Thinkpad X40
+ (input_layer.c) (Peter Stuge)
+ - Fixed Makefile "dist" target to work properly. (Makefile) (Ted Felix)
+ - Added kacpimon to the codebase as a debugging tool. (kacpimon directory,
+ Makefile) (Ted Felix)
+ - Removed erroneous comment in sock.c about the 256 connection limit.
+ (sock.c) (Ted Felix)
+ - Removed unnecessary #include from connection_list.c. (connection_list.c)
+ (Ted Felix)
+
+* Mon May 04 2009 Ted Felix <http://www.tedfelix.com/>
+ - Fixed strict aliasing issue with gcc 4.4. (acpi_ids.c) (Michael Meskes
+ and Peter Alfredsen)
+ - 1.0.10-netlink3 release.
+
+* Sat May 02 2009 Ted Felix <http://www.tedfelix.com/>
+ - Merge of the following three 1.0.10 changes into 1.0.10-netlink2
+ (Michael Meskes and Ted Felix)
+
+* Wed Apr 22 2009 Tim Hockin <thockin@hockin.org>
+ - Bump version to 1.0.10 for release.
+
+* Wed Apr 22 2009 Tim Hockin <thockin@hockin.org>
+ - Add a -C (--clientmax) command line flag to set max number of non-root
+ socket connections. (acpi.c acpid.h acpid.8 event.c)
+ - Set the maximum number of socket clients to 256 by default. (acpid.h)
+ - Close clients that have disconnected. (acpid.c event.c) (Aaron Plattner
+ <aplattner@nvidia.com>)
+ - Give up and exit() if 5 accept() calls fail in a row. (acpid.c)
+
+* Mon Feb 09 2009 Tim Hockin <thockin@hockin.org>
+ - Open /dev/null O_RDWR, rather than O_RDONLY. (acpid.c)
+
+* Thu Dec 11 2008 Ted Felix <http://www.tedfelix.com/>
+ - Version 1.0.8ted1
+ - Netlink and Input Layer support. Many files have been changed and
+ several have been added. (Ted Felix <http://www.tedfelix.com/>)
+
+* Tue Oct 28 2008 Tim Hockin <thockin@hockin.org>
+ - Bump version to 1.0.8 for release.
+
+* Sun Oct 26 2008 Tim Hockin <thockin@hockin.org>
+ - Define _GNU_SOURCE. (Makefile) (Ted Felix <http://www.tedfelix.com/>)
+ - Rename a variable to avoid shadowing a global. (event.c) (Ted Felix
+ <http://www.tedfelix.com/>)
+ - Fix typos in man pages. (acpid.8, acpi_listen.8) (Ted Felix
+ <http://www.tedfelix.com/>)
+ - GCC 4.3.2 gives chdir() the _wur attribute (warn unused result). Check
+ for errors. (acpid.c) (Ted Felix <http://www.tedfelix.com/>)
+ - Check for ferror() in parse_file(). (event.c) (Ted Felix
+ <http://www.tedfelix.com/>)
+ - Only read regular files in acpid_read_conf(). (event.c) (Ted Felix
+ <http://www.tedfelix.com/>)
+ - Stop processing ACPI events when a lockfile exists (from Debian).
+ (acpid.8, acpid.c, acpid.h) (Ted Felix <http://www.tedfelix.com/>)
+
+* Sat Nov 24 2007 Tim Hockin <thockin@hockin.org>
+ - Build with -O2 flag (Makefile).
+ - Add -l (--logevents) option to enable logging of all events. Due to a
+ number of reports of log flooding (bad ACPI BIOS?) The new default is to
+ NOT log events. (acpid.c acpid.h event.h acpid.8)
+ - Add pidfile support and a -p (--pidfile) option to change the pidfile.
+ Default is /var/run/acpid.pid. (acpid.c acpid.8) (Javier Pello
+ <jpello@users.sourceforge.net>)
+ - Rename ACPI_* constants to ACPID_*. (acpid.c acpi_listen.c)
+ - Remove a bad cast of malloc()'s return value. (event.c)
+ - Add proper make dependencies. (Makefile)
+ - Close client file descriptors on exec(). (acpid.c) (Zdenek Prikryl
+ <zprikryl@redhat.com>)
+
+* Mon Sep 24 2007 Tim Hockin <thockin@hockin.org>
+ - Don't use a mode argument on open("/dev/null") (acpid.c) (Steve Grubb
+ <sgrubb@redhat.com>)
+ - Use GCC "__attribute__((format(printf)))" for acpid_log (acpid.c) (Steve
+ Grubb <sgrubb@redhat.com>)
+ - Fix a shadowed variable name (event.c) (Steve Grubb <sgrubb@redhat.com>)
+ - Fix a leaked fd on error (event.c) (Steve Grubb <sgrubb@redhat.com>)
+ - Fix a signed/unsigned comparison (event.c) (Steve Grubb
+ <sgrubb@redhat.com>)
+ - Compile with more warnings (Makefile) (Steve Grubb <sgrubb@redhat.com>)
+
+* Sat June 30 2007 Tim Hockin <thockin@hockin.org>
+ - Bump version to 1.0.6 for release.
+
+* Thu May 24 2007 Tim Hockin <thockin@hockin.org>
+ - Print event handler output to stdout only in debug mode (acpid.c, event.c).
+ - Update man page for new logging.
+
+* Wed May 23 2007 Tim Hockin <thockin@hockin.org>
+ - Correctly check for malloc() failures (event.c)
+ - Skip editor backup files when scanning for conf files (event.c) (Stefan
+ Seyfried <seife@suse.de>)
+ - Use syslog() for logging (acpid.c, event.c, acpid.h) (Stefan Seyfried
+ <seife@suse.de>)
+
+* Fri Dec 30 2005 Tim Hockin <thockin@hockin.org>
+ - Add a do_detach argument to acpid_cleanup_rules() to avoid closing
+ clients on a HUP (acpid.c, event.c) (Frederic Lepied
+ <flepied@users.sourceforge.net>)
+
+* Sat Sep 24 2005 Tim Hockin <thockin@hockin.org>
+ - Document -f option in the acpid man page (acpid.8)
+
+* Fri Sep 23 2005 Tim Hockin <thockin@hockin.org>
+ - Fix rule and fd leak when clients disconnect on socket (event.c) (Timo
+ Hoenig <thoenig@suse.de>)
+
+* Fri Oct 19 2005 Tim Hockin <thockin@hockin.org>
+ - Use socklen_t for sockets calls (ud_socket.c)
+
+* Sun Oct 24 2004 Tim Hockin <thockin@hockin.org>
+ - Update my own email to not say @sun.com anymore
+
+* Sun Oct 17 2004 Tim Hockin <thockin@hockin.org>
+ - mkdir BINDIR in Makefile
+
+* Sun Oct 17 2004 Tim Hockin <thockin@hockin.org>
+ - set acpi_listen stdout to be line-buffered (Gilles Chanteperdrix
+ <gilles.chanteperdrix@laposte.net>)
+ - detect a closed socket and exit (acpi_listen.c)
+ - detect a closed events file and exit (acpid.c)
+ - print read_line() errors (acpi_listen.c, acpid.c)
+ - added sample battery config and handler (Frank Dietrich
+ <ABLEsoft@gmx.de>)
+ - added sample AC adapter config/handler and hotkey config/handler for
+ Panasonic notebooks (David Bronaugh <dbronaugh@linuxboxen.org>)
+ - prep for 1.0.4 release
+
+* Fri Feb 13 2004 Tim Hockin <thockin@sun.com>
+ - dump debian/ and redhat/ dirs -- too much hassle
+ - change 'make rpm' to 'make dist'
+ - minor Makefile cleanup
+ - README cleanup
+ - prep for 1.0.3 release
+
+* Thu Dec 18 2003 Tim Hockin <thockin@sun.com>
+ - unblock signals before exec()ing a handler
+ - remove odd 'been_here' from signals_handled() (debug artifact?)
+
+* Mon Nov 24 2003 Tim Hockin <thockin@sun.com>
+ - add -c, --count option to acpi_listen (Luca Capello <luca.pca.it>)
+ - add -t, --time option to acpi_listen (Luca Capello <luca.pca.it>)
+ - return a meaningful value if we break out of the main loop (acpi_listen.c)
+ - break out usage() from handle_cmdline() (acpi_listen.c)
+
+* Mon Nov 17 2003 Tim Hockin <thockin@sun.com>
+ - Decouple logging to stdout from foregrounding
+ - Add acpi_listen (source and man)
+ - Add ud_connect()
+ - Remove (euid == 0) check
+ - ifdef the bad-kernel checking - it consumes a byte of data!
+
+* Fri Nov 14 2003 Tim Hockin <thockin@sun.com>
+ - Add -f option (run in foreground)
+
+* Tue May 13 2003 Tim Hockin <thockin@sun.com>
+ - Fixed a dumb bug with %e expansion for commands
+ - Add COPYING file
+ - Add TODO file
+
+* Fri Mar 15 2002 Tim Hockin <thockin@sun.com>
+ - Updated RPM spec with patch from sun for chkconfig on/off
+ - Add Changelog, make 'make rpm' use it.
+ - 1.0.1
+
+* Wed Mar 13 2002 Tim Hockin <thockin@sun.com>
+ - Fixed logging bug - not appending to log (O_APPEND needed)
+ - Fix 'make install' to not need root access
+ - Fix RPM spec to not need root
+
+* Thu Sep 6 2001 Tim Hockin <thockin@sun.com>
+ - 1.0.0
+
+* Thu Aug 16 2001 Tim Hockin <thockin@sun.com>
+ - Added commandline options to actions
+
+* Wed Aug 15 2001 Tim Hockin <thockin@sun.com>
+ - Added UNIX domain socket support
+ - Changed /etc/acpid.d to /etc/acpid/events
+
+* Mon Aug 13 2001 Tim Hockin <thockin@sun.com>
+ - added changelog
+ - 0.99.1-1
+
--- /dev/null
+# Makefile for ACPI daemon
+
+# update these numbers for new releases
+VERSION = 2.0.14
+
+OPT = -O2
+
+DESTDIR =
+PREFIX = /usr
+
+BINDIR = $(PREFIX)/bin
+SBINDIR = $(PREFIX)/sbin
+MANDIR = $(PREFIX)/share/man
+DOCDIR = $(PREFIX)/share/doc/acpid
+
+SBIN_PROGS = acpid
+BIN_PROGS = acpi_listen
+PROGS = $(SBIN_PROGS) $(BIN_PROGS)
+
+acpid_SRCS = acpid.c acpi_ids.c connection_list.c event.c input_layer.c \
+ inotify_handler.c libnetlink.c log.c netlink.c proc.c sock.c ud_socket.c
+acpid_OBJS = $(acpid_SRCS:.c=.o)
+
+acpi_listen_SRCS = acpi_listen.c log.c ud_socket.c
+acpi_listen_OBJS = $(acpi_listen_SRCS:.c=.o)
+
+all_SRCS = $(acpid_SRCS) $(acpi_listen_SRCS)
+
+MAN8 = acpid.8 acpi_listen.8
+MAN8GZ = $(MAN8:.8=.8.gz)
+
+DOCS = COPYING Changelog README TESTPLAN TODO
+
+CFLAGS = -W -Wall -Werror -Wundef -Wshadow -D_GNU_SOURCE $(OPT) \
+ -fno-strict-aliasing -g $(DEFS)
+DEFS = -DVERSION="\"$(VERSION)\""
+
+all: $(PROGS)
+
+acpid: $(acpid_OBJS)
+
+acpi_listen: $(acpi_listen_OBJS)
+
+man: $(MAN8)
+ for a in $^; do gzip -f -9 -c $$a > $$a.gz; done
+
+install_docs:
+ mkdir -p $(DESTDIR)/$(DOCDIR)
+ for a in $(DOCS); do install -m 0644 $$a $(DESTDIR)/$(DOCDIR) ; done
+ cp -a samples $(DESTDIR)/$(DOCDIR)
+
+install: $(PROGS) man install_docs
+ mkdir -p $(DESTDIR)/$(SBINDIR)
+ mkdir -p $(DESTDIR)/$(BINDIR)
+ install -m 0750 acpid $(DESTDIR)/$(SBINDIR)
+ install -m 0755 acpi_listen $(DESTDIR)/$(BINDIR)
+ mkdir -p $(DESTDIR)/$(MANDIR)/man8
+ install -m 0644 $(MAN8GZ) $(DESTDIR)/$(MANDIR)/man8
+# You might want to run mandb(8) after install in case your system uses it.
+
+DISTTMP=/tmp
+DISTNAME=acpid-$(VERSION)
+FULLTMP = $(DISTTMP)/$(DISTNAME)
+dist:
+ rm -rf $(FULLTMP)
+ mkdir -p $(FULLTMP)
+ cp -a * $(FULLTMP)
+ find $(FULLTMP) -type d -name CVS | xargs rm -rf
+ make -C $(FULLTMP) clean
+ make -C $(FULLTMP)/kacpimon clean
+ rm -f $(FULLTMP)/cscope.out
+ rm -f $(FULLTMP)/*anjuta*
+ find $(FULLTMP) -name '*~' | xargs rm -f
+ # Get rid of previous dist
+ rm -f $(FULLTMP)/$(DISTNAME).tar.gz
+ tar -C $(DISTTMP) -zcvf $(DISTNAME).tar.gz $(DISTNAME)
+ rm -rf $(FULLTMP)
+
+clean:
+ $(RM) $(PROGS) $(MAN8GZ) *.o .depend
+
+dep depend:
+ @$(RM) .depend
+ @$(MAKE) .depend
+
+.depend: $(all_SRCS)
+ @for f in $^; do \
+ OBJ=$$(echo $$f | sed 's/\.cp*$$/.o/'); \
+ $(CPP) $(PP_INCLUDES) -MM $$f -MT $$OBJ; \
+ done > $@
+
+# NOTE: 'sinclude' is "silent-include". This suppresses a warning if
+# .depend does not exist. Since Makefile includes this file, and this
+# file includes .depend, .depend is itself "a makefile" and Makefile is
+# dependent on it. Any makefile for which there is a rule (as above for
+# .depend) will be evaluated before anything else. If the rule executes
+# and the makefile is updated, make will reload the original Makefile and
+# start over.
+#
+# This means that the .depend rule will always be checked first. If
+# .depend gets rebuilt, then the dependencies we have already sincluded
+# must have been stale. Make starts over, the old dependencies are
+# tossed, and the new dependencies are sincluded.
+#
+# So why use 'sinclude' instead of 'include'? We want to ALWAYS make
+# Makefile depend on .depend, even if .depend doesn't exist yet. But we
+# don't want that pesky warning.
+sinclude .depend
--- /dev/null
+acpid for netlink
+
+This is Ted Felix's <http://www.tedfelix.com> branch of the acpid project
+which includes support for netlink and the input layer.
+
+/proc/acpi/event is a deprecated kernel interface for ACPI events. Newer
+kernels rely on netlink and the input layer to send ACPI-related events.
+This branch of acpid uses these new interfaces.
+
+Any comments or patches for this branch should be sent to Ted Felix:
+http://www.tedfelix.com
+
+***********************************************************
+* README for acpid *
+* *
+* Daemon for Advanced Configuration and Power Interface *
+* Tim Hockin *
+* <thockin@hockin.org> *
+* *
+***********************************************************
+
+Feb 13, 2004
+
+Overview
+--------
+
+ ACPID used to try to handle events internally. Rather than try to climb
+ an ever-growing mountain, ACPID now lets YOU define what events to handle.
+ Any event that publishes itself to /proc/acpi/event can be handled.
+
+ ACPID reads a set of configuration files which define event->action pairs.
+ This is how you make it do stuff. See the man page for details.
+
+Implementation status
+---------------------
+
+ This version should have complete support for generic handling of events.
+
+Requirements
+------------
+
+ For 2.6 and newer 2.4 kernels, ACPI seems to be fully integrated. That
+ should be all you need.
+
+ The ACPI kernel driver should be working on your system before trying
+ ACPID. Verify ACPI is loaded by verifying the existence of
+ /proc/acpi/event.
+
+Compiling ACPID
+---------------
+
+ type "make"
--- /dev/null
+acpid Test Plan
+
+Suggestions
+
+- Run all these tests with valgrind to detect memory leaks.
+- It's best to test without a window manager running (such as GNOME or KDE) as they tend to handle acpi events on their own and override acpid. To bring down X on a system that is configured with a graphical login, there's usually an "init" script you can run. As an example, with Debian/GNOME, log off of your X/GNOME session, switch to another tty (e.g. Alt-Ctrl-F1), login, and do this:
+ sudo /etc/init.d/gdm stop
+ It's different if you are using upstart:
+ sudo initctl stop gdm
+ And systemd requires a different incantation:
+ [anyone care to enlighten me?]
+ Now X is out of the way and you can test from the console.
+- You can kill acpid with "sudo killall acpid". Or if you are using upstart:
+ sudo initctl stop acpid
+ For systemd:
+ [anyone?]
+- To make testing more convenient, you can run acpid from a shell as "acpid -ld" to get maximum logging. Use Ctrl-C to stop acpid.
+
+Normal Paths
+
+* proc fs, all events
+Start acpid against /proc/acpi/event (if it exists).
+Test each of the following events:
+1. Power Button
+2. Laptop Lid Switch
+3. Sleep Button
+4. Laptop AC Adapter
+5. Laptop Battery
+
+* input layer/netlink, all events
+Start acpid against the input layer and netlink.
+Test each of the following events:
+1. Power Button (tests ACPI -> input layer)
+2. Laptop Lid Switch (tests ACPI -> input layer)
+3. Sleep Button (tests ACPI -> input layer)
+4. Laptop AC Adapter (tests ACPI -> netlink)
+5. Laptop Battery (tests ACPI -> netlink)
+6. Special Keyboard Key(s)
+
+* input layer/netlink fallback
+Start acpid with a bogus events file specified via the options.
+ acpid -e /proc/acpi/bogus
+Make sure a connection is made via the input layer and netlink.
+
+* lockfile procfs
+Start acpid against the proc fs
+Try some events and make sure they are coming through.
+Create the lockfile.
+Try some events and make sure they do not come through.
+Remove the lockfile.
+Try some events and make sure they are coming through.
+
+* lockfile netlink
+Start acpid against input layer and netlink.
+Try some events and make sure they are coming through.
+Create the lockfile.
+Try some events and make sure they do not come through.
+Remove the lockfile.
+Try some events and make sure they are coming through.
+
+* USB disconnect
+Start acpid (input layer and netlink) with a second USB keyboard attached.
+Try some events and make sure they are coming through.
+Disconnect the second USB keyboard.
+Make sure acpid is still running.
+Try some events and make sure they are coming through.
+
+* USB connect
+Start acpid against input layer and netlink.
+Try some events and make sure they are coming through.
+Connect a second USB keyboard.
+Check logging to see if acpid has found the new keyboard.
+Try some events from the second USB keyboard and make sure they are coming through.
+
+
+Debugging Paths
+
+* event logging
+Run acpid without the -l option and make sure no events are logged to syslog.
+Run acpid with the -l option and make sure events are logged to syslog.
+
+* debug mode
+Run acpid with the -d option and note that it runs in the foreground and provides debugging info to the console.
+acpid also supports up to 4 debug levels in the event handler. Might want to try "-dddd" and see what happens.
+
+* foreground mode
+Run acpid with the -f option and note that it runs in the foreground.
+Run acpid without the -f option and note that it runs in the background.
+
--- /dev/null
+Future Enhancements
+-------------------
+
+systemd support
+ - Support receiving unix sockets from systemd.
+ - Make daemonize() do what's right for systemd.
+ - Can we auto-detect systemd? Maybe the presence of the environment vars
+ for the unix sockets?
+
+Look into using libnl for netlink
+ - Can this be done?
+
+DBUS support
+Re-implement autoconf/automake support
+Re-add ancillary files needed for wider distribution
+Add a main config file
+ - support multiple event sources (such as named pipes)
+Allow socket-connected clients to filter incoming events
--- /dev/null
+#ifndef __ACPI_GENETLINK_H__
+#define __ACPI_GENETLINK_H__ 1
+
+#include <linux/types.h>
+
+struct acpi_genl_event {
+ char device_class[20];
+ char bus_id[15];
+ __u32 type;
+ __u32 data;
+};
+
+/* attributes of acpi_genl_family */
+enum {
+ ACPI_GENL_ATTR_UNSPEC,
+ ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */
+ __ACPI_GENL_ATTR_MAX,
+};
+#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
+
+/* commands supported by the acpi_genl_family */
+enum {
+ ACPI_GENL_CMD_UNSPEC,
+ ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ __ACPI_GENL_CMD_MAX,
+};
+#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
+#define GENL_MAX_FAM_OPS 256
+#define GENL_MAX_FAM_GRPS 256
+
+#define ACPI_EVENT_FAMILY_NAME "acpi_event"
+#define ACPI_EVENT_MCAST_GROUP_NAME "acpi_mc_group"
+
+#endif
--- /dev/null
+/*
+ * acpi_ids.c - ACPI Netlink Group and Family IDs
+ *
+ * Copyright (C) 2008 Ted Felix (www.tedfelix.com)
+ * Portions from acpi_genl Copyright (C) Zhang Rui <rui.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+/* needed by netlink.h, should be in there */
+#include <arpa/inet.h>
+#include <linux/types.h>
+#include <string.h>
+
+#include "genetlink.h"
+#include "libnetlink.h"
+
+#include "acpid.h"
+
+#define GENL_MAX_FAM_GRPS 256
+#define ACPI_EVENT_FAMILY_NAME "acpi_event"
+#define ACPI_EVENT_MCAST_GROUP_NAME "acpi_mc_group"
+
+static int initialized = 0;
+static __u16 acpi_event_family_id = 0;
+static __u32 acpi_event_mcast_group_id = 0;
+
+/*
+ * A CTRL_CMD_GETFAMILY message returns an attribute table that looks
+ * like this:
+ *
+ * CTRL_ATTR_FAMILY_ID Use this to make sure we get the proper msgs
+ * CTRL_ATTR_MCAST_GROUPS
+ * CTRL_ATTR_MCAST_GRP_NAME
+ * CTRL_ATTR_MCAST_GRP_ID Need this for the group mask
+ * ...
+ */
+
+static int
+get_ctrl_grp_id(struct rtattr *arg)
+{
+ struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+ char *name;
+
+ if (arg == NULL)
+ return -1;
+
+ /* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the */
+ /* group name and ID */
+ parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
+
+ /* if either of the entries needed cannot be found, bail */
+ if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
+ return -1;
+
+ /* get the name of this multicast group we've found */
+ name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+
+ /* if it does not match the ACPI event multicast group name, bail */
+ if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
+ return -1;
+
+ /* At this point, we've found what we were looking for. We now */
+ /* have the multicast group ID for ACPI events over generic netlink. */
+ acpi_event_mcast_group_id =
+ *((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
+
+ return 0;
+}
+
+/* n = the response to a CTRL_CMD_GETFAMILY message */
+static int
+genl_get_mcast_group_id(struct nlmsghdr *n)
+{
+ /*
+ * Attribute table. Note the type name "rtattr" which means "route
+ * attribute". This is a vestige of one of netlink's main uses:
+ * routing.
+ */
+ struct rtattr *tb[CTRL_ATTR_MAX + 1];
+ /* place for the generic netlink header in the incoming message */
+ struct genlmsghdr ghdr;
+ /* length of the attribute and payload */
+ int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+ /* Pointer to the attribute portion of the message */
+ struct rtattr *attrs;
+
+ if (len < 0) {
+ fprintf(stderr, "%s: netlink CTRL_CMD_GETFAMILY response, "
+ "wrong controller message len: %d\n", progname, len);
+ return -1;
+ }
+
+ if (n->nlmsg_type != GENL_ID_CTRL) {
+ fprintf(stderr, "%s: not a netlink controller message, "
+ "nlmsg_len=%d nlmsg_type=0x%x\n",
+ progname, n->nlmsg_len, n->nlmsg_type);
+ return 0;
+ }
+
+ /* copy generic netlink header into structure */
+ memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
+
+ if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
+ ghdr.cmd != CTRL_CMD_DELFAMILY &&
+ ghdr.cmd != CTRL_CMD_NEWFAMILY &&
+ ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
+ ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
+ fprintf(stderr, "%s: unknown netlink controller command %d\n",
+ progname, ghdr.cmd);
+ return 0;
+ }
+
+ /* set attrs to point to the attribute */
+ attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
+ /* Read the table from the message into "tb". This actually just */
+ /* places pointers into the message into tb[]. */
+ parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+ /* if a family ID attribute is present, get it */
+ if (tb[CTRL_ATTR_FAMILY_ID])
+ {
+ acpi_event_family_id =
+ *((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
+ }
+
+ /* if a "multicast groups" attribute is present... */
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
+ int i;
+
+ /* get the group table within this attribute */
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
+ tb[CTRL_ATTR_MCAST_GROUPS]);
+
+ /* for each group */
+ for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
+ /* if this group is valid */
+ if (tb2[i])
+ /* Parse the ID. If successful, we're done. */
+ if (!get_ctrl_grp_id(tb2[i]))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+genl_get_ids(char *family_name)
+{
+ /* handle to the netlink connection */
+ struct rtnl_handle rth;
+ /* holds the request we are going to send and the reply */
+ struct {
+ struct nlmsghdr n;
+ char buf[4096]; /* ??? Is this big enough for all cases? */
+ } req;
+ /* pointer to the nlmsghdr in req */
+ struct nlmsghdr *nlh;
+ /* place for the generic netlink header before copied into req */
+ struct genlmsghdr ghdr;
+ /* return value */
+ int ret = -1;
+
+ /* clear out the request */
+ memset(&req, 0, sizeof(req));
+
+ /* set up nlh to point to the netlink header in req */
+ nlh = &req.n;
+ /* set up the netlink header */
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = GENL_ID_CTRL;
+
+ /* clear out the generic netlink message header */
+ memset(&ghdr, 0, sizeof(struct genlmsghdr));
+ /* set the command we want to run: "GETFAMILY" */
+ ghdr.cmd = CTRL_CMD_GETFAMILY;
+ /* copy it into req */
+ memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
+
+ /* the message payload is the family name */
+ addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
+ family_name, strlen(family_name) + 1);
+
+ /* open a generic netlink connection */
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
+ fprintf(stderr, "%s: cannot open generic netlink socket\n",
+ progname);
+ return -1;
+ }
+
+ /*
+ * Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
+ * netlink controller. Reply will be in nlh upon return.
+ */
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+ fprintf(stderr, "%s: error talking to the kernel via netlink\n",
+ progname);
+ goto ctrl_done;
+ }
+
+ /* process the response */
+ if (genl_get_mcast_group_id(nlh) < 0) {
+ fprintf(stderr, "%s: failed to get acpi_event netlink "
+ "multicast group\n", progname);
+ goto ctrl_done;
+ }
+
+ ret = 0;
+
+ctrl_done:
+ rtnl_close(&rth);
+ return ret;
+}
+
+/* initialize the ACPI IDs */
+static void
+acpi_ids_init()
+{
+ genl_get_ids(ACPI_EVENT_FAMILY_NAME);
+
+ initialized = 1;
+}
+
+/* returns the netlink family ID for ACPI event messages */
+__u16
+acpi_ids_getfamily()
+{
+ /* if the IDs haven't been initialized, initialize them */
+ if (initialized == 0)
+ acpi_ids_init();
+
+ return acpi_event_family_id;
+}
+
+/* returns the netlink multicast group ID for ACPI event messages */
+__u32
+acpi_ids_getgroup()
+{
+ /* if the IDs haven't been initialized, initialize them */
+ if (initialized == 0)
+ acpi_ids_init();
+
+ return acpi_event_mcast_group_id;
+}
--- /dev/null
+/*
+ * acpi_ids.h - ACPI Netlink Group and Family IDs
+ *
+ * Copyright (C) 2008 Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ */
+
+#ifndef ACPI_IDS_H__
+#define ACPI_IDS_H__
+
+/* returns the netlink family ID for ACPI event messages */
+extern __u16 acpi_ids_getfamily();
+
+/* returns the netlink multicast group ID for ACPI event messages */
+extern __u32 acpi_ids_getgroup();
+
+#endif /* ACPI_IDS_H__ */
--- /dev/null
+.TH acpi_listen 8 "Nov 2003"
+.\" Portions Copyright (c) 2003 Sun Microsystems
+.\" Copyright (c) 2004 Tim Hockin (thockin@hockin.org)
+.\" Some parts (C) 2003 - Gismo / Luca Capello <luca.pca.it> http://luca.pca.it
+.SH NAME
+acpi_listen \- ACPI event listener
+.SH SYNOPSIS
+\fBacpi_listen\fP [\fIoptions\fP]
+
+.SH DESCRIPTION
+\fBacpid\fP is the system-wide ACPI event catcher. \fBacpi_listen\fP is a
+simple shell-friendly tool which connects to acpid and listens for events.
+When an event occurs, acpi_listen will print it on stdout.
+
+.SH OPTIONS
+.TP
+.BI \-c "\fR, \fP" \--count " events"
+Receive up to a maximum number of ACPI events, then exit.
+.TP
+.BI \-s "\fR, \fP" \--socketfile " filename"
+This option changes the name of the UNIX domain socket which \fBacpid\fP opens.
+Default is \fI/var/run/acpid.socket\fP.
+.TP
+.BI \-t "\fR, \fP" \--time " seconds"
+Listen for the specified time in seconds, before exiting.
+.TP
+.BI \-v "\fR, \fP" \--version
+Print version information and exit.
+.TP
+.BI \-h "\fR, \fP" \--help
+Show help and exit.
+
+.SH FILES
+.PD 0
+.B /var/run/acpid.socket
+.PD
+
+.SH BUGS
+There are no known bugs. To file bug reports, see \fBAUTHORS\fP below.
+.SH SEE ALSO
+regcomp(3), sh(1), socket(2), connect(2)
+.SH AUTHORS
+Tim Hockin <thockin@hockin.org>
+.br
+Luca Capello <luca@pca.it>
+.br
+
--- /dev/null
+/*
+ * acpi_listen.c - ACPI client for acpid's UNIX socket
+ *
+ * Portions Copyright (C) 2003 Sun Microsystems (thockin@sun.com)
+ * Some parts (C) 2003 - Gismo / Luca Capello <luca.pca.it> http://luca.pca.it
+ * Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <time.h>
+#include <sys/poll.h>
+#include <grp.h>
+#include <signal.h>
+
+#include "acpid.h"
+#include "ud_socket.h"
+
+static int handle_cmdline(int *argc, char ***argv);
+static char *read_line(int fd);
+
+const char *progname;
+const char *socketfile = ACPID_SOCKETFILE;
+static int max_events;
+
+static void
+time_expired(int signum __attribute__((unused)))
+{
+ exit(EXIT_SUCCESS);
+}
+
+int
+main(int argc, char **argv)
+{
+ int sock_fd;
+ int ret;
+
+ /* handle an alarm */
+ signal(SIGALRM, time_expired);
+
+ /* learn who we really are */
+ progname = (const char *)strrchr(argv[0], '/');
+ progname = progname ? (progname + 1) : argv[0];
+
+ /* handle the commandline */
+ handle_cmdline(&argc, &argv);
+
+ /* open the socket */
+ sock_fd = ud_connect(socketfile);
+ if (sock_fd < 0) {
+ fprintf(stderr, "%s: can't open socket %s: %s\n",
+ progname, socketfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ fcntl(sock_fd, F_SETFD, FD_CLOEXEC);
+
+ /* set stdout to be line buffered */
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ /* main loop */
+ ret = 0;
+ while (1) {
+ char *event;
+
+ /* read and handle an event */
+ event = read_line(sock_fd);
+ if (event) {
+ fprintf(stdout, "%s\n", event);
+ } else if (errno == EPIPE) {
+ fprintf(stderr, "connection closed\n");
+ break;
+ } else {
+ static int nerrs;
+ if (++nerrs >= ACPID_MAX_ERRS) {
+ fprintf(stderr, "too many errors - aborting\n");
+ ret = 1;
+ break;
+ }
+ }
+
+ if (max_events > 0 && --max_events == 0) {
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct option opts[] = {
+ {"count", 0, 0, 'c'},
+ {"socketfile", 1, 0, 's'},
+ {"time", 0, 0, 't'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, 'h'},
+ {NULL, 0, 0, 0},
+};
+static const char *opts_help[] = {
+ "Set the maximum number of events.", /* count */
+ "Use the specified socket file.", /* socketfile */
+ "Listen for the specified time (in seconds).",/* time */
+ "Print version information.", /* version */
+ "Print this message.", /* help */
+};
+
+static void
+usage(FILE *fp)
+{
+ struct option *opt;
+ const char **hlp;
+ int max, size;
+
+ fprintf(fp, "Usage: %s [OPTIONS]\n", progname);
+ max = 0;
+ for (opt = opts; opt->name; opt++) {
+ size = strlen(opt->name);
+ if (size > max)
+ max = size;
+ }
+ for (opt = opts, hlp = opts_help; opt->name; opt++, hlp++) {
+ fprintf(fp, " -%c, --%s", opt->val, opt->name);
+ size = strlen(opt->name);
+ for (; size < max; size++)
+ fprintf(fp, " ");
+ fprintf(fp, " %s\n", *hlp);
+ }
+}
+
+/*
+ * Parse command line arguments
+ */
+static int
+handle_cmdline(int *argc, char ***argv)
+{
+ for (;;) {
+ int i;
+ i = getopt_long(*argc, *argv, "c:s:t:vh", opts, NULL);
+ if (i == -1) {
+ break;
+ }
+ switch (i) {
+ case 'c':
+ if (!isdigit(optarg[0])) {
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ max_events = atoi(optarg);
+ break;
+ case 's':
+ socketfile = optarg;
+ break;
+ case 't':
+ if (!isdigit(optarg[0])) {
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ alarm(atoi(optarg));
+ break;
+ case 'v':
+ printf(PACKAGE "-" VERSION "\n");
+ exit(EXIT_SUCCESS);
+ case 'h':
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ default:
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ *argc -= optind;
+ *argv += optind;
+
+ return 0;
+}
+
+#define MAX_BUFLEN 1024
+static char *
+read_line(int fd)
+{
+ static char *buf;
+ int buflen = 64;
+ int i = 0;
+ int r;
+ int searching = 1;
+
+ while (searching) {
+ buf = realloc(buf, buflen);
+ if (!buf) {
+ fprintf(stderr, "ERR: malloc(%d): %s\n",
+ buflen, strerror(errno));
+ return NULL;
+ }
+ memset(buf+i, 0, buflen-i);
+
+ while (i < buflen) {
+ r = read(fd, buf+i, 1);
+ if (r < 0 && errno != EINTR) {
+ /* we should do something with the data */
+ fprintf(stderr, "ERR: read(): %s\n",
+ strerror(errno));
+ return NULL;
+ } else if (r == 0) {
+ /* signal this in an almost standard way */
+ errno = EPIPE;
+ return NULL;
+ } else if (r == 1) {
+ /* scan for a newline */
+ if (buf[i] == '\n') {
+ searching = 0;
+ buf[i] = '\0';
+ break;
+ }
+ i++;
+ }
+ }
+ if (buflen >= MAX_BUFLEN) {
+ break;
+ }
+ buflen *= 2;
+ }
+
+ return buf;
+}
--- /dev/null
+.TH acpid 8 ""
+.\" Portions Copyright (c) 2001 Sun Microsystems
+.\" Portions Copyright (c) Tim Hockin (thockin@hockin.org)
+.SH NAME
+acpid \- Advanced Configuration and Power Interface event daemon
+.SH SYNOPSIS
+\fBacpid\fP [\fIoptions\fP]
+
+.SH DESCRIPTION
+\fBacpid\fP is designed to notify user-space programs of ACPI events.
+\fBacpid\fP should be started during the system boot, and will run as a
+background process, by default. It will open an events file
+(\fI/proc/acpi/event\fP by default) and attempt to read whole lines which
+represent ACPI events. If the events file does not exist, \fBacpid\fP will
+attempt to connect to the Linux kernel via the input layer and netlink. When an
+ACPI event is received from one of these sources, \fBacpid\fP will examine a
+list of rules, and execute the rules that match the event. \fBacpid\fP will
+ignore all incoming ACPI events if a lock file exists (\fI/var/lock/acpid\fP by
+default).
+.PP
+\fIRules\fP are defined by simple configuration files. \fBacpid\fP
+will look in a configuration directory (\fI/etc/acpi/events\fP by default),
+and parse all regular files with names that consist entirely of upper and
+lower case letters, digits, underscores, and hyphens (similar to
+run-parts(8)).
+.\" that do not begin with a period ('.') or end with a tilde (~).
+Each file must define two things: an \fIevent\fP and an
+\fIaction\fP. Any blank lines, or lines where the first character is a
+hash ('#') are ignored. Extraneous lines are flagged as warnings, but
+are not fatal. Each line has three tokens: the key, a literal equal sign,
+and the value. The key can be up to 63 characters, and is case-insensitive
+(but whitespace matters). The value can be up to 511 characters, and is
+case and whitespace sensitive.
+.PP
+The event value is a regular expression (see regcomp(3)), against which events are matched.
+.PP
+The action value is a commandline, which will be invoked via \fI/bin/sh\fP
+whenever an event matching the rule in question occurs. The commandline may
+include shell-special characters, and they will be preserved. The only special
+characters in an action value are "%" escaped. The string "%e" will be
+replaced by the literal text of the event for which the action was invoked.
+This string may contain spaces, so the commandline must take care to quote the "%e" if it wants a single token. The string "%%" will be replaced by a
+literal "%". All other "%" escapes are reserved, and will cause a rule to
+not load.
+.PP
+This feature allows multiple rules to be defined for the same event (though no
+ordering is guaranteed), as well as one rule to be defined for multiple events.
+To force \fBacpid\fP to reload the rule configuration, send it a SIGHUP.
+.PP
+In addition to rule files, \fBacpid\fP also accepts connections on a UNIX
+domain socket (\fI/var/run/acpid.socket\fP by default). Any application may
+connect to this socket. Once connected, \fBacpid\fP will send the text of
+all ACPI events to the client. The client has the responsibility of filtering
+for messages about which it cares. \fBacpid\fP will not close the client
+socket except in the case of a SIGHUP or \fBacpid\fP exiting.
+.PP
+For faster startup, this socket can be passed in as stdin so that \fBacpid\fP
+need not create the socket. In addition, if a socket is passed in as stdin,
+\fBacpid\fP will not daemonize. It will be run in foreground. This behavior
+is provided to support systemd(1).
+.PP
+.B acpid
+will log all of its activities, as well as the stdout and stderr of any
+actions, to syslog.
+.PP
+All the default files and directories can be changed with commandline options.
+.SH OPTIONS
+.TP 12
+.BI \-c "\fR, \fP" \-\-confdir " directory"
+This option changes the directory in which \fBacpid\fP looks for rule
+configuration files. Default is \fI/etc/acpi/events\fP.
+.TP 12
+.BI \-C "\fR, \fP" \-\-clientmax " number"
+This option changes the maximum number of non-root socket connections which
+can be made to the \fBacpid\fP socket. Default is \fI256\fP.
+.TP 12
+.BI \-d "\fR, \fP" \-\-debug
+This option increases the \fBacpid\fP debug level by one. If the debug level
+is non-zero, \fBacpid\fP will run in the foreground, and will log to
+stderr, in addition to the regular syslog.
+.TP
+.BI \-e "\fR, \fP" \-\-eventfile " filename"
+This option changes the event file from which \fBacpid\fP reads events.
+Default is \fI/proc/acpi/event\fP.
+.TP
+.BI \-n "\fR, \fP" \-\-netlink
+This option forces \fBacpid\fP to use the Linux kernel input layer and netlink interface for ACPI events.
+.TP
+.BI \-f "\fR, \fP" \-\-foreground
+This option keeps \fBacpid\fP in the foreground by not forking at startup.
+.TP
+.BI \-l "\fR, \fP" \-\-logevents
+This option tells \fBacpid\fP to log information about all events and actions.
+.TP
+.BI \-L "\fR, \fP" \-\-lockfile " filename"
+This option changes the lock file used to stop event processing.
+Default is \fI/var/lock/acpid\fP.
+.TP
+.BI \-g "\fR, \fP" \-\-socketgroup " groupname"
+This option changes the group ownership of the UNIX domain socket to which
+\fBacpid\fP publishes events.
+.TP
+.BI \-m "\fR, \fP" \-\-socketmode " mode"
+This option changes the permissions of the UNIX domain socket to which
+\fBacpid\fP publishes events. Default is \fI0666\fP.
+.TP
+.BI \-s "\fR, \fP" \-\-socketfile " filename"
+This option changes the name of the UNIX domain socket which \fBacpid\fP opens.
+Default is \fI/var/run/acpid.socket\fP.
+.TP
+.BI \-S "\fR, \fP" \-\-nosocket " filename"
+This option tells \fBacpid\fP not to open a UNIX domain socket. This
+overrides the \fI-s\fP option, and negates all other socket options.
+.TP
+.BI \-p "\fR, \fP" \-\-pidfile " filename"
+This option tells \fBacpid\fP to use the specified file as its pidfile. If
+the file exists, it will be removed and over-written.
+Default is \fI/var/run/acpid.pid\fP.
+.TP
+.BI \-v "\fR, \fP" \-\-version
+Print version information and exit.
+.TP
+.BI \-h "\fR, \fP" \-\-help
+Show help and exit.
+.SH EXAMPLE
+This example will shut down your system if you press the power button.
+.PP
+Create a file named /etc/acpi/events/power that contains the following:
+.IP
+.br
+event=button/power
+.br
+action=/etc/acpi/power.sh "%e"
+.PP
+Then create a file named /etc/acpi/power.sh that contains the following:
+.IP
+/sbin/shutdown \-h now "Power button pressed"
+.PP
+Now, when \fBacpid\fP is running, a press of the power button will cause the
+rule in /etc/acpi/events/power to trigger the script in /etc/acpi/power.sh.
+The script will then shut down the system.
+.SH DEPENDENCIES
+\fBacpid\fP should work on any linux kernel released since 2003.
+.SH FILES
+.PD 0
+.B /proc/acpi/event
+.br
+.B /dev/input/event*
+.br
+.B /etc/acpi/
+.br
+.B /var/run/acpid.socket
+.br
+.B /var/run/acpid.pid
+.br
+.B /var/lock/acpid
+.br
+.PD
+.SH BUGS
+There are no known bugs. To file bug reports, see \fBAUTHORS\fP below.
+.SH SEE ALSO
+regcomp(3), sh(1), socket(2), connect(2), systemd(1), acpi_listen(8),
+kacpimon(8)
+.SH AUTHORS
+Ted Felix (www.tedfelix.com)
+.br
+Tim Hockin <thockin@hockin.org>
+.br
+Andrew Henroid
+
--- /dev/null
+/*
+ * acpid.c - ACPI daemon
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <getopt.h>
+
+#include "acpid.h"
+#include "log.h"
+#include "event.h"
+#include "connection_list.h"
+#include "proc.h"
+#include "sock.h"
+#include "input_layer.h"
+#include "inotify_handler.h"
+#include "netlink.h"
+
+static int handle_cmdline(int *argc, char ***argv);
+static void close_fds(void);
+static int daemonize(void);
+static void open_log(void);
+static int std2null(void);
+static int create_pidfile(void);
+static void clean_exit(int sig);
+static void reload_conf(int sig);
+
+/* global debug level */
+int acpid_debug;
+
+/* do we log event info? */
+int logevents;
+
+const char *progname;
+static const char *confdir = ACPID_CONFDIR;
+static const char *lockfile = ACPID_LOCKFILE;
+static int nosocket;
+static int foreground;
+static const char *pidfile = ACPID_PIDFILE;
+static int netlink;
+
+int
+main(int argc, char **argv)
+{
+ /* learn who we really are */
+ progname = (const char *)strrchr(argv[0], '/');
+ progname = progname ? (progname + 1) : argv[0];
+
+ /* handle the commandline */
+ handle_cmdline(&argc, &argv);
+
+ /* close any extra file descriptors */
+ close_fds();
+
+ /* open the log */
+ open_log();
+
+ if (!netlink) {
+ /* open the acpi event file in the proc fs */
+ /* if the open fails, try netlink */
+ if (open_proc())
+ netlink = 1;
+ }
+
+ if (netlink) {
+ /* open the input layer */
+ open_input();
+
+ /* watch for new input layer devices */
+ open_inotify();
+
+ /* open netlink */
+ open_netlink();
+ }
+
+ /* open our socket */
+ if (!nosocket) {
+ open_sock();
+ }
+
+ /* if we're running in the background, and we're not being started */
+ /* by systemd */
+ if (!foreground && !is_socket(STDIN_FILENO)) {
+ if (daemonize() < 0)
+ exit(EXIT_FAILURE);
+ }
+
+ /* redirect standard files to /dev/null */
+ if (std2null() < 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ acpid_log(LOG_INFO, "starting up with %s",
+ netlink ? "netlink and the input layer" : "proc fs");
+
+ /* trap key signals */
+ signal(SIGHUP, reload_conf);
+ signal(SIGINT, clean_exit);
+ signal(SIGQUIT, clean_exit);
+ signal(SIGTERM, clean_exit);
+ signal(SIGPIPE, SIG_IGN);
+
+ /* read in our configuration */
+ if (acpid_read_conf(confdir)) {
+ exit(EXIT_FAILURE);
+ }
+
+ /* create our pidfile */
+ if (create_pidfile() < 0) {
+ exit(EXIT_FAILURE);
+ }
+
+ acpid_log(LOG_INFO, "waiting for events: event logging is %s",
+ logevents ? "on" : "off");
+
+ /* main loop */
+ while (1) {
+ fd_set readfds;
+ int nready;
+ int i;
+ struct connection *p;
+
+ /* it's going to get clobbered, so use a copy */
+ readfds = *get_fdset();
+
+ /* wait on data */
+ nready = select(get_highestfd() + 1, &readfds, NULL, NULL, NULL);
+
+ if (nready < 0 && errno == EINTR) {
+ continue;
+ } else if (nready < 0) {
+ acpid_log(LOG_ERR, "select(): %s", strerror(errno));
+ continue;
+ }
+
+ /* house keeping */
+ acpid_close_dead_clients();
+
+ /* for each connection */
+ for (i = 0; i <= get_number_of_connections(); ++i) {
+ int fd;
+
+ p = get_connection(i);
+
+ /* if this connection is invalid, bail */
+ if (!p)
+ break;
+
+ /* get the file descriptor */
+ fd = p->fd;
+
+ /* if this file descriptor has data waiting */
+ if (FD_ISSET(fd, &readfds)) {
+ /* delegate to this connection's process function */
+ p->process(fd);
+ }
+ }
+ }
+
+ clean_exit_with_status(EXIT_SUCCESS);
+
+ return 0;
+}
+
+/*
+ * Parse command line arguments
+ */
+static int
+handle_cmdline(int *argc, char ***argv)
+{
+ struct option opts[] = {
+ {"confdir", 1, 0, 'c'},
+ {"clientmax", 1, 0, 'C'},
+ {"debug", 0, 0, 'd'},
+ {"eventfile", 1, 0, 'e'},
+ {"foreground", 0, 0, 'f'},
+ {"logevents", 0, 0, 'l'},
+ {"socketgroup", 1, 0, 'g'},
+ {"socketmode", 1, 0, 'm'},
+ {"socketfile", 1, 0, 's'},
+ {"nosocket", 1, 0, 'S'},
+ {"pidfile", 1, 0, 'p'},
+ {"lockfile", 1, 0, 'L'},
+ {"netlink", 0, 0, 'n'},
+ {"version", 0, 0, 'v'},
+ {"help", 0, 0, 'h'},
+ {NULL, 0, 0, 0},
+ };
+ const char *opts_help[] = {
+ "Set the configuration directory.", /* confdir */
+ "Set the limit on non-root socket connections.",/* clientmax */
+ "Increase debugging level (implies -f).",/* debug */
+ "Use the specified file for events.", /* eventfile */
+ "Run in the foreground.", /* foreground */
+ "Log all event activity.", /* logevents */
+ "Set the group on the socket file.", /* socketgroup */
+ "Set the permissions on the socket file.",/* socketmode */
+ "Use the specified socket file.", /* socketfile */
+ "Do not listen on a UNIX socket (overrides -s).",/* nosocket */
+ "Use the specified PID file.", /* pidfile */
+ "Use the specified lockfile to stop processing.", /* lockfile */
+ "Force netlink/input layer mode. (overrides -e)", /* netlink */
+ "Print version information.", /* version */
+ "Print this message.", /* help */
+ };
+ struct option *opt;
+ const char **hlp;
+ int max, size;
+
+ for (;;) {
+ int i;
+ i = getopt_long(*argc, *argv,
+ "c:C:de:flg:m:s:Sp:L:nvh", opts, NULL);
+ if (i == -1) {
+ break;
+ }
+ switch (i) {
+ case 'c':
+ confdir = optarg;
+ break;
+ case 'C':
+ clientmax = strtol(optarg, NULL, 0);
+ break;
+ case 'd':
+ foreground = 1;
+ acpid_debug++;
+ log_debug_to_stderr = 1;
+ break;
+ case 'e':
+ eventfile = optarg;
+ break;
+ case 'f':
+ foreground = 1;
+ break;
+ case 'l':
+ logevents = 1;
+ break;
+ case 'g':
+ socketgroup = optarg;
+ break;
+ case 'm':
+ socketmode = strtol(optarg, NULL, 8);
+ break;
+ case 's':
+ socketfile = optarg;
+ break;
+ case 'S':
+ nosocket = 1;
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 'L':
+ lockfile = optarg;
+ break;
+ case 'n':
+ netlink = 1;
+ break;
+ case 'v':
+ printf(PACKAGE "-" VERSION "\n");
+ exit(EXIT_SUCCESS);
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [OPTIONS]\n", progname);
+ max = 0;
+ for (opt = opts; opt->name; opt++) {
+ size = strlen(opt->name);
+ if (size > max)
+ max = size;
+ }
+ for (opt = opts, hlp = opts_help;
+ opt->name;
+ opt++, hlp++) {
+ fprintf(stderr, " -%c, --%s",
+ opt->val, opt->name);
+ size = strlen(opt->name);
+ for (; size < max; size++)
+ fprintf(stderr, " ");
+ fprintf(stderr, " %s\n", *hlp);
+ }
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ *argc -= optind;
+ *argv += optind;
+
+ return 0;
+}
+
+static void
+close_fds(void)
+{
+ int fd, max;
+ max = sysconf(_SC_OPEN_MAX);
+ for (fd = 3; fd < max; fd++)
+ close(fd);
+}
+
+static int
+daemonize(void)
+{
+ pid_t pid, sid;
+
+ /* fork off the parent process */
+ pid = fork();
+ if (pid < 0) {
+ acpid_log(LOG_ERR, "fork: %s", strerror(errno));
+ return -1;
+ }
+ /* if we got a good PID, then we can exit the parent process */
+ if (pid > 0) {
+ exit(EXIT_SUCCESS);
+ }
+
+ /* at this point we are executing as the child process */
+
+ /* change the umask to something predictable instead of inheriting */
+ /* whatever from the parent */
+ umask(0);
+
+ /* create a new SID for the child process and */
+ /* detach the process from the parent (normally a shell) */
+ sid = setsid();
+ if (sid < 0) {
+ acpid_log(LOG_ERR, "setsid: %s", strerror(errno));
+ return -1;
+ }
+
+ /* Change the current working directory. This prevents the current
+ directory from being locked; hence not being able to remove it. */
+ if (chdir("/") < 0) {
+ acpid_log(LOG_ERR, "chdir(\"/\"): %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+open_log(void)
+{
+ int log_opts;
+
+ /* open the syslog */
+ log_opts = LOG_CONS|LOG_NDELAY;
+ if (acpid_debug) {
+ log_opts |= LOG_PERROR;
+ }
+ openlog(PACKAGE, log_opts, LOG_DAEMON);
+}
+
+static int
+std2null(void)
+{
+ int nullfd;
+
+ /* open /dev/null */
+ nullfd = open("/dev/null", O_RDWR);
+ if (nullfd < 0) {
+ acpid_log(LOG_ERR, "can't open /dev/null: %s", strerror(errno));
+ return -1;
+ }
+
+ /* set up stdin, stdout, stderr to /dev/null */
+
+ /* don't redirect stdin if we're being sent a socket by systemd */
+ if (!is_socket(STDIN_FILENO) &&
+ dup2(nullfd, STDIN_FILENO) != STDIN_FILENO) {
+ acpid_log(LOG_ERR, "dup2() stdin: %s", strerror(errno));
+ return -1;
+ }
+ if (!acpid_debug && dup2(nullfd, STDOUT_FILENO) != STDOUT_FILENO) {
+ acpid_log(LOG_ERR, "dup2() stdout: %s", strerror(errno));
+ return -1;
+ }
+ if (!acpid_debug && dup2(nullfd, STDERR_FILENO) != STDERR_FILENO) {
+ acpid_log(LOG_ERR, "dup2() stderr: %s", strerror(errno));
+ return -1;
+ }
+
+ close(nullfd);
+
+ return 0;
+}
+
+static int
+create_pidfile(void)
+{
+ int fd;
+
+ /* JIC */
+ unlink(pidfile);
+
+ /* open the pidfile */
+ fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd >= 0) {
+ FILE *f;
+
+ /* write our pid to it */
+ f = fdopen(fd, "w");
+ if (f != NULL) {
+ fprintf(f, "%d\n", getpid());
+ fclose(f);
+ /* leave the fd open */
+ return 0;
+ }
+ close(fd);
+ }
+
+ /* something went wrong */
+ acpid_log(LOG_ERR, "can't create pidfile %s: %s",
+ pidfile, strerror(errno));
+ return -1;
+}
+
+void
+clean_exit_with_status(int status)
+{
+ acpid_cleanup_rules(1);
+ acpid_log(LOG_NOTICE, "exiting");
+ unlink(pidfile);
+ exit(status);
+}
+
+static void
+clean_exit(int sig __attribute__((unused)))
+{
+ clean_exit_with_status(EXIT_SUCCESS);
+}
+
+static void
+reload_conf(int sig __attribute__((unused)))
+{
+ acpid_log(LOG_NOTICE, "reloading configuration");
+ acpid_cleanup_rules(0);
+ acpid_read_conf(confdir);
+}
+
+int
+locked()
+{
+ struct stat trash;
+
+ /* check for existence of a lockfile */
+ return (stat(lockfile, &trash) == 0);
+}
+
--- /dev/null
+/*
+ * acpid.h - ACPI daemon
+ *
+ * Copyright (C) 1999-2000 Andrew Henroid
+ * Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * 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
+ */
+
+#ifndef ACPID_H__
+#define ACPID_H__
+
+#define ACPI_PROCDIR "/proc/acpi"
+#define ACPID_EVENTFILE ACPI_PROCDIR "/event"
+#define ACPID_CONFDIR "/etc/acpi/events"
+#define ACPID_SOCKETFILE "/var/run/acpid.socket"
+#define ACPID_SOCKETMODE 0666
+#define ACPID_CLIENTMAX 256
+#define ACPID_PIDFILE "/var/run/acpid.pid"
+#define ACPID_LOCKFILE "/var/lock/acpid"
+#define ACPID_MAX_ERRS 5
+
+/* ??? make these changeable by commandline option? */
+#define ACPID_INPUTLAYERDIR "/dev/input"
+#define ACPID_INPUTLAYERFILES ACPID_INPUTLAYERDIR "/event*"
+
+#define PACKAGE "acpid"
+
+/*
+ * acpid.c
+ */
+extern int acpid_debug;
+extern int logevents;
+extern const char *progname;
+
+extern int locked();
+
+extern void clean_exit_with_status(int status);
+
+#endif /* ACPID_H__ */
--- /dev/null
+/*
+ * connection_list.c - ACPI daemon connection list
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * Tabs at 4
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "acpid.h"
+#include "log.h"
+
+#include "connection_list.h"
+
+#define max(a, b) (((a)>(b))?(a):(b))
+
+/*---------------------------------------------------------------*/
+/* private objects */
+
+#define MAX_CONNECTIONS 20
+
+static struct connection connection_list[MAX_CONNECTIONS];
+
+static int nconnections = 0;
+
+/* fd_set containing all the fd's that come in */
+static fd_set allfds;
+
+/* highest fd that is opened */
+/* (-2 + 1) causes select() to return immediately */
+static int highestfd = -2;
+
+/*---------------------------------------------------------------*/
+/* public functions */
+
+void
+add_connection(struct connection *p)
+{
+ if (nconnections < 0)
+ return;
+ if (nconnections >= MAX_CONNECTIONS) {
+ acpid_log(LOG_ERR, "Too many connections.");
+ /* ??? This routine should return -1 in this situation so that */
+ /* callers can clean up any open fds and whatnot. */
+ return;
+ }
+
+ if (nconnections == 0)
+ FD_ZERO(&allfds);
+
+ /* add the connection to the connection list */
+ connection_list[nconnections] = *p;
+ ++nconnections;
+
+ /* add to the fd set */
+ FD_SET(p->fd, &allfds);
+ highestfd = max(highestfd, p->fd);
+}
+
+/*---------------------------------------------------------------*/
+
+void
+delete_connection(int fd)
+{
+ int i;
+
+ close(fd);
+
+ /* remove from the fd set */
+ FD_CLR(fd, &allfds);
+
+ for (i = 0; i < nconnections; ++i) {
+ /* if the file descriptors match, delete the connection */
+ if (connection_list[i].fd == fd) {
+ free(connection_list[i].pathname);
+
+ --nconnections;
+ connection_list[i] = connection_list[nconnections];
+
+ break;
+ }
+ }
+
+ /* prepare for recalculation of highestfd */
+ highestfd = -2;
+
+ /* recalculate highestfd */
+ for (i = 0; i < nconnections; ++i) {
+ highestfd = max(highestfd, connection_list[i].fd);
+ }
+}
+
+/*---------------------------------------------------------------*/
+
+struct connection *
+find_connection(int fd)
+{
+ int i;
+
+ /* for each connection */
+ for (i = 0; i < nconnections; ++i) {
+ /* if the file descriptors match, return the connection */
+ if (connection_list[i].fd == fd)
+ return &connection_list[i];
+ }
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------*/
+
+struct connection *
+find_connection_name(char *pathname)
+{
+ int i;
+
+ /* for each connection */
+ for (i = 0; i < nconnections; ++i) {
+ /* skip null pathnames */
+ if (connection_list[i].pathname == NULL)
+ continue;
+
+ /* if the pathname matches, return the connection */
+ if (strcmp(connection_list[i].pathname, pathname) == 0)
+ return &connection_list[i];
+ }
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------*/
+
+int
+get_number_of_connections()
+{
+ return nconnections;
+}
+
+/*---------------------------------------------------------------*/
+
+struct connection *
+get_connection(int i)
+{
+ if (i < 0 || i >= nconnections)
+ return NULL;
+
+ return &connection_list[i];
+}
+
+/*---------------------------------------------------------------*/
+
+const fd_set *
+get_fdset()
+{
+ return &allfds;
+}
+
+/*---------------------------------------------------------------*/
+
+int
+get_highestfd()
+{
+ return highestfd;
+}
--- /dev/null
+/*
+ * connection_list.h - ACPI daemon connection list
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * Tabs at 4
+ */
+
+#ifndef CONNECTION_LIST_H__
+#define CONNECTION_LIST_H__
+
+#include <sys/select.h>
+
+/*****************************************************************
+ * Connection List Public Members
+ *****************************************************************/
+
+struct connection
+{
+ /* file descriptor */
+ int fd;
+
+ /* process incoming data on the connection */
+ /* ??? suggest passing a pointer to this connection struct */
+ void (* process)(int fd);
+
+ /* Optional. Used by find_connection_name() to find the connection for a
+ specific file. Set to NULL if not specified. Memory will be freed
+ with free() when connection is deleted. */
+ char *pathname;
+
+ /* 0 indicates this is probably not a keyboard device */
+ int kybd;
+};
+
+/* add a connection to the list */
+extern void add_connection(struct connection *p);
+
+/* delete a connection from the list */
+extern void delete_connection(int fd);
+
+/* find a connection in the list by file descriptor */
+/* ??? This routine is unnecessary. When we call the connection's process
+ * routine, we should pass a pointer to the connection. That will have
+ * the usual fd along with everything else. */
+extern struct connection *find_connection(int fd);
+
+/* find a connection in the list by pathname */
+/* ??? unused last I checked */
+extern struct connection *find_connection_name(char *pathname);
+
+/* get the number of connections in the list */
+extern int get_number_of_connections();
+
+/* get a specific connection by index from the list */
+extern struct connection *get_connection(int i);
+
+/* get an fd_set with all the fd's that have been added to the list */
+extern const fd_set *get_fdset();
+
+/* get the highest fd that was added to the list */
+extern int get_highestfd();
+
+#endif /* CONNECTION_LIST_H__ */
--- /dev/null
+/*
+ * event.c - ACPI daemon event handler
+ *
+ * Copyright (C) 2000 Andrew Henroid
+ * Copyright (C) 2001 Sun Microsystems (thockin@sun.com)
+ * Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <regex.h>
+#include <signal.h>
+
+#include "acpid.h"
+#include "log.h"
+#include "sock.h"
+#include "ud_socket.h"
+
+/*
+ * What is a rule? It's polymorphic, pretty much.
+ */
+#define RULE_REGEX_FLAGS (REG_EXTENDED | REG_ICASE | REG_NOSUB | REG_NEWLINE)
+struct rule {
+ enum {
+ RULE_NONE = 0,
+ RULE_CMD,
+ RULE_CLIENT,
+ } type;
+ char *origin;
+ regex_t *event;
+ union {
+ char *cmd;
+ int fd;
+ } action;
+ struct rule *next;
+ struct rule *prev;
+};
+struct rule_list {
+ struct rule *head;
+ struct rule *tail;
+};
+static struct rule_list cmd_list;
+static struct rule_list client_list;
+
+/* rule routines */
+static void enlist_rule(struct rule_list *list, struct rule *r);
+static void delist_rule(struct rule_list *list, struct rule *r);
+static struct rule *new_rule(void);
+static void free_rule(struct rule *r);
+
+/* other helper routines */
+static void lock_rules(void);
+static void unlock_rules(void);
+static sigset_t *signals_handled(void);
+static struct rule *parse_file(const char *file);
+static struct rule *parse_client(int client);
+static int do_cmd_rule(struct rule *r, const char *event);
+static int do_client_rule(struct rule *r, const char *event);
+static int safe_write(int fd, const char *buf, int len);
+static char *parse_cmd(const char *cmd, const char *event);
+static int check_escapes(const char *str);
+
+/*
+ * read in all the configuration files
+ */
+int
+acpid_read_conf(const char *confdir)
+{
+ DIR *dir;
+ struct dirent *dirent;
+ char *file = NULL;
+ int nrules = 0;
+ regex_t preg;
+ int rc = 0;
+
+ lock_rules();
+
+ dir = opendir(confdir);
+ if (!dir) {
+ acpid_log(LOG_ERR, "opendir(%s): %s",
+ confdir, strerror(errno));
+ unlock_rules();
+ return -1;
+ }
+
+ /* Compile the regular expression. This is based on run-parts(8). */
+ rc = regcomp(&preg, "^[a-zA-Z0-9_-]+$", RULE_REGEX_FLAGS);
+ if (rc) {
+ acpid_log(LOG_ERR, "regcomp(): %d", rc);
+ unlock_rules();
+ return -1;
+ }
+
+ /* scan all the files */
+ while ((dirent = readdir(dir))) {
+ int len;
+ struct rule *r;
+ struct stat stat_buf;
+
+ len = strlen(dirent->d_name);
+
+ /* skip "." and ".." */
+ if (strncmp(dirent->d_name, ".", sizeof(dirent->d_name)) == 0)
+ continue;
+ if (strncmp(dirent->d_name, "..", sizeof(dirent->d_name)) == 0)
+ continue;
+
+ /* skip any files that don't match the run-parts convention */
+ if (regexec(&preg, dirent->d_name, 0, NULL, 0) != 0) {
+ acpid_log(LOG_INFO, "skipping conf file %s/%s",
+ confdir, dirent->d_name);
+ continue;
+ }
+
+ /* Compute the length of the full path name adding one for */
+ /* the slash and one more for the NULL. */
+ len += strlen(confdir) + 2;
+
+ file = malloc(len);
+ if (!file) {
+ acpid_log(LOG_ERR, "malloc(): %s", strerror(errno));
+ unlock_rules();
+ return -1;
+ }
+ snprintf(file, len, "%s/%s", confdir, dirent->d_name);
+
+ /* allow only regular files and symlinks to files */
+ if (stat(file, &stat_buf) != 0) {
+ acpid_log(LOG_ERR, "stat(%s): %s", file,
+ strerror(errno));
+ free(file);
+ continue; /* keep trying the rest of the files */
+ }
+ if (!S_ISREG(stat_buf.st_mode)) {
+ acpid_log(LOG_INFO, "skipping non-file %s", file);
+ free(file);
+ continue; /* skip non-regular files */
+ }
+
+ r = parse_file(file);
+ if (r) {
+ enlist_rule(&cmd_list, r);
+ nrules++;
+ }
+ free(file);
+ }
+ closedir(dir);
+ unlock_rules();
+
+ acpid_log(LOG_INFO, "%d rule%s loaded",
+ nrules, (nrules == 1)?"":"s");
+
+ return 0;
+}
+
+/*
+ * cleanup all rules
+ */
+int
+acpid_cleanup_rules(int do_detach)
+{
+ struct rule *p;
+ struct rule *next;
+
+ lock_rules();
+
+ if (acpid_debug >= 3) {
+ acpid_log(LOG_DEBUG, "cleaning up rules");
+ }
+
+ if (do_detach) {
+ /* tell our clients to buzz off */
+ p = client_list.head;
+ while (p) {
+ next = p->next;
+ delist_rule(&client_list, p);
+ close(p->action.fd);
+ free_rule(p);
+ p = next;
+ }
+ }
+
+ /* clear out our conf rules */
+ p = cmd_list.head;
+ while (p) {
+ next = p->next;
+ delist_rule(&cmd_list, p);
+ free_rule(p);
+ p = next;
+ }
+
+ unlock_rules();
+
+ return 0;
+}
+
+static struct rule *
+parse_file(const char *file)
+{
+ FILE *fp;
+ char buf[512];
+ int line = 0;
+ struct rule *r;
+
+ acpid_log(LOG_DEBUG, "parsing conf file %s", file);
+
+ fp = fopen(file, "r");
+ if (!fp) {
+ acpid_log(LOG_ERR, "fopen(%s): %s", file, strerror(errno));
+ return NULL;
+ }
+
+ /* make a new rule */
+ r = new_rule();
+ if (!r) {
+ fclose(fp);
+ return NULL;
+ }
+ r->type = RULE_CMD;
+ r->origin = strdup(file);
+ if (!r->origin) {
+ acpid_log(LOG_ERR, "strdup(): %s", strerror(errno));
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+
+ /* read each line */
+ while (!feof(fp) && !ferror(fp)) {
+ char *p = buf;
+ char key[64];
+ char val[512];
+ int n;
+
+ line++;
+ memset(key, 0, sizeof(key));
+ memset(val, 0, sizeof(val));
+
+ if (fgets(buf, sizeof(buf)-1, fp) == NULL) {
+ continue;
+ }
+
+ /* skip leading whitespace */
+ while (*p && isspace((int)*p)) {
+ p++;
+ }
+ /* blank lines and comments get ignored */
+ if (!*p || *p == '#') {
+ continue;
+ }
+
+ /* quick parse */
+ n = sscanf(p, "%63[^=\n]=%255[^\n]", key, val);
+ if (n != 2) {
+ acpid_log(LOG_WARNING, "can't parse %s at line %d",
+ file, line);
+ continue;
+ }
+ if (acpid_debug >= 3) {
+ acpid_log(LOG_DEBUG, " key=\"%s\" val=\"%s\"",
+ key, val);
+ }
+ /* handle the parsed line */
+ if (!strcasecmp(key, "event")) {
+ int rv;
+ r->event = malloc(sizeof(regex_t));
+ if (!r->event) {
+ acpid_log(LOG_ERR, "malloc(): %s",
+ strerror(errno));
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+ rv = regcomp(r->event, val, RULE_REGEX_FLAGS);
+ if (rv) {
+ char rbuf[128];
+ regerror(rv, r->event, rbuf, sizeof(rbuf));
+ acpid_log(LOG_ERR, "regcomp(): %s", rbuf);
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+ } else if (!strcasecmp(key, "action")) {
+ if (check_escapes(val) < 0) {
+ acpid_log(LOG_ERR, "can't load file %s",
+ file);
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+ r->action.cmd = strdup(val);
+ if (!r->action.cmd) {
+ acpid_log(LOG_ERR, "strdup(): %s",
+ strerror(errno));
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+ } else {
+ acpid_log(LOG_WARNING,
+ "unknown option '%s' in %s at line %d",
+ key, file, line);
+ continue;
+ }
+ }
+ if (!r->event || !r->action.cmd) {
+ acpid_log(LOG_INFO, "skipping incomplete file %s", file);
+ free_rule(r);
+ fclose(fp);
+ return NULL;
+ }
+ fclose(fp);
+
+ return r;
+}
+
+int
+acpid_add_client(int clifd, const char *origin)
+{
+ struct rule *r;
+ int nrules = 0;
+
+ acpid_log(LOG_NOTICE, "client connected from %s", origin);
+
+ r = parse_client(clifd);
+ if (r) {
+ r->origin = strdup(origin);
+ enlist_rule(&client_list, r);
+ nrules++;
+ }
+
+ acpid_log(LOG_INFO, "%d client rule%s loaded",
+ nrules, (nrules == 1)?"":"s");
+
+ return 0;
+}
+
+static struct rule *
+parse_client(int client)
+{
+ struct rule *r;
+ int rv;
+
+ /* make a new rule */
+ r = new_rule();
+ if (!r) {
+ return NULL;
+ }
+ r->type = RULE_CLIENT;
+ r->action.fd = client;
+ r->event = malloc(sizeof(regex_t));
+ if (!r->event) {
+ acpid_log(LOG_ERR, "malloc(): %s", strerror(errno));
+ free_rule(r);
+ return NULL;
+ }
+ rv = regcomp(r->event, ".*", RULE_REGEX_FLAGS);
+ if (rv) {
+ char buf[128];
+ regerror(rv, r->event, buf, sizeof(buf));
+ acpid_log(LOG_ERR, "regcomp(): %s", buf);
+ free_rule(r);
+ return NULL;
+ }
+
+ return r;
+}
+
+/*
+ * a few rule methods
+ */
+
+static void
+enlist_rule(struct rule_list *list, struct rule *r)
+{
+ r->next = r->prev = NULL;
+ if (!list->head) {
+ list->head = list->tail = r;
+ } else {
+ list->tail->next = r;
+ r->prev = list->tail;
+ list->tail = r;
+ }
+}
+
+static void
+delist_rule(struct rule_list *list, struct rule *r)
+{
+ if (r->next) {
+ r->next->prev = r->prev;
+ } else {
+ list->tail = r->prev;
+ }
+
+ if (r->prev) {
+ r->prev->next = r->next;
+ } else {
+ list->head = r->next;;
+ }
+
+ r->next = r->prev = NULL;
+}
+
+static struct rule *
+new_rule(void)
+{
+ struct rule *r;
+
+ r = malloc(sizeof(*r));
+ if (!r) {
+ acpid_log(LOG_ERR, "malloc(): %s", strerror(errno));
+ return NULL;
+ }
+
+ r->type = RULE_NONE;
+ r->origin = NULL;
+ r->event = NULL;
+ r->action.cmd = NULL;
+ r->prev = r->next = NULL;
+
+ return r;
+}
+
+/* I hope you delisted the rule before you free() it */
+static void
+free_rule(struct rule *r)
+{
+ if (r->type == RULE_CMD) {
+ if (r->action.cmd) {
+ free(r->action.cmd);
+ }
+ }
+
+ if (r->origin) {
+ free(r->origin);
+ }
+ if (r->event) {
+ regfree(r->event);
+ free(r->event);
+ }
+
+ free(r);
+}
+
+static int
+client_is_dead(int fd)
+{
+ struct pollfd pfd;
+ int r;
+
+ /* check the fd to see if it is dead */
+ pfd.fd = fd;
+ pfd.events = POLLERR | POLLHUP;
+ r = poll(&pfd, 1, 0);
+
+ if (r < 0) {
+ acpid_log(LOG_ERR, "poll(): %s", strerror(errno));
+ return 0;
+ }
+
+ return pfd.revents;
+}
+
+void
+acpid_close_dead_clients(void)
+{
+ struct rule *p;
+
+ lock_rules();
+
+ /* scan our client list */
+ p = client_list.head;
+ while (p) {
+ struct rule *next = p->next;
+ if (client_is_dead(p->action.fd)) {
+ struct ucred cred;
+ /* closed */
+ acpid_log(LOG_NOTICE,
+ "client %s has disconnected", p->origin);
+ delist_rule(&client_list, p);
+ ud_get_peercred(p->action.fd, &cred);
+ if (cred.uid != 0) {
+ non_root_clients--;
+ }
+ close(p->action.fd);
+ free_rule(p);
+ }
+ p = next;
+ }
+
+ unlock_rules();
+}
+
+/*
+ * the main hook for propogating events
+ */
+int
+acpid_handle_event(const char *event)
+{
+ struct rule *p;
+ int nrules = 0;
+ struct rule_list *ar[] = { &client_list, &cmd_list, NULL };
+ struct rule_list **lp;
+
+ /* make an event be atomic wrt known signals */
+ lock_rules();
+
+ /* scan each rule list for any rules that care about this event */
+ for (lp = ar; *lp; lp++) {
+ struct rule_list *l = *lp;
+ p = l->head;
+ while (p) {
+ /* the list can change underneath us */
+ struct rule *pnext = p->next;
+ if (!regexec(p->event, event, 0, NULL, 0)) {
+ /* a match! */
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "rule from %s matched",
+ p->origin);
+ }
+ nrules++;
+ if (p->type == RULE_CMD) {
+ do_cmd_rule(p, event);
+ } else if (p->type == RULE_CLIENT) {
+ do_client_rule(p, event);
+ } else {
+ acpid_log(LOG_WARNING,
+ "unknown rule type: %d",
+ p->type);
+ }
+ } else {
+ if (acpid_debug >= 3 && logevents) {
+ acpid_log(LOG_INFO,
+ "rule from %s did not match",
+ p->origin);
+ }
+ }
+ p = pnext;
+ }
+ }
+
+ unlock_rules();
+
+ if (logevents) {
+ acpid_log(LOG_INFO, "%d total rule%s matched",
+ nrules, (nrules == 1)?"":"s");
+ }
+
+ return 0;
+}
+
+/* helper functions to block signals while iterating */
+static sigset_t *
+signals_handled(void)
+{
+ static sigset_t set;
+
+ sigemptyset(&set);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGTERM);
+ sigaddset(&set, SIGQUIT);
+ sigaddset(&set, SIGINT);
+
+ return &set;
+}
+
+static void
+lock_rules(void)
+{
+ if (acpid_debug >= 4) {
+ acpid_log(LOG_DEBUG, "blocking signals for rule lock");
+ }
+ sigprocmask(SIG_BLOCK, signals_handled(), NULL);
+}
+
+static void
+unlock_rules(void)
+{
+ if (acpid_debug >= 4) {
+ acpid_log(LOG_DEBUG, "unblocking signals for rule lock");
+ }
+ sigprocmask(SIG_UNBLOCK, signals_handled(), NULL);
+}
+
+/*
+ * the meat of the rules
+ */
+
+static int
+do_cmd_rule(struct rule *rule, const char *event)
+{
+ pid_t pid;
+ int status;
+ const char *action;
+
+ pid = fork();
+ switch (pid) {
+ case -1:
+ acpid_log(LOG_ERR, "fork(): %s", strerror(errno));
+ return -1;
+ case 0: /* child */
+ /* parse the commandline, doing any substitutions needed */
+ action = parse_cmd(rule->action.cmd, event);
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "executing action \"%s\"", action);
+ }
+
+ /* reset signals */
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+ sigprocmask(SIG_UNBLOCK, signals_handled(), NULL);
+
+ if (acpid_debug && logevents) {
+ fprintf(stdout, "BEGIN HANDLER MESSAGES\n");
+ }
+ umask(0077);
+ execl("/bin/sh", "/bin/sh", "-c", action, NULL);
+ /* should not get here */
+ acpid_log(LOG_ERR, "execl(): %s", strerror(errno));
+ _exit(EXIT_FAILURE);
+ }
+
+ /* parent */
+ waitpid(pid, &status, 0);
+ if (acpid_debug && logevents) {
+ fprintf(stdout, "END HANDLER MESSAGES\n");
+ }
+
+ if (logevents) {
+ if (WIFEXITED(status)) {
+ acpid_log(LOG_INFO, "action exited with status %d",
+ WEXITSTATUS(status));
+ } else if (WIFSIGNALED(status)) {
+ acpid_log(LOG_INFO, "action exited on signal %d",
+ WTERMSIG(status));
+ } else {
+ acpid_log(LOG_INFO, "action exited with status %d",
+ status);
+ }
+ }
+
+ return 0;
+}
+
+static int
+do_client_rule(struct rule *rule, const char *event)
+{
+ int r;
+ int client = rule->action.fd;
+
+ if (logevents) {
+ acpid_log(LOG_INFO, "notifying client %s", rule->origin);
+ }
+
+ r = safe_write(client, event, strlen(event));
+ if (r < 0 && errno == EPIPE) {
+ struct ucred cred;
+ /* closed */
+ acpid_log(LOG_NOTICE,
+ "client %s has disconnected", rule->origin);
+ delist_rule(&client_list, rule);
+ ud_get_peercred(rule->action.fd, &cred);
+ if (cred.uid != 0) {
+ non_root_clients--;
+ }
+ close(rule->action.fd);
+ free_rule(rule);
+ return -1;
+ }
+ safe_write(client, "\n", 1);
+
+ return 0;
+}
+
+#define NTRIES 100
+static int
+safe_write(int fd, const char *buf, int len)
+{
+ int r;
+ int ttl = 0;
+ int ntries = NTRIES;
+
+ do {
+ r = write(fd, buf+ttl, len-ttl);
+ if (r < 0) {
+ if (errno != EAGAIN && errno != EINTR) {
+ /* a legit error */
+ return r;
+ }
+ ntries--;
+ } else if (r > 0) {
+ /* as long as we make forward progress, reset ntries */
+ ntries = NTRIES;
+ ttl += r;
+ }
+ } while (ttl < len && ntries);
+
+ if (!ntries) {
+ if (acpid_debug >= 2) {
+ acpid_log(LOG_ERR, "safe_write() timed out");
+ }
+ return r;
+ }
+
+ return ttl;
+}
+
+static char *
+parse_cmd(const char *cmd, const char *event)
+{
+ static char buf[4096];
+ size_t i;
+ const char *p;
+
+ p = cmd;
+ i = 0;
+
+ memset(buf, 0, sizeof(buf));
+ while (i < (sizeof(buf)-1)) {
+ if (*p == '%') {
+ p++;
+ if (*p == 'e') {
+ /* handle an event expansion */
+ size_t size = sizeof(buf) - i;
+ size = snprintf(buf+i, size, "%s", event);
+ i += size;
+ p++;
+ continue;
+ }
+ }
+ if (!*p) {
+ break;
+ }
+ buf[i++] = *p++;
+ }
+ if (acpid_debug >= 2) {
+ acpid_log(LOG_DEBUG, "expanded \"%s\" -> \"%s\"", cmd, buf);
+ }
+
+ return buf;
+}
+
+static int
+check_escapes(const char *str)
+{
+ const char *p;
+ int r = 0;
+
+ p = str;
+ while (*p) {
+ /* found an escape */
+ if (*p == '%') {
+ p++;
+ if (!*p) {
+ acpid_log(LOG_WARNING,
+ "invalid escape at EOL");
+ return -1;
+ } else if (*p != '%' && *p != 'e') {
+ acpid_log(LOG_WARNING,
+ "invalid escape \"%%%c\"", *p);
+ r = -1;
+ }
+ }
+ p++;
+ }
+ return r;
+}
--- /dev/null
+/*
+ * event.h - ACPI daemon event handler
+ *
+ * Copyright (C) 1999-2000 Andrew Henroid
+ * Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * 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
+ */
+
+#ifndef EVENT_H__
+#define EVENT_H__
+
+extern int acpid_read_conf(const char *confdir);
+extern int acpid_add_client(int client, const char *origin);
+extern int acpid_cleanup_rules(int do_detach);
+extern int acpid_handle_event(const char *event);
+extern void acpid_close_dead_clients(void);
+
+#endif /* EVENT_H__ */
--- /dev/null
+#ifndef __LINUX_GENERIC_NETLINK_H
+#define __LINUX_GENERIC_NETLINK_H
+
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ 16 /* length of family name */
+
+#define GENL_MIN_ID NLMSG_MIN_TYPE
+#define GENL_MAX_ID 1023
+
+struct genlmsghdr {
+ __u8 cmd;
+ __u8 version;
+ __u16 reserved;
+};
+
+#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_CMD_CAP_DO 0x02
+#define GENL_CMD_CAP_DUMP 0x04
+#define GENL_CMD_CAP_HASPOL 0x08
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_GENERATE 0
+#define GENL_ID_CTRL NLMSG_MIN_TYPE
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+ CTRL_CMD_UNSPEC,
+ CTRL_CMD_NEWFAMILY,
+ CTRL_CMD_DELFAMILY,
+ CTRL_CMD_GETFAMILY,
+ CTRL_CMD_NEWOPS,
+ CTRL_CMD_DELOPS,
+ CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
+ __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+ CTRL_ATTR_UNSPEC,
+ CTRL_ATTR_FAMILY_ID,
+ CTRL_ATTR_FAMILY_NAME,
+ CTRL_ATTR_VERSION,
+ CTRL_ATTR_HDRSIZE,
+ CTRL_ATTR_MAXATTR,
+ CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
+ __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+ CTRL_ATTR_OP_UNSPEC,
+ CTRL_ATTR_OP_ID,
+ CTRL_ATTR_OP_FLAGS,
+ __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
--- /dev/null
+/*
+ * inotify_handler.c - inotify Handler for New Devices
+ *
+ * Watches /dev/input for new input layer device files.
+ *
+ * Copyright (C) 2009, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <sys/inotify.h>
+#include <sys/select.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+/* local */
+#include "acpid.h"
+#include "log.h"
+#include "connection_list.h"
+#include "input_layer.h"
+
+/*-----------------------------------------------------------------*/
+/* called when an inotify event is received */
+void process_inotify(int fd)
+{
+ int bytes;
+ /* union to avoid strict-aliasing problems */
+ union {
+ char buffer[256]; /* a tad large */
+ struct inotify_event event;
+ } eventbuf;
+
+ bytes = read(fd, &eventbuf.buffer, sizeof(eventbuf.buffer));
+
+ acpid_log(LOG_DEBUG, "inotify read bytes: %d", bytes);
+
+ /* eof is not expected */
+ if (bytes == 0) {
+ acpid_log(LOG_WARNING, "inotify fd eof encountered");
+ return;
+ }
+ else if (bytes < 0) {
+ /* EINVAL means buffer wasn't big enough. See inotify(7). */
+ acpid_log(LOG_ERR, "inotify read error: %s (%d)",
+ strerror(errno), errno);
+ acpid_log(LOG_ERR, "disconnecting from inotify");
+ delete_connection(fd);
+ return;
+ }
+
+ acpid_log(LOG_DEBUG, "inotify name len: %d", eventbuf.event.len);
+
+ const int dnsize = 256;
+ char devname[dnsize];
+
+ /* if a name is included */
+ if (eventbuf.event.len > 0) {
+ /* devname = ACPID_INPUTLAYERDIR + "/" + pevent -> name */
+ strcpy(devname, ACPID_INPUTLAYERDIR);
+ strcat(devname, "/");
+ strncat(devname, eventbuf.event.name, dnsize - strlen(devname) - 1);
+ }
+
+ /* if this is a create */
+ if (eventbuf.event.mask & IN_CREATE) {
+ acpid_log(LOG_DEBUG, "inotify about to open: %s", devname);
+
+ open_inputfile(devname);
+ }
+
+ /* if this is a delete */
+ if (eventbuf.event.mask & IN_DELETE) {
+ /* struct connection *c; */
+
+ acpid_log(LOG_DEBUG, "inotify received a delete for: %s", devname);
+
+#if 0
+/* Switching back to the original ENODEV detection scheme. See
+ process_input() in input_layer.c. */
+/* keeping this for future reference */
+ /* search for the event file in the connection list */
+ /* ??? Or should we just have a delete_connection_name()? */
+ c = find_connection_name(devname);
+
+ /* close that connection if found */
+ if (c)
+ delete_connection(c->fd);
+#endif
+ }
+}
+
+/*-----------------------------------------------------------------*/
+/* Set up an inotify watch on /dev/input. */
+void open_inotify(void)
+{
+ int fd = -1;
+ int wd = -1;
+ struct connection c;
+
+ /* set up inotify */
+ fd = inotify_init();
+
+ if (fd < 0) {
+ acpid_log(LOG_ERR, "inotify_init() failed: %s (%d)",
+ strerror(errno), errno);
+ return;
+ }
+
+ acpid_log(LOG_DEBUG, "inotify fd: %d", fd);
+
+ /* watch for files being created or deleted in /dev/input */
+ wd = inotify_add_watch(fd, ACPID_INPUTLAYERDIR, IN_CREATE | IN_DELETE);
+
+ if (wd < 0) {
+ acpid_log(LOG_ERR, "inotify_add_watch() failed: %s (%d)",
+ strerror(errno), errno);
+ close(fd);
+ return;
+ }
+
+ acpid_log(LOG_DEBUG, "inotify wd: %d", wd);
+
+ /* add a connection to the list */
+ c.fd = fd;
+ c.process = process_inotify;
+ c.pathname = NULL;
+ c.kybd = 0;
+ add_connection(&c);
+}
+
--- /dev/null
+/*
+ * inotify_handler.h - inotify Handler for New Devices
+ *
+ * Watches /dev/input for new input layer device files.
+ *
+ * Copyright (C) 2009, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef INOTIFY_HANDLER_H__
+#define INOTIFY_HANDLER_H__
+
+/* Set up an inotify watch on /dev/input. */
+extern void open_inotify(void);
+
+#endif /* INOTIFY_HANDLER_H__ */
--- /dev/null
+/*
+ * input_layer - Kernel ACPI Event Input Layer Interface
+ *
+ * Handles the details of getting kernel ACPI events from the input
+ * layer (/dev/input/event*).
+ *
+ * Inspired by (and in some cases blatantly lifted from) Vojtech Pavlik's
+ * evtest.c.
+ *
+ * Copyright (C) 2008-2009, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <glob.h>
+
+/* local */
+#include "acpid.h"
+#include "log.h"
+#include "connection_list.h"
+#include "event.h"
+
+#define DIM(a) (sizeof(a) / sizeof(a[0]))
+
+struct evtab_entry {
+ struct input_event event;
+ const char *str;
+};
+
+/* Event Table: Events we are interested in and their strings. Use
+ evtest.c, acpi_genl, or kacpimon to find new events to add to this
+ table. */
+static struct evtab_entry evtab[] = {
+ {{{0,0}, EV_KEY, KEY_POWER, 1}, "button/power PBTN 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_SUSPEND, 1},
+ "button/suspend SUSP 00000080 00000000"},
+ {{{0,0}, EV_SW, SW_LID, 1}, "button/lid LID close"},
+ {{{0,0}, EV_SW, SW_LID, 0}, "button/lid LID open"},
+ /* blue access IBM button on Thinkpad T42p*/
+ {{{0,0}, EV_KEY, KEY_PROG1, 1}, "button/prog1 PROG1 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_VENDOR, 1}, "button/vendor VNDR 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F1, 1}, "button/fnf1 FNF1 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F2, 1}, "button/fnf2 FNF2 00000080 00000000"},
+ /* Fn-F2 produces KEY_BATTERY on Thinkpad T42p */
+ {{{0,0}, EV_KEY, KEY_BATTERY, 1},
+ "button/battery BAT 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_SCREENLOCK, 1},
+ "button/screenlock SCRNLCK 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_COFFEE, 1}, "button/coffee CFEE 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_SLEEP, 1}, "button/sleep SBTN 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_WLAN, 1}, "button/wlan WLAN 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F6, 1}, "button/fnf6 FNF6 00000080 00000000"},
+ /* procfs on Thinkpad 600X reports "video VID0 00000080 00000000" */
+ /* typical events file has "video.* 00000080" */
+ {{{0,0}, EV_KEY, KEY_SWITCHVIDEOMODE, 1},
+ "video/switchmode VMOD 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F9, 1}, "button/fnf9 FNF9 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F10, 1}, "button/fnf10 FF10 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_FN_F11, 1}, "button/fnf11 FF11 00000080 00000000"},
+ /* Fn-F9 produces KEY_F24 on Thinkpad T42p */
+ {{{0,0}, EV_KEY, KEY_F24, 1}, "button/f24 F24 00000080 00000000"},
+
+#if 0
+ /* These "EV_MSC, 4, x" events cause trouble. They are triggered */
+ /* by unexpected keys on the keyboard. */
+ /* The 4 is MSC_SCAN, so these are actually scan code events. */
+
+ /* EV_MSC, MSC_SCAN, KEY_MINUS This is triggered by the minus key. */
+ {{{0,0}, EV_MSC, 4, 12}, "button/fnbs FNBS 00000080 00000000"},
+
+ /* EV_MSC, MSC_SCAN, KEY_EQUAL Triggered by the equals key. */
+ {{{0,0}, EV_MSC, 4, 13}, "button/fnins FNINS 00000080 00000000"},
+
+ /* EV_MSC, MSC_SCAN, KEY_BACKSPACE Triggered by the backspace key. */
+ {{{0,0}, EV_MSC, 4, 14}, "button/fndel FNDEL 00000080 00000000"},
+
+ /* EV_MSC, MSC_SCAN, KEY_E Triggered by the 'E' key. */
+ {{{0,0}, EV_MSC, 4, 18}, "button/fnpgdown FNPGDOWN 00000080 00000000"},
+#endif
+
+ {{{0,0}, EV_KEY, KEY_ZOOM, 1}, "button/zoom ZOOM 00000080 00000000"},
+ /* typical events file has "video.* 00000087" */
+ {{{0,0}, EV_KEY, KEY_BRIGHTNESSDOWN, 1},
+ "video/brightnessdown BRTDN 00000087 00000000"},
+ /* typical events file has "video.* 00000086" */
+ {{{0,0}, EV_KEY, KEY_BRIGHTNESSUP, 1},
+ "video/brightnessup BRTUP 00000086 00000000"},
+ {{{0,0}, EV_KEY, KEY_KBDILLUMTOGGLE, 1},
+ "button/kbdillumtoggle KBILLUM 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_VOLUMEDOWN, 1},
+ "button/volumedown VOLDN 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_VOLUMEUP, 1},
+ "button/volumeup VOLUP 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_MUTE, 1},
+ "button/mute MUTE 00000080 00000000"},
+ /* cd play/pause buttons */
+ {{{0,0}, EV_KEY, KEY_NEXTSONG, 1},
+ "cd/next CDNEXT 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_PREVIOUSSONG, 1},
+ "cd/prev CDPREV 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_PLAYPAUSE, 1},
+ "cd/play CDPLAY 00000080 00000000"},
+ {{{0,0}, EV_KEY, KEY_STOPCD, 1},
+ "cd/stop CDSTOP 00000080 00000000"},
+ /* additional events divined from the kernel's video.c */
+ {{{0,0}, EV_KEY, KEY_VIDEO_NEXT, 1},
+ "video/next NEXT 00000083 00000000"},
+ {{{0,0}, EV_KEY, KEY_VIDEO_PREV, 1},
+ "video/prev PREV 00000084 00000000"},
+ {{{0,0}, EV_KEY, KEY_BRIGHTNESS_CYCLE, 1},
+ "video/brightnesscycle BCYC 00000085 00000000"},
+ {{{0,0}, EV_KEY, KEY_BRIGHTNESS_ZERO, 1},
+ "video/brightnesszero BZRO 00000088 00000000"},
+ {{{0,0}, EV_KEY, KEY_DISPLAY_OFF, 1},
+ "video/displayoff DOFF 00000089 00000000"}
+};
+
+/*----------------------------------------------------------------------*/
+/* Given an input event, returns the string corresponding to that event.
+ If there is no corresponding string, NULL is returned. */
+static const char *
+event_string(struct input_event event)
+{
+ unsigned i;
+
+ /* for each entry in the event table */
+ /* ??? is there a faster way? */
+ for (i = 0; i < DIM(evtab); ++i)
+ {
+ /* if this is a matching event, return its string */
+ if (event.type == evtab[i].event.type &&
+ event.code == evtab[i].event.code &&
+ event.value == evtab[i].event.value) {
+ return evtab[i].str;
+ }
+ }
+
+ return NULL;
+}
+
+/*-----------------------------------------------------------------*/
+/* returns non-zero if the event type/code is one we need */
+static int
+need_event(int type, int code)
+{
+ unsigned i;
+
+ /* for each entry in the event table */
+ for (i = 0; i < DIM(evtab); ++i) {
+ /* if we found a matching event */
+ if (type == evtab[i].event.type &&
+ code == evtab[i].event.code) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------*/
+/* called when an input layer event is received */
+void process_input(int fd)
+{
+ struct input_event event;
+ ssize_t nbytes;
+ const char *str;
+ static int nerrs;
+ struct connection *c;
+ char str2[100];
+
+ nbytes = read(fd, &event, sizeof(event));
+
+ if (nbytes == 0) {
+ acpid_log(LOG_WARNING, "input layer connection closed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (nbytes < 0) {
+ /* if it's a signal, bail */
+ if (errno == EINTR)
+ return;
+ if (errno == ENODEV) {
+ acpid_log(LOG_WARNING, "input device has been disconnected, fd %d",
+ fd);
+ delete_connection(fd);
+ return;
+ }
+ acpid_log(LOG_ERR, "input layer read error: %s (%d)",
+ strerror(errno), errno);
+ if (++nerrs >= ACPID_MAX_ERRS) {
+ acpid_log(LOG_ERR,
+ "too many errors reading "
+ "input layer - aborting");
+ exit(EXIT_FAILURE);
+ }
+ return;
+ }
+
+ /* ??? Is it possible for a partial message to come across? */
+ /* If so, we've got more code to write... */
+
+ if (nbytes != sizeof(event)) {
+ acpid_log(LOG_WARNING, "input layer unexpected length: "
+ "%d expected: %d", nbytes, sizeof(event));
+ return;
+ }
+
+ c = find_connection(fd);
+
+ /* if we're getting scancodes, we probably have a keyboard */
+ if (event.type == EV_MSC && event.code == MSC_SCAN) {
+ if (c)
+ c->kybd = 1; /* appears to be a keyboard device */
+ }
+
+ /* convert the event into a string */
+ str = event_string(event);
+ /* if this is not an event we care about, bail */
+ if (str == NULL)
+ return;
+
+ /* If we suspect this is a keyboard, and we have enough space, tack a
+ * "K" on to the end of the event string. */
+ if (c && c->kybd && strnlen(str, sizeof(str2)) <= sizeof(str2) - 3) {
+ strcpy(str2, str);
+ strcat(str2, " K");
+ str = str2;
+ }
+
+ /* if we're locked, don't process the event */
+ if (locked()) {
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "lockfile present, not processing "
+ "input layer event \"%s\"", str);
+ }
+ return;
+ }
+
+ if (logevents)
+ acpid_log(LOG_INFO,
+ "received input layer event \"%s\"", str);
+
+ /* send the event off to the handler */
+ acpid_handle_event(str);
+
+ if (logevents)
+ acpid_log(LOG_INFO,
+ "completed input layer event \"%s\"", str);
+}
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+/*--------------------------------------------------------------------*/
+/* returns non-zero if the file descriptor supports one of the events */
+/* supported by event_string(). */
+static int
+has_event(int fd)
+{
+ int type, code;
+ unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
+
+ memset(bit, 0, sizeof(bit));
+ /* get the event type bitmap */
+ ioctl(fd, EVIOCGBIT(0, sizeof(bit[0])), bit[0]);
+
+ /* for each event type */
+ for (type = 0; type < EV_MAX; type++) {
+ /* if this event type is supported */
+ if (test_bit(type, bit[0])) {
+ /* skip sync */
+ if (type == EV_SYN) continue;
+ /* get the event code mask */
+ ioctl(fd, EVIOCGBIT(type, sizeof(bit[type])), bit[type]);
+ /* for each event code */
+ for (code = 0; code < KEY_MAX; code++) {
+ /* if this event code is supported */
+ if (test_bit(code, bit[type])) {
+ /* if we need this event */
+ if (need_event(type, code) != 0)
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*-----------------------------------------------------------------*
+ * open a single input layer file for input */
+int open_inputfile(const char *filename)
+{
+ int fd;
+ struct connection c;
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+
+ /* Make sure scripts we exec() (in event.c) don't get our file
+ descriptors. */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ if (fd >= 0) {
+ char evname[256];
+
+ /* if this file doesn't have events we need, indicate failure */
+ if (!has_event(fd)) {
+ close(fd);
+ return -1;
+ }
+
+ /* get this event file's name for debugging */
+ strcpy(evname, "Unknown");
+ ioctl(fd, EVIOCGNAME(sizeof(evname)), evname);
+
+ acpid_log(LOG_DEBUG, "input layer %s (%s) "
+ "opened successfully, fd %d", filename, evname, fd);
+
+ /* add a connection to the list */
+ c.fd = fd;
+ c.process = process_input;
+ /* delete_connection() will free */
+ c.pathname = malloc(strlen(filename) + 1);
+ if (c.pathname)
+ strcpy(c.pathname, filename);
+ /* assume not a keyboard until we see a scancode */
+ c.kybd = 0;
+ add_connection(&c);
+
+ return 0; /* success */
+ }
+
+ /* open unsuccessful */
+ return -1;
+}
+
+/*-----------------------------------------------------------------*
+ * open each of the appropriate /dev/input/event* files for input */
+void open_input(void)
+{
+ char *filename = NULL;
+ glob_t globbuf;
+ unsigned i;
+ int success = 0;
+
+ /* get all the matching event filenames */
+ glob(ACPID_INPUTLAYERFILES, 0, NULL, &globbuf);
+
+ /* for each event file */
+ for (i = 0; i < globbuf.gl_pathc; ++i) {
+ filename = globbuf.gl_pathv[i];
+
+ /* open this input layer device file */
+ if (open_inputfile(filename) == 0)
+ success = 1;
+ }
+
+ if (!success)
+ acpid_log(LOG_ERR, "cannot open input layer");
+
+ globfree(&globbuf);
+}
+
--- /dev/null
+/*
+ * input_layer.h - Kernel ACPI Event Input Layer Interface
+ *
+ * Handles the details of getting kernel ACPI events from the input
+ * layer (/dev/input/event*).
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef INPUT_LAYER_H__
+#define INPUT_LAYER_H__
+
+/* Open each of the appropriate /dev/input/event* files for input. */
+extern void open_input(void);
+
+/* Open a single input layer device file for input. */
+extern int open_inputfile(const char *filename);
+
+#endif /* INPUT_LAYER_H__ */
--- /dev/null
+Moved to the man page. Try this:
+
+ man -l kacpimon.8
--- /dev/null
+#ifndef __ACPI_GENETLINK_H__
+#define __ACPI_GENETLINK_H__ 1
+
+#include <linux/types.h>
+
+struct acpi_genl_event {
+ char device_class[20];
+ char bus_id[15];
+ __u32 type;
+ __u32 data;
+};
+
+/* attributes of acpi_genl_family */
+enum {
+ ACPI_GENL_ATTR_UNSPEC,
+ ACPI_GENL_ATTR_EVENT, /* ACPI event info needed by user space */
+ __ACPI_GENL_ATTR_MAX,
+};
+#define ACPI_GENL_ATTR_MAX (__ACPI_GENL_ATTR_MAX - 1)
+
+/* commands supported by the acpi_genl_family */
+enum {
+ ACPI_GENL_CMD_UNSPEC,
+ ACPI_GENL_CMD_EVENT, /* kernel->user notifications for ACPI events */ __ACPI_GENL_CMD_MAX,
+};
+#define ACPI_GENL_CMD_MAX (__ACPI_GENL_CMD_MAX - 1)
+#define GENL_MAX_FAM_OPS 256
+#define GENL_MAX_FAM_GRPS 256
+
+#define ACPI_EVENT_FAMILY_NAME "acpi_event"
+#define ACPI_EVENT_MCAST_GROUP_NAME "acpi_mc_group"
+
+#endif
--- /dev/null
+/*
+ * acpi_ids.c - ACPI Netlink Group and Family IDs
+ *
+ * Copyright (C) 2008 Ted Felix (www.tedfelix.com)
+ * Portions from acpi_genl Copyright (C) Zhang Rui <rui.zhang@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+/* needed by netlink.h, should be in there */
+#include <arpa/inet.h>
+#include <linux/types.h>
+#include <string.h>
+
+#include "genetlink.h"
+#include "libnetlink.h"
+
+#define GENL_MAX_FAM_GRPS 256
+#define ACPI_EVENT_FAMILY_NAME "acpi_event"
+#define ACPI_EVENT_MCAST_GROUP_NAME "acpi_mc_group"
+
+static int initialized = 0;
+static __u16 acpi_event_family_id = 0;
+static __u32 acpi_event_mcast_group_id = 0;
+
+/*
+ * A CTRL_CMD_GETFAMILY message returns an attribute table that looks
+ * like this:
+ *
+ * CTRL_ATTR_FAMILY_ID Use this to make sure we get the proper msgs
+ * CTRL_ATTR_MCAST_GROUPS
+ * CTRL_ATTR_MCAST_GRP_NAME
+ * CTRL_ATTR_MCAST_GRP_ID Need this for the group mask
+ * ...
+ */
+
+static int
+get_ctrl_grp_id(struct rtattr *arg)
+{
+ struct rtattr *tb[CTRL_ATTR_MCAST_GRP_MAX + 1];
+ char *name;
+
+ if (arg == NULL)
+ return -1;
+
+ /* nested within the CTRL_ATTR_MCAST_GROUPS attribute are the */
+ /* group name and ID */
+ parse_rtattr_nested(tb, CTRL_ATTR_MCAST_GRP_MAX, arg);
+
+ /* if either of the entries needed cannot be found, bail */
+ if (!tb[CTRL_ATTR_MCAST_GRP_NAME] || !tb[CTRL_ATTR_MCAST_GRP_ID])
+ return -1;
+
+ /* get the name of this multicast group we've found */
+ name = RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_NAME]);
+
+ /* if it does not match the ACPI event multicast group name, bail */
+ if (strcmp(name, ACPI_EVENT_MCAST_GROUP_NAME))
+ return -1;
+
+ /* At this point, we've found what we were looking for. We now */
+ /* have the multicast group ID for ACPI events over generic netlink. */
+ acpi_event_mcast_group_id =
+ *((__u32 *)RTA_DATA(tb[CTRL_ATTR_MCAST_GRP_ID]));
+
+ return 0;
+}
+
+/* n = the response to a CTRL_CMD_GETFAMILY message */
+static int
+genl_get_mcast_group_id(struct nlmsghdr *n)
+{
+ /*
+ * Attribute table. Note the type name "rtattr" which means "route
+ * attribute". This is a vestige of one of netlink's main uses:
+ * routing.
+ */
+ struct rtattr *tb[CTRL_ATTR_MAX + 1];
+ /* place for the generic netlink header in the incoming message */
+ struct genlmsghdr ghdr;
+ /* length of the attribute and payload */
+ int len = n->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
+ /* Pointer to the attribute portion of the message */
+ struct rtattr *attrs;
+
+ if (len < 0) {
+ printf("Netlink: CTRL_CMD_GETFAMILY response, "
+ "wrong controller message len: %d\n", len);
+ return -1;
+ }
+
+ if (n->nlmsg_type != GENL_ID_CTRL) {
+ printf("Netlink: Not a controller message, nlmsg_len=%d "
+ "nlmsg_type=0x%x\n", n->nlmsg_len, n->nlmsg_type);
+ return 0;
+ }
+
+ /* copy generic netlink header into structure */
+ memcpy(&ghdr, NLMSG_DATA(n), GENL_HDRLEN);
+
+ if (ghdr.cmd != CTRL_CMD_GETFAMILY &&
+ ghdr.cmd != CTRL_CMD_DELFAMILY &&
+ ghdr.cmd != CTRL_CMD_NEWFAMILY &&
+ ghdr.cmd != CTRL_CMD_NEWMCAST_GRP &&
+ ghdr.cmd != CTRL_CMD_DELMCAST_GRP) {
+ printf("Netlink: Unknown controller command %d\n", ghdr.cmd);
+ return 0;
+ }
+
+ /* set attrs to point to the attribute */
+ attrs = (struct rtattr *)(NLMSG_DATA(n) + GENL_HDRLEN);
+ /* Read the table from the message into "tb". This actually just */
+ /* places pointers into the message into tb[]. */
+ parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
+
+ /* if a family ID attribute is present, get it */
+ if (tb[CTRL_ATTR_FAMILY_ID])
+ {
+ acpi_event_family_id =
+ *((__u32 *)RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
+ }
+
+ /* if a "multicast groups" attribute is present... */
+ if (tb[CTRL_ATTR_MCAST_GROUPS]) {
+ struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
+ int i;
+
+ /* get the group table within this attribute */
+ parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS,
+ tb[CTRL_ATTR_MCAST_GROUPS]);
+
+ /* for each group */
+ for (i = 0; i < GENL_MAX_FAM_GRPS; i++)
+ /* if this group is valid */
+ if (tb2[i])
+ /* Parse the ID. If successful, we're done. */
+ if (!get_ctrl_grp_id(tb2[i]))
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+genl_get_ids(char *family_name)
+{
+ /* handle to the netlink connection */
+ struct rtnl_handle rth;
+ /* holds the request we are going to send and the reply */
+ struct {
+ struct nlmsghdr n;
+ char buf[4096]; /* ??? Is this big enough for all cases? */
+ } req;
+ /* pointer to the nlmsghdr in req */
+ struct nlmsghdr *nlh;
+ /* place for the generic netlink header before copied into req */
+ struct genlmsghdr ghdr;
+ /* return value */
+ int ret = -1;
+
+ /* clear out the request */
+ memset(&req, 0, sizeof(req));
+
+ /* set up nlh to point to the netlink header in req */
+ nlh = &req.n;
+ /* set up the netlink header */
+ nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
+ nlh->nlmsg_type = GENL_ID_CTRL;
+
+ /* clear out the generic netlink message header */
+ memset(&ghdr, 0, sizeof(struct genlmsghdr));
+ /* set the command we want to run: "GETFAMILY" */
+ ghdr.cmd = CTRL_CMD_GETFAMILY;
+ /* copy it into req */
+ memcpy(NLMSG_DATA(&req.n), &ghdr, GENL_HDRLEN);
+
+ /* the message payload is the family name */
+ addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME,
+ family_name, strlen(family_name) + 1);
+
+ /* open a generic netlink connection */
+ if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC) < 0) {
+ printf("Netlink: Cannot open generic netlink socket\n");
+ return -1;
+ }
+
+ /*
+ * Send CTRL_CMD_GETFAMILY message (in nlh) to the generic
+ * netlink controller. Reply will be in nlh upon return.
+ */
+ if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL) < 0) {
+ printf("Netlink: Error talking to the kernel\n");
+ goto ctrl_done;
+ }
+
+ /* process the response */
+ if (genl_get_mcast_group_id(nlh) < 0) {
+ printf("Netlink: Failed to get acpi_event multicast group\n");
+ goto ctrl_done;
+ }
+
+ ret = 0;
+
+ctrl_done:
+ rtnl_close(&rth);
+ return ret;
+}
+
+/* initialize the ACPI IDs */
+static void
+acpi_ids_init()
+{
+ genl_get_ids(ACPI_EVENT_FAMILY_NAME);
+
+ initialized = 1;
+}
+
+/* returns the netlink family ID for ACPI event messages */
+__u16
+acpi_ids_getfamily()
+{
+ /* if the IDs haven't been initialized, initialize them */
+ if (initialized == 0)
+ acpi_ids_init();
+
+ return acpi_event_family_id;
+}
+
+/* returns the netlink multicast group ID for ACPI event messages */
+__u32
+acpi_ids_getgroup()
+{
+ /* if the IDs haven't been initialized, initialize them */
+ if (initialized == 0)
+ acpi_ids_init();
+
+ return acpi_event_mcast_group_id;
+}
--- /dev/null
+/*
+ * acpi_ids.h - ACPI Netlink Group and Family IDs
+ *
+ * Copyright (C) 2008 Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ */
+
+#ifndef ACPI_IDS_H__
+#define ACPI_IDS_H__
+
+/* returns the netlink family ID for ACPI event messages */
+extern __u16 acpi_ids_getfamily();
+
+/* returns the netlink multicast group ID for ACPI event messages */
+extern __u32 acpi_ids_getgroup();
+
+#endif /* ACPI_IDS_H__ */
--- /dev/null
+/*
+ * connection_list.c - ACPI daemon connection list
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * Tabs at 4
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+
+#include "connection_list.h"
+
+#define max(a, b) (((a)>(b))?(a):(b))
+
+/*---------------------------------------------------------------*/
+/* private objects */
+
+#define MAX_CONNECTIONS 20
+
+static struct connection connection_list[MAX_CONNECTIONS];
+
+static int nconnections = 0;
+
+/* fd_set containing all the fd's that come in */
+static fd_set allfds;
+
+/* highest fd that is opened */
+/* (-2 + 1) causes select() to return immediately */
+static int highestfd = -2;
+
+/*---------------------------------------------------------------*/
+/* public functions */
+
+void
+add_connection(struct connection *p)
+{
+ if (nconnections < 0)
+ return;
+ if (nconnections >= MAX_CONNECTIONS) {
+ printf("add_connection(): Too many connections.\n");
+ return;
+ }
+
+ if (nconnections == 0)
+ FD_ZERO(&allfds);
+
+ /* add the connection to the connection list */
+ connection_list[nconnections] = *p;
+ ++nconnections;
+
+ /* add to the fd set */
+ FD_SET(p->fd, &allfds);
+ highestfd = max(highestfd, p->fd);
+}
+
+/*---------------------------------------------------------------*/
+
+struct connection *
+find_connection(int fd)
+{
+ int i;
+
+ /* for each connection */
+ for (i = 0; i < nconnections; ++i) {
+ /* if the file descriptors match, return the connection */
+ if (connection_list[i].fd == fd)
+ return &connection_list[i];
+ }
+
+ return NULL;
+}
+
+/*---------------------------------------------------------------*/
+
+int
+get_number_of_connections()
+{
+ return nconnections;
+}
+
+/*---------------------------------------------------------------*/
+
+struct connection *
+get_connection(int i)
+{
+ if (i < 0 || i >= nconnections)
+ return NULL;
+
+ return &connection_list[i];
+}
+
+/*---------------------------------------------------------------*/
+
+const fd_set *
+get_fdset()
+{
+ return &allfds;
+}
+
+/*---------------------------------------------------------------*/
+
+int
+get_highestfd()
+{
+ return highestfd;
+}
--- /dev/null
+/*
+ * connection_list.h - connection list
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * Tabs at 4
+ */
+
+#ifndef CONNECTION_LIST_H__
+#define CONNECTION_LIST_H__
+
+#include <sys/select.h>
+
+/*****************************************************************
+ * Connection List Public Members
+ *****************************************************************/
+
+struct connection
+{
+ /* file descriptor */
+ int fd;
+
+ /* process incoming data on the connection */
+ void (* process)(int fd);
+};
+
+/* add a connection to the list */
+extern void add_connection(struct connection *p);
+
+/* find a connection in the list by file descriptor */
+extern struct connection *find_connection(int fd);
+
+/* get the number of connections in the list */
+extern int get_number_of_connections();
+
+/* get a specific connection by index from the list */
+extern struct connection *get_connection(int i);
+
+/* get an fd_set with all the fd's that have been added to the list */
+extern const fd_set *get_fdset();
+
+/* get the highest fd that was added to the list */
+extern int get_highestfd();
+
+#endif /* CONNECTION_LIST_H__ */
--- /dev/null
+#ifndef __LINUX_GENERIC_NETLINK_H
+#define __LINUX_GENERIC_NETLINK_H
+
+#include <linux/netlink.h>
+
+#define GENL_NAMSIZ 16 /* length of family name */
+
+#define GENL_MIN_ID NLMSG_MIN_TYPE
+#define GENL_MAX_ID 1023
+
+struct genlmsghdr {
+ __u8 cmd;
+ __u8 version;
+ __u16 reserved;
+};
+
+#define GENL_HDRLEN NLMSG_ALIGN(sizeof(struct genlmsghdr))
+
+#define GENL_ADMIN_PERM 0x01
+#define GENL_CMD_CAP_DO 0x02
+#define GENL_CMD_CAP_DUMP 0x04
+#define GENL_CMD_CAP_HASPOL 0x08
+
+/*
+ * List of reserved static generic netlink identifiers:
+ */
+#define GENL_ID_GENERATE 0
+#define GENL_ID_CTRL NLMSG_MIN_TYPE
+
+/**************************************************************************
+ * Controller
+ **************************************************************************/
+
+enum {
+ CTRL_CMD_UNSPEC,
+ CTRL_CMD_NEWFAMILY,
+ CTRL_CMD_DELFAMILY,
+ CTRL_CMD_GETFAMILY,
+ CTRL_CMD_NEWOPS,
+ CTRL_CMD_DELOPS,
+ CTRL_CMD_GETOPS,
+ CTRL_CMD_NEWMCAST_GRP,
+ CTRL_CMD_DELMCAST_GRP,
+ CTRL_CMD_GETMCAST_GRP, /* unused */
+ __CTRL_CMD_MAX,
+};
+
+#define CTRL_CMD_MAX (__CTRL_CMD_MAX - 1)
+
+enum {
+ CTRL_ATTR_UNSPEC,
+ CTRL_ATTR_FAMILY_ID,
+ CTRL_ATTR_FAMILY_NAME,
+ CTRL_ATTR_VERSION,
+ CTRL_ATTR_HDRSIZE,
+ CTRL_ATTR_MAXATTR,
+ CTRL_ATTR_OPS,
+ CTRL_ATTR_MCAST_GROUPS,
+ __CTRL_ATTR_MAX,
+};
+
+#define CTRL_ATTR_MAX (__CTRL_ATTR_MAX - 1)
+
+enum {
+ CTRL_ATTR_OP_UNSPEC,
+ CTRL_ATTR_OP_ID,
+ CTRL_ATTR_OP_FLAGS,
+ __CTRL_ATTR_OP_MAX,
+};
+
+#define CTRL_ATTR_OP_MAX (__CTRL_ATTR_OP_MAX - 1)
+
+enum {
+ CTRL_ATTR_MCAST_GRP_UNSPEC,
+ CTRL_ATTR_MCAST_GRP_NAME,
+ CTRL_ATTR_MCAST_GRP_ID,
+ __CTRL_ATTR_MCAST_GRP_MAX,
+};
+#define CTRL_ATTR_MCAST_GRP_MAX (__CTRL_ATTR_MCAST_GRP_MAX - 1)
+
+#endif /* __LINUX_GENERIC_NETLINK_H */
--- /dev/null
+/*
+ * input_layer - Kernel ACPI Event Input Layer Interface
+ *
+ * Handles the details of getting kernel ACPI events from the input
+ * layer (/dev/input/event*).
+ *
+ * Inspired by (and in some cases blatantly lifted from) Vojtech Pavlik's
+ * evtest.c.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <glob.h>
+
+/* local */
+#include "connection_list.h"
+#include "kacpimon.h"
+
+#define DIM(a) (sizeof(a) / sizeof(a[0]))
+
+#define INPUT_LAYER_FS "/dev/input/event*"
+
+/*-----------------------------------------------------------------*/
+/* called when an input layer event is received */
+void process_input(int fd)
+{
+ struct input_event event;
+ ssize_t nbytes;
+
+ nbytes = read(fd, &event, sizeof(event));
+
+ if (nbytes == 0) {
+ printf("Input layer connection closed.\n");
+ return;
+ }
+
+ if (nbytes < 0) {
+ /* if it's a signal, bail */
+ if (errno == EINTR)
+ return;
+
+ printf("Input layer read error: %s (%d)\n",
+ strerror(errno), errno);
+ return;
+ }
+
+ /* ??? Is it possible for a partial message to come across? */
+ /* If so, we've got more code to write... */
+
+ if (nbytes != sizeof(event)) {
+ printf("Input Layer unexpected Length\n");
+ printf(" Expected: %lu Got: %zd\n",
+ (unsigned long) sizeof(event), nbytes);
+ return;
+ }
+
+ /* If the Escape key was pressed, set the exitflag to exit. */
+ if (event.type == EV_KEY &&
+ event.code == KEY_ESC &&
+ event.value == 1) {
+ printf("Escape key pressed\n");
+ exitflag = 1;
+ }
+
+ if (event.type == EV_SYN)
+ printf("Input Layer: Sync\n");
+ else
+ /* format and display the event struct in decimal */
+ printf("Input Layer: "
+ "Type: %hu Code: %hu Value: %d\n",
+ event.type, event.code, event.value);
+}
+
+#define BITS_PER_LONG (sizeof(long) * 8)
+#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
+#define OFF(x) ((x)%BITS_PER_LONG)
+#define LONG(x) ((x)/BITS_PER_LONG)
+#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
+
+/*-----------------------------------------------------------------*
+ * open each of the appropriate /dev/input/event* files for input */
+void open_input(void)
+{
+ char *filename = NULL;
+ glob_t globbuf;
+ unsigned i;
+ int fd;
+ struct connection c;
+ int had_some_success = 0;
+ char evname[256];
+
+ /* get all the matching event filenames */
+ glob(INPUT_LAYER_FS, 0, NULL, &globbuf);
+
+ /* for each event file */
+ for (i = 0; i < globbuf.gl_pathc; ++i)
+ {
+ filename = globbuf.gl_pathv[i];
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (fd >= 0) {
+ /* get this event file's name */
+ strcpy(evname, "Unknown");
+ ioctl(fd, EVIOCGNAME(sizeof(evname)), evname);
+
+ printf("%s (%s) opened successfully\n", filename, evname);
+ had_some_success = 1;
+
+ /* add a connection to the list */
+ c.fd = fd;
+ c.process = process_input;
+ add_connection(&c);
+ }
+ else
+ {
+ if (had_some_success == 1)
+ continue;
+ int errno2 = errno;
+ printf("open for %s failed: %s (%d)\n",
+ filename, strerror(errno2), errno2);
+ if (errno2 == EACCES)
+ printf(" (try running as root)\n");
+ if (errno2 == ENOENT)
+ printf(" (input layer driver may not be present)\n");
+ }
+ }
+
+ globfree(&globbuf);
+}
+
--- /dev/null
+/*
+ * input_layer.h - Kernel ACPI Event Input Layer Interface
+ *
+ * Handles the details of getting kernel ACPI events from the input
+ * layer (/dev/input/event*).
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef INPUT_LAYER_H__
+#define INPUT_LAYER_H__
+
+/* open each of the appropriate /dev/input/event* files for input */
+extern void open_input(void);
+
+#endif /* INPUT_LAYER_H__ */
--- /dev/null
+.TH kacpimon 8 "Nov 2009"
+.\" Copyright (c) 2009 Ted Felix (www.tedfelix.com)
+.SH NAME
+kacpimon \- Kernel ACPI Event Monitor
+.SH SYNOPSIS
+\fBkacpimon\fP
+.SH DESCRIPTION
+\fBkacpimon\fP is a monitor program that connects to three sources of ACPI
+events:
+.IP
+1. /proc/acpi/event
+.br
+2. netlink
+.br
+3. the input layer (/dev/input/event*)
+.PP
+\fBkacpimon\fP then reports on what it sees while it is connected.
+.PP
+Use \fBkacpimon\fP to make sure your kernel is sending acpi-related events
+via the three sources mentioned above.
+.PP
+Since both \fBacpid\fP and \fBkacpimon\fP connect to the same sources, be sure
+to kill \fBacpid\fP before running kacpimon:
+.IP
+sudo killall acpid
+.PP
+Also be sure to run \fBkacpimon\fP as root or else it won't be able to open
+/proc/acpi/event or the input layer:
+.IP
+sudo ./kacpimon
+.PP
+If you want to pipe the output of \fBkacpimon\fP to a file, be sure to use
+the "Esc" key to exit rather than Ctrl-C. Using Ctrl-C will cause
+output to be lost.
+.SH FILES
+.TP
+.I /proc/acpi/event
+kernel ACPI event file
+.TP
+.I /dev/input/event*
+input layer event files
+.SH SEE ALSO
+.BR acpi_listen (8),
+.BR acpid (8)
+.SH AUTHOR
+Ted Felix <www.tedfelix.com>
+.SH TODO
+\- Make kacpimon something that actually gets installed?
+.SH LICENSE
+kacpimon \- Kernel ACPI Event Monitor
+.br
+Copyright (C) 2009 Ted Felix (www.tedfelix.com)
+.PP
+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.
+.PP
+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.
+.PP
+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
+Or visit www.gnu.org.
+
--- /dev/null
+/*
+ * kacpimon - Kernel ACPI Event Monitor
+ *
+ * Monitors kernel ACPI events from multiple interfaces and reports them
+ * to the console.
+ *
+ * Inspired by (and in some cases blatantly lifted from) Vojtech Pavlik's
+ * evtest.c, Zhang Rui's acpi_genl, and Alexey Kuznetsov's libnetlink.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/select.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+
+/* local */
+#include "libnetlink.h"
+#include "genetlink.h"
+#include "acpi_genetlink.h"
+
+#include "acpi_ids.h"
+#include "connection_list.h"
+#include "input_layer.h"
+#include "netlink.h"
+
+/* ??? Isn't this in a system header someplace? */
+#define max(a, b) (((a)>(b))?(a):(b))
+
+/*********************************************************************/
+
+/* Exit flag that can be set by any of the functions to cause the */
+/* program to exit. */
+int exitflag = 0;
+
+/****************************************************************
+ * Old /proc/acpi/event interface
+ ****************************************************************/
+
+void process_proc(int fd)
+{
+ const int buffsize = 1024;
+ char buffer[buffsize];
+ ssize_t nbytes;
+
+ for (;;)
+ {
+ nbytes = read(fd, buffer, buffsize - 1);
+
+ /* ??? Do we need to worry about partial messages? */
+
+ /* If there are no data to read, bail. */
+ if (nbytes <= 0)
+ break;
+
+ /* Ensure we have a zero terminator */
+ buffer[nbytes] = 0;
+
+ printf("/proc/acpi/event: %s", buffer);
+ }
+}
+
+// ---------------------------------------------------------------
+
+void open_proc(void)
+{
+ char *filename = "/proc/acpi/event";
+ int fd;
+ struct connection c;
+
+ fd = open(filename, O_RDONLY | O_NONBLOCK);
+ if (fd >= 0)
+ {
+ printf("%s opened successfully\n", filename);
+
+ /* Add a connection to the list. */
+ c.fd = fd;
+ c.process = process_proc;
+ add_connection(&c);
+ }
+ else
+ {
+ int errno2 = errno;
+ printf("open for %s: %s (%d)\n",
+ filename, strerror(errno2), errno2);
+ if (errno2 == EACCES)
+ printf(" (try running as root)\n");
+ if (errno2 == ENOENT)
+ printf(" (ACPI proc filesystem may not be present)\n");
+ if (errno2 == EBUSY)
+ printf(" (ACPI proc filesystem is in use. "
+ "You might need to kill acpid.)\n");
+ }
+}
+
+/****************************************************************
+ * Main Program Functions
+ ****************************************************************/
+
+void monitor(void)
+{
+ while (exitflag == 0)
+ {
+ fd_set readfds;
+ int nready;
+ int i;
+
+ /* It's going to get clobbered, so use a copy. */
+ readfds = *get_fdset();
+
+ /* Wait on data. */
+ nready = select(get_highestfd() + 1, &readfds, NULL, NULL, NULL);
+
+ /* If something goes wrong, bail. */
+ if (nready <= 0)
+ break;
+
+ /* For each connection */
+ for (i = 0; i <= get_number_of_connections(); ++i)
+ {
+ int fd;
+ struct connection *p;
+
+ p = get_connection(i);
+
+ /* If this connection is invalid, bail. */
+ if (!p)
+ break;
+
+ /* Get the file descriptor */
+ fd = p -> fd;
+
+ /* If this file descriptor has data waiting, */
+ if (FD_ISSET(fd, &readfds))
+ {
+ p->process(fd);
+ }
+ }
+ }
+}
+
+// ---------------------------------------------------------------
+
+void close_all()
+{
+ int i = 0;
+
+ /* For each connection */
+ for (i = 0; i <= get_number_of_connections(); ++i)
+ {
+ struct connection *p;
+
+ p = get_connection(i);
+
+ /* If this connection is invalid, try the next. */
+ if (p == 0)
+ continue;
+
+ close(p -> fd);
+ }
+}
+
+// ---------------------------------------------------------------
+
+int main(void)
+{
+ printf("Kernel ACPI Event Monitor...\n");
+
+ open_proc();
+ open_input();
+ open_netlink();
+
+ printf("Press Escape to exit, or Ctrl-C if that doesn't work.\n");
+
+ monitor();
+
+ printf("Closing files...\n");
+
+ close_all();
+
+ printf("Goodbye\n");
+
+ return 0;
+}
--- /dev/null
+/*
+ * kacpimon - Kernel ACPI Event Monitor
+ *
+ * Monitors kernel ACPI events from multiple interfaces and reports them
+ * to the console.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef KACPIMON_H__
+#define KACPIMON_H__
+
+/* ??? Encapsulate with accessor functions? */
+
+/* Exit flag that can be set by any of the functions to cause the */
+/* program to exit. */
+extern int exitflag;
+
+#endif /* KACPIMON_H__ */
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Modified by Ted Felix (www.tedfelix.com) to fix warnings.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+ int rcvbuf = 32768;
+
+ memset(rth, 0, sizeof(rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0,
+ (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ h = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(h, (unsigned)status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (junk) {
+ err = junk(&nladdr, h, arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ return 0;
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *msgerr = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -msgerr->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if (nladdr.nl_pid != (unsigned)peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *msgerr = (struct nlmsgerr*)NLMSG_DATA(h);
+ if ((unsigned)l < sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -msgerr->error;
+ if (errno == 0) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+ perror("RTNETLINK1 answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || (unsigned)len>sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + (int)NLMSG_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
--- /dev/null
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+// needed by netlink.h, should be in there
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
--- /dev/null
+# kacpimon makefile
+
+sources = acpi_ids.c kacpimon.c connection_list.c input_layer.c libnetlink.c \
+ netlink.c
+target = kacpimon
+# debug build
+#CFLAGS = -g -O0 -Wall -Wextra -Wundef -Wshadow -Werror
+# release build
+#CFLAGS = -O2 -Wall -Wextra -Wundef -Wshadow -Werror
+# hybrid build, optimized, but with debugging symbols (Debian-style)
+CFLAGS = -g -O2 -Wall -Wextra -Wundef -Wshadow -Werror
+
+objects := $(sources:.c=.o)
+
+# Default target
+$(target) : $(objects)
+
+depends := $(sources:.c=.d)
+
+# Dependency files can be made from .c files like this...
+# (the sed line adds the .d file itself as a target)
+%.d : %.c
+ @set -e; \
+ rm -f $@; \
+ $(CPP) -MM $(CPPFLAGS) $< > $@.$$$$; \
+ sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; \
+ rm -f $@.$$$$
+
+# Pull in all dependencies from the .d files.
+-include $(depends)
+
+SBIN = /usr/sbin
+install: $(target)
+ mkdir -p $(SBIN)
+ install -m 750 kacpimon $(SBIN)
+
+DISTTMP=/tmp
+DISTNAME=kacpimon
+FULLTMP = $(DISTTMP)/$(DISTNAME)
+dist:
+ rm -rf $(FULLTMP)
+ mkdir -p $(FULLTMP)
+ cp -a * $(FULLTMP)
+ find $(FULLTMP) -type d -name CVS | xargs rm -rf
+ make -C $(FULLTMP) clean
+ rm -f $(FULLTMP)/cscope.out
+ find $(FULLTMP) -name '*~' | xargs rm -f
+ # Get rid of previous dist
+ rm -f $(FULLTMP)/$(DISTNAME).tar.gz
+ tar -C $(DISTTMP) -zcvf $(DISTNAME).tar.gz $(DISTNAME)
+ rm -rf $(FULLTMP)
+
+.PHONY : clean
+clean :
+ rm -f $(depends) $(objects) $(target)
+
--- /dev/null
+/*
+ * netlink.c - Kernel ACPI Event Netlink Interface
+ *
+ * Handles the details of getting kernel ACPI events from netlink.
+ *
+ * Inspired by (and in some cases blatantly lifted from) Zhang Rui's
+ * acpi_genl and Alexey Kuznetsov's libnetlink. Thanks also to Yi Yang
+ * at intel.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+/* local */
+#include "libnetlink.h"
+#include "genetlink.h"
+#include "acpi_genetlink.h"
+
+#include "kacpimon.h"
+#include "acpi_ids.h"
+#include "connection_list.h"
+
+/* ??? Isn't this in a system header someplace? */
+#define max(a, b) (((a)>(b))?(a):(b))
+
+static void
+format_netlink(struct nlmsghdr *msg)
+{
+ struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr = NLMSG_DATA(msg);
+ int len;
+ struct rtattr *attrs;
+
+ len = msg->nlmsg_len;
+
+ /* if this message doesn't have the proper family ID, drop it */
+ if (msg->nlmsg_type != acpi_ids_getfamily()) {
+ printf("Netlink: Message received with improper family ID.\n");
+ return;
+ }
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+ if (len < 0) {
+ printf("Netlink: Wrong controller message len: %d\n", len);
+ return;
+ }
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ /* parse the attributes in this message */
+ parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len);
+
+ /* if there's an ACPI event attribute... */
+ if (tb[ACPI_GENL_ATTR_EVENT]) {
+ /* get the actual event struct */
+ struct acpi_genl_event *event =
+ RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]);
+ /* format it */
+ printf("netlink: %s %s %08x %08x\n",
+ event->device_class,
+ event->bus_id, event->type, event->data);
+ }
+}
+
+/* (based on rtnl_listen() in libnetlink.c) */
+void
+process_netlink(int fd)
+{
+ int status;
+ struct nlmsghdr *h;
+ /* the address for recvmsg() */
+ struct sockaddr_nl nladdr;
+ /* the io vector for recvmsg() */
+ struct iovec iov;
+ /* recvmsg() parameters */
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ /* buffer for the incoming data */
+ char buf[8192];
+
+ /* set up the netlink address */
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ /* set up the I/O vector */
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ /* read the data into the buffer */
+ status = recvmsg(fd, &msg, 0);
+
+ /* if there was a problem, print a message and keep trying */
+ if (status < 0) {
+ /* if we were interrupted by a signal, bail */
+ if (errno == EINTR)
+ return;
+ printf("Netlink: recvmsg() error: %s (%d)\n",
+ strerror(errno), errno);
+ return;
+ }
+ /* if an orderly shutdown has occurred, we're done */
+ if (status == 0) {
+ printf("Netlink: Connection closed\n");
+ return;
+ }
+ /* check to see if the address length has changed */
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ printf("Netlink: Unexpected address length: %d\n", msg.msg_namelen);
+ return;
+ }
+
+ /* for each message received */
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len > status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ printf("Netlink: Message truncated (1)\n");
+ return;
+ }
+ printf("Netlink: Malformed message. Length: %d\n", len);
+ return;
+ }
+
+ /* format the message */
+ format_netlink(h);
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ printf("Netlink: Message truncated (2)\n");
+ return;
+ }
+ if (status) {
+ printf("Netlink: Remnant of size %d\n", status);
+ return;
+ }
+
+ return;
+}
+
+/* convert the netlink multicast group number into a bit map */
+/* (e.g. 4 => 16, 5 => 32) */
+static __u32
+nl_mgrp(__u32 group)
+{
+ if (group > 31) {
+ printf("Netlink: Use setsockopt for this group: %d\n", group);
+ return 0;
+ }
+ return group ? (1 << (group - 1)) : 0;
+}
+
+void open_netlink(void)
+{
+ struct rtnl_handle rth;
+ struct connection c;
+
+ /* some debugging */
+ printf("Netlink ACPI Family ID: %hu\n", acpi_ids_getfamily());
+ printf("Netlink ACPI Multicast Group ID: %hu\n", acpi_ids_getgroup());
+
+ /* open the appropriate netlink socket for input */
+ if (rtnl_open_byproto(
+ &rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) {
+ printf("Netlink: Cannot open generic netlink socket\n");
+ return;
+ }
+
+ printf("netlink opened successfully\n");
+
+ /* add a connection to the list */
+ c.fd = rth.fd;
+ c.process = process_netlink;
+ add_connection(&c);
+}
--- /dev/null
+/*
+ * netlink.h - Kernel ACPI Event Netlink Interface
+ *
+ * Handles the details of getting kernel ACPI events from netlink.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef NETLINK_H__
+#define NETLINK_H__
+
+/* open the netlink connection */
+extern void open_netlink(void);
+
+#endif /* NETLINK_H__ */
--- /dev/null
+button/power PBTN 00000080 00000001
+button/power (input layer)
+button/lid LID 00000080 0000000f
+button/lid closed (input layer)
+button/lid LID 00000080 00000010
+button/lid open (input layer)
+button/sleep SBTN 00000080 00000007
+button/sleep (input layer)
+ac_adapter AC 00000080 00000000
+battery BAT0 00000080 00000001
+ac_adapter ACPI0003:00 00000080 00000000
+battery PNP0C0A:00 00000080 00000001
+ac_adapter AC 00000080 00000001
+battery BAT0 00000080 00000001
+ac_adapter ACPI0003:00 00000080 00000001
+battery PNP0C0A:00 00000080 00000001
--- /dev/null
+/*
+ * libnetlink.c RTnetlink service routines.
+ *
+ * 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.
+ *
+ * Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *
+ * Modified by Ted Felix (www.tedfelix.com) to fix warnings.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <net/if_arp.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/uio.h>
+
+#include "libnetlink.h"
+
+void rtnl_close(struct rtnl_handle *rth)
+{
+ if (rth->fd >= 0) {
+ close(rth->fd);
+ rth->fd = -1;
+ }
+}
+
+int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions,
+ int protocol)
+{
+ socklen_t addr_len;
+ int sndbuf = 32768;
+ int rcvbuf = 32768;
+
+ memset(rth, 0, sizeof(rth));
+
+ rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
+ if (rth->fd < 0) {
+ perror("Cannot open netlink socket");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf)) < 0) {
+ perror("SO_SNDBUF");
+ return -1;
+ }
+
+ if (setsockopt(rth->fd,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf)) < 0) {
+ perror("SO_RCVBUF");
+ return -1;
+ }
+
+ memset(&rth->local, 0, sizeof(rth->local));
+ rth->local.nl_family = AF_NETLINK;
+ rth->local.nl_groups = subscriptions;
+
+ if (bind(rth->fd, (struct sockaddr*)&rth->local, sizeof(rth->local)) < 0) {
+ perror("Cannot bind netlink socket");
+ return -1;
+ }
+ addr_len = sizeof(rth->local);
+ if (getsockname(rth->fd, (struct sockaddr*)&rth->local, &addr_len) < 0) {
+ perror("Cannot getsockname");
+ return -1;
+ }
+ if (addr_len != sizeof(rth->local)) {
+ fprintf(stderr, "Wrong address length %d\n", addr_len);
+ return -1;
+ }
+ if (rth->local.nl_family != AF_NETLINK) {
+ fprintf(stderr, "Wrong address family %d\n", rth->local.nl_family);
+ return -1;
+ }
+ rth->seq = time(NULL);
+ return 0;
+}
+
+int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions)
+{
+ return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE);
+}
+
+int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct rtgenmsg g;
+ } req;
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ memset(&req, 0, sizeof(req));
+ req.nlh.nlmsg_len = sizeof(req);
+ req.nlh.nlmsg_type = type;
+ req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ req.nlh.nlmsg_pid = 0;
+ req.nlh.nlmsg_seq = rth->dump = ++rth->seq;
+ req.g.rtgen_family = family;
+
+ return sendto(rth->fd, (void*)&req, sizeof(req), 0,
+ (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_send(struct rtnl_handle *rth, const char *buf, int len)
+{
+ struct sockaddr_nl nladdr;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ return sendto(rth->fd, buf, len, 0, (struct sockaddr*)&nladdr, sizeof(nladdr));
+}
+
+int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len)
+{
+ struct nlmsghdr nlh;
+ struct sockaddr_nl nladdr;
+ struct iovec iov[2] = {
+ { .iov_base = &nlh, .iov_len = sizeof(nlh) },
+ { .iov_base = req, .iov_len = len }
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ };
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+
+ nlh.nlmsg_len = NLMSG_LENGTH(len);
+ nlh.nlmsg_type = type;
+ nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST;
+ nlh.nlmsg_pid = 0;
+ nlh.nlmsg_seq = rth->dump = ++rth->seq;
+
+ return sendmsg(rth->fd, &msg, 0);
+}
+
+int rtnl_dump_filter(struct rtnl_handle *rth,
+ rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2)
+{
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ iov.iov_base = buf;
+ while (1) {
+ int status;
+ struct nlmsghdr *h;
+
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rth->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+
+ h = (struct nlmsghdr*)buf;
+ while (NLMSG_OK(h, (unsigned)status)) {
+ int err;
+
+ if (nladdr.nl_pid != 0 ||
+ h->nlmsg_pid != rth->local.nl_pid ||
+ h->nlmsg_seq != rth->dump) {
+ if (junk) {
+ err = junk(&nladdr, h, arg2);
+ if (err < 0)
+ return err;
+ }
+ goto skip_it;
+ }
+
+ if (h->nlmsg_type == NLMSG_DONE)
+ return 0;
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *msgerr = (struct nlmsgerr*)NLMSG_DATA(h);
+ if (h->nlmsg_len < NLMSG_LENGTH(sizeof(struct nlmsgerr))) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -msgerr->error;
+ perror("RTNETLINK answers");
+ }
+ return -1;
+ }
+ err = filter(&nladdr, h, arg1);
+ if (err < 0)
+ return err;
+
+skip_it:
+ h = NLMSG_NEXT(h, status);
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg)
+{
+ int status;
+ unsigned seq;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov = {
+ .iov_base = (void*) n,
+ .iov_len = n->nlmsg_len
+ };
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[16384];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = peer;
+ nladdr.nl_groups = groups;
+
+ n->nlmsg_seq = seq = ++rtnl->seq;
+
+ if (answer == NULL)
+ n->nlmsg_flags |= NLM_F_ACK;
+
+ status = sendmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ perror("Cannot talk to rtnetlink");
+ return -1;
+ }
+
+ memset(buf,0,sizeof(buf));
+
+ iov.iov_base = buf;
+
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ if (nladdr.nl_pid != (unsigned)peer ||
+ h->nlmsg_pid != rtnl->local.nl_pid ||
+ h->nlmsg_seq != seq) {
+ if (junk) {
+ err = junk(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+ /* Don't forget to skip that message. */
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ continue;
+ }
+
+ if (h->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *msgerr = (struct nlmsgerr*)NLMSG_DATA(h);
+ if ((unsigned)l < sizeof(struct nlmsgerr)) {
+ fprintf(stderr, "ERROR truncated\n");
+ } else {
+ errno = -msgerr->error;
+ if (errno == 0) {
+ if (answer)
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+ perror("RTNETLINK1 answers");
+ }
+ return -1;
+ }
+ if (answer) {
+ memcpy(answer, h, h->nlmsg_len);
+ return 0;
+ }
+
+ fprintf(stderr, "Unexpected reply!!!\n");
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_listen(struct rtnl_handle *rtnl,
+ rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct nlmsghdr *h;
+ struct sockaddr_nl nladdr;
+ struct iovec iov;
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ char buf[8192];
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ iov.iov_base = buf;
+ while (1) {
+ iov.iov_len = sizeof(buf);
+ status = recvmsg(rtnl->fd, &msg, 0);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("OVERRUN");
+ continue;
+ }
+ if (status == 0) {
+ fprintf(stderr, "EOF on netlink\n");
+ return -1;
+ }
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ fprintf(stderr, "Sender address length == %d\n", msg.msg_namelen);
+ exit(1);
+ }
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int err;
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l<0 || len>status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Truncated message\n");
+ return -1;
+ }
+ fprintf(stderr, "!!!malformed message: len=%d\n", len);
+ exit(1);
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ fprintf(stderr, "Message truncated\n");
+ continue;
+ }
+ if (status) {
+ fprintf(stderr, "!!!Remnant of size %d\n", status);
+ exit(1);
+ }
+ }
+}
+
+int rtnl_from_file(FILE *rtnl, rtnl_filter_t handler,
+ void *jarg)
+{
+ int status;
+ struct sockaddr_nl nladdr;
+ char buf[8192];
+ struct nlmsghdr *h = (void*)buf;
+
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ while (1) {
+ int err, len;
+ int l;
+
+ status = fread(&buf, 1, sizeof(*h), rtnl);
+
+ if (status < 0) {
+ if (errno == EINTR)
+ continue;
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status == 0)
+ return 0;
+
+ len = h->nlmsg_len;
+ l = len - sizeof(*h);
+
+ if (l<0 || (unsigned)len>sizeof(buf)) {
+ fprintf(stderr, "!!!malformed message: len=%d @%lu\n",
+ len, ftell(rtnl));
+ return -1;
+ }
+
+ status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl);
+
+ if (status < 0) {
+ perror("rtnl_from_file: fread");
+ return -1;
+ }
+ if (status < l) {
+ fprintf(stderr, "rtnl-from_file: truncated message\n");
+ return -1;
+ }
+
+ err = handler(&nladdr, h, jarg);
+ if (err < 0)
+ return err;
+ }
+}
+
+int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *rta;
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) {
+ fprintf(stderr,"addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), &data, 4);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
+ return 0;
+}
+
+int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data,
+ int alen)
+{
+ int len = RTA_LENGTH(alen);
+ struct rtattr *rta;
+
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addattr_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+ rta = NLMSG_TAIL(n);
+ rta->rta_type = type;
+ rta->rta_len = len;
+ memcpy(RTA_DATA(rta), data, alen);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len)
+{
+ if ((int)NLMSG_ALIGN(n->nlmsg_len) + (int)NLMSG_ALIGN(len) > maxlen) {
+ fprintf(stderr, "addraw_l ERROR: message exceeded bound of %d\n",maxlen);
+ return -1;
+ }
+
+ memcpy(NLMSG_TAIL(n), data, len);
+ memset((void *) NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len);
+ n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len);
+ return 0;
+}
+
+int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data)
+{
+ int len = RTA_LENGTH(4);
+ struct rtattr *subrta;
+
+ if (RTA_ALIGN(rta->rta_len) + len > maxlen) {
+ fprintf(stderr,"rta_addattr32: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), &data, 4);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len;
+ return 0;
+}
+
+int rta_addattr_l(struct rtattr *rta, int maxlen, int type,
+ const void *data, int alen)
+{
+ struct rtattr *subrta;
+ int len = RTA_LENGTH(alen);
+
+ if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
+ fprintf(stderr,"rta_addattr_l: Error! max allowed bound %d exceeded\n",maxlen);
+ return -1;
+ }
+ subrta = (struct rtattr*)(((char*)rta) + RTA_ALIGN(rta->rta_len));
+ subrta->rta_type = type;
+ subrta->rta_len = len;
+ memcpy(RTA_DATA(subrta), data, alen);
+ rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
+ return 0;
+}
+
+int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max)
+ tb[rta->rta_type] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return 0;
+}
+
+int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+ int i = 0;
+
+ memset(tb, 0, sizeof(struct rtattr *) * max);
+ while (RTA_OK(rta, len)) {
+ if (rta->rta_type <= max && i < max)
+ tb[i++] = rta;
+ rta = RTA_NEXT(rta,len);
+ }
+ if (len)
+ fprintf(stderr, "!!!Deficit %d, rta_len=%d\n", len, rta->rta_len);
+ return i;
+}
--- /dev/null
+#ifndef __LIBNETLINK_H__
+#define __LIBNETLINK_H__ 1
+
+#include <asm/types.h>
+/* needed by netlink.h, should be in there */
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+struct rtnl_handle
+{
+ int fd;
+ struct sockaddr_nl local;
+ struct sockaddr_nl peer;
+ __u32 seq;
+ __u32 dump;
+};
+
+extern int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions);
+extern int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, int protocol);
+extern void rtnl_close(struct rtnl_handle *rth);
+extern int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type);
+extern int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len);
+
+typedef int (*rtnl_filter_t)(const struct sockaddr_nl *,
+ struct nlmsghdr *n, void *);
+extern int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter,
+ void *arg1,
+ rtnl_filter_t junk,
+ void *arg2);
+extern int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer,
+ unsigned groups, struct nlmsghdr *answer,
+ rtnl_filter_t junk,
+ void *jarg);
+extern int rtnl_send(struct rtnl_handle *rth, const char *buf, int);
+
+
+extern int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data);
+extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, int alen);
+extern int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len);
+extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data);
+extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, const void *data, int alen);
+
+extern int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+extern int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, int len);
+
+#define parse_rtattr_nested(tb, max, rta) \
+ (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta)))
+
+extern int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler,
+ void *jarg);
+extern int rtnl_from_file(FILE *, rtnl_filter_t handler,
+ void *jarg);
+
+#define NLMSG_TAIL(nmsg) \
+ ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+#ifndef IFA_RTA
+#define IFA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifaddrmsg))))
+#endif
+#ifndef IFA_PAYLOAD
+#define IFA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifaddrmsg))
+#endif
+
+#ifndef IFLA_RTA
+#define IFLA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg))))
+#endif
+#ifndef IFLA_PAYLOAD
+#define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg))
+#endif
+
+#ifndef NDA_RTA
+#define NDA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndmsg))))
+#endif
+#ifndef NDA_PAYLOAD
+#define NDA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndmsg))
+#endif
+
+#ifndef NDTA_RTA
+#define NDTA_RTA(r) \
+ ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ndtmsg))))
+#endif
+#ifndef NDTA_PAYLOAD
+#define NDTA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ndtmsg))
+#endif
+
+#endif /* __LIBNETLINK_H__ */
+
--- /dev/null
+/*
+ * log.c - ACPI daemon logging
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+#include "log.h"
+
+int log_debug_to_stderr = 0;
+
+int
+#ifdef __GNUC__
+__attribute__((format(printf, 2, 3)))
+#endif
+acpid_log(int level, const char *fmt, ...)
+{
+ va_list args;
+
+ if (level == LOG_DEBUG) {
+ /* if "-d" has been specified */
+ if (log_debug_to_stderr) {
+ /* send debug output to stderr */
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+
+ fprintf(stderr, "\n");
+ }
+ } else {
+ va_start(args, fmt);
+ vsyslog(level, fmt, args);
+ va_end(args);
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * log.h - ACPI daemon logging
+ *
+ * Copyright (C) 1999-2000 Andrew Henroid
+ * Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * 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
+ */
+
+#ifndef LOG_H__
+#define LOG_H__
+
+/* for LOG_ERR, LOG_DEBUG, LOG_INFO, etc... */
+#include <syslog.h>
+
+/*
+ * Set to 1 to send LOG_DEBUG logging to stderr, zero to ignore LOG_DEBUG
+ * logging. Default is zero.
+ */
+extern int log_debug_to_stderr;
+
+extern int acpid_log(int level, const char *fmt, ...);
+
+#endif /* LOG_H__ */
--- /dev/null
+/*
+ * netlink.c - Kernel ACPI Event Netlink Interface
+ *
+ * Handles the details of getting kernel ACPI events from netlink.
+ *
+ * Inspired by (and in some cases blatantly lifted from) Zhang Rui's
+ * acpi_genl and Alexey Kuznetsov's libnetlink. Thanks also to Yi Yang
+ * at intel.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+/* system */
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+/* local */
+#include "acpid.h"
+#include "log.h"
+#include "event.h"
+
+#include "libnetlink.h"
+#include "genetlink.h"
+#include "acpi_genetlink.h"
+
+#include "acpi_ids.h"
+#include "connection_list.h"
+
+static void
+format_netlink(struct nlmsghdr *msg)
+{
+ struct rtattr *tb[ACPI_GENL_ATTR_MAX + 1];
+ struct genlmsghdr *ghdr = NLMSG_DATA(msg);
+ int len;
+ struct rtattr *attrs;
+
+ len = msg->nlmsg_len;
+
+ /* if this message doesn't have the proper family ID, drop it */
+ if (msg->nlmsg_type != acpi_ids_getfamily()) {
+ if (logevents) {
+ acpid_log(LOG_INFO, "wrong netlink family ID.");
+ }
+ return;
+ }
+
+ len -= NLMSG_LENGTH(GENL_HDRLEN);
+
+ if (len < 0) {
+ acpid_log(LOG_WARNING,
+ "wrong netlink controller message len: %d", len);
+ return;
+ }
+
+ attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
+ /* parse the attributes in this message */
+ parse_rtattr(tb, ACPI_GENL_ATTR_MAX, attrs, len);
+
+ /* if there's an ACPI event attribute... */
+ if (tb[ACPI_GENL_ATTR_EVENT]) {
+ /* get the actual event struct */
+ struct acpi_genl_event *event =
+ RTA_DATA(tb[ACPI_GENL_ATTR_EVENT]);
+ char buf[64];
+
+ /* format it */
+ snprintf(buf, sizeof(buf), "%s %s %08x %08x",
+ event->device_class, event->bus_id, event->type, event->data);
+
+ /* if we're locked, don't process the event */
+ if (locked()) {
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "lockfile present, not processing "
+ "netlink event \"%s\"", buf);
+ }
+ return;
+ }
+
+ if (logevents)
+ acpid_log(LOG_INFO,
+ "received netlink event \"%s\"", buf);
+
+ /* send the event off to the handler */
+ acpid_handle_event(buf);
+
+ if (logevents)
+ acpid_log(LOG_INFO,
+ "completed netlink event \"%s\"", buf);
+ }
+}
+
+/* (based on rtnl_listen() in libnetlink.c) */
+void
+process_netlink(int fd)
+{
+ int status;
+ struct nlmsghdr *h;
+ /* the address for recvmsg() */
+ struct sockaddr_nl nladdr;
+ /* the io vector for recvmsg() */
+ struct iovec iov;
+ /* recvmsg() parameters */
+ struct msghdr msg = {
+ .msg_name = &nladdr,
+ .msg_namelen = sizeof(nladdr),
+ .msg_iov = &iov,
+ .msg_iovlen = 1,
+ };
+ /* buffer for the incoming data */
+ char buf[8192];
+ static int nerrs;
+
+ /* set up the netlink address */
+ memset(&nladdr, 0, sizeof(nladdr));
+ nladdr.nl_family = AF_NETLINK;
+ nladdr.nl_pid = 0;
+ nladdr.nl_groups = 0;
+
+ /* set up the I/O vector */
+ iov.iov_base = buf;
+ iov.iov_len = sizeof(buf);
+
+ /* read the data into the buffer */
+ status = recvmsg(fd, &msg, 0);
+
+ /* if there was a problem, print a message and keep trying */
+ if (status < 0) {
+ /* if we were interrupted by a signal, bail */
+ if (errno == EINTR)
+ return;
+
+ acpid_log(LOG_ERR, "netlink read error: %s (%d)",
+ strerror(errno), errno);
+ if (++nerrs >= ACPID_MAX_ERRS) {
+ acpid_log(LOG_ERR,
+ "too many errors reading via "
+ "netlink - aborting");
+ exit(EXIT_FAILURE);
+ }
+ return;
+ }
+ /* if an orderly shutdown has occurred, we're done */
+ if (status == 0) {
+ acpid_log(LOG_WARNING, "netlink connection closed");
+ exit(EXIT_FAILURE);
+ }
+ /* check to see if the address length has changed */
+ if (msg.msg_namelen != sizeof(nladdr)) {
+ acpid_log(LOG_WARNING, "netlink unexpected length: "
+ "%d expected: %d", msg.msg_namelen, sizeof(nladdr));
+ return;
+ }
+
+ /* for each message received */
+ for (h = (struct nlmsghdr*)buf; (unsigned)status >= sizeof(*h); ) {
+ int len = h->nlmsg_len;
+ int l = len - sizeof(*h);
+
+ if (l < 0 || len > status) {
+ if (msg.msg_flags & MSG_TRUNC) {
+ acpid_log(LOG_WARNING, "netlink msg truncated (1)");
+ return;
+ }
+ acpid_log(LOG_WARNING,
+ "malformed netlink msg, length %d", len);
+ return;
+ }
+
+ /* format the message */
+ format_netlink(h);
+
+ status -= NLMSG_ALIGN(len);
+ h = (struct nlmsghdr*)((char*)h + NLMSG_ALIGN(len));
+ }
+ if (msg.msg_flags & MSG_TRUNC) {
+ acpid_log(LOG_WARNING, "netlink msg truncated (2)");
+ return;
+ }
+ if (status) {
+ acpid_log(LOG_WARNING, "netlink remnant of size %d", status);
+ return;
+ }
+
+ return;
+}
+
+/* convert the netlink multicast group number into a bit map */
+/* (e.g. 4 => 16, 5 => 32) */
+static __u32
+nl_mgrp(__u32 group)
+{
+ if (group > 31) {
+ acpid_log(LOG_ERR, "Unexpected group number %d", group);
+ return 0;
+ }
+ return group ? (1 << (group - 1)) : 0;
+}
+
+void open_netlink(void)
+{
+ struct rtnl_handle rth;
+ struct connection c;
+
+ /* open the appropriate netlink socket for input */
+ if (rtnl_open_byproto(
+ &rth, nl_mgrp(acpi_ids_getgroup()), NETLINK_GENERIC) < 0) {
+ acpid_log(LOG_ERR, "cannot open generic netlink socket");
+ return;
+ }
+
+ acpid_log(LOG_DEBUG, "netlink opened successfully");
+
+ /* add a connection to the list */
+ c.fd = rth.fd;
+ c.process = process_netlink;
+ c.pathname = NULL;
+ c.kybd = 0;
+ add_connection(&c);
+}
+
--- /dev/null
+/*
+ * netlink.h - Kernel ACPI Event Netlink Interface
+ *
+ * Handles the details of getting kernel ACPI events from netlink.
+ *
+ * Copyright (C) 2008, Ted Felix (www.tedfelix.com)
+ *
+ * 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
+ *
+ * (tabs at 4)
+ */
+
+#ifndef NETLINK_H__
+#define NETLINK_H__
+
+/* open the netlink connection */
+extern void open_netlink(void);
+
+#endif /* NETLINK_H__ */
--- /dev/null
+diff -uprN acpid-2.0.9/Makefile acpid-2.0.9-new/Makefile
+--- acpid-2.0.9/Makefile 2011-03-12 08:06:19.000000000 -0800
++++ acpid-2.0.9-new/Makefile 2011-05-25 15:58:12.990292018 -0700
+@@ -31,9 +31,9 @@ MAN8GZ = $(MAN8:.8=.8.gz)
+
+ DOCS = COPYING Changelog README TESTPLAN TODO
+
+-CFLAGS = -W -Wall -Werror -Wundef -Wshadow -D_GNU_SOURCE $(OPT) \
+- -fno-strict-aliasing -g $(DEFS)
+-DEFS = -DVERSION="\"$(VERSION)\""
++CFLAGS = -W -Wall -Werror -Wundef -Wshadow -O2 -g $(DEFS) $(RPM_OPT_FLAGS) -fPIE
++LDFLAGS = -pie -Wl,-z,relro
++DEFS = -DVERSION="\"$(VERSION)\"" -D_GNU_SOURCE
+
+ all: $(PROGS)
+
--- /dev/null
+#!/bin/sh
+
+/usr/sbin/acpid &
+/etc/acpi/actions/start.sh
--- /dev/null
+# /etc/acpi/events/battery
+# Called when AC power goes away and we switch to battery
+
+event=battery.*
+action=/etc/acpi/actions/battery.sh
--- /dev/null
+#!/bin/sh
+
+. /etc/sysconfig/acpi
+. /usr/share/acpi/power-funcs
+
+if [ x$1 = xstop ] ; then
+ # When stop, we go to the AC state regardless of the actual power state.
+ RUN_SCRIPT_DIR=/etc/acpi/ac.d
+
+ # However, if we have a stored power state, and that power state is already
+ # AC, then we don't need to do anything, and we exit immediately.
+ if [ -f "$POWERSTATE" ]; then
+ OLDSTATE=$(cat $POWERSTATE)
+ if [ "$OLDSTATE" = "AC" ] ; then
+ exit 0
+ fi
+ fi
+else
+ # Get the power state (AC/BATTERY) into STATE
+ getState;
+
+ # Compare the power state with a stored state and exit if the state is the
+ # same. If not, then store the power state for comparison the next time
+ # around.
+ checkStateChanged;
+
+ if [ "$STATE" = "BATTERY" ] ; then
+ RUN_SCRIPT_DIR=/etc/acpi/battery.d
+ else
+ RUN_SCRIPT_DIR=/etc/acpi/ac.d
+ fi
+fi
+
+for SCRIPT in $RUN_SCRIPT_DIR/*.sh; do
+ if [ -f $SCRIPT ] ; then
+ . $SCRIPT
+ fi
+done
+
--- /dev/null
+# ACPID config to suspend machine if lidbutton is pressed, but only if
+# no gnome-power-manager is running
+
+event=button/lid.*
+action=/etc/acpi/actions/lid.sh
--- /dev/null
+#!/bin/sh
+
+PATH=/sbin:/bin:/usr/bin
+
+grep -q open /proc/acpi/button/lid/*/state
+if [ $? == 0 ]; then
+ exit
+fi
+if [ "`who -r | awk '{print $2}'`" == "0" ]; then
+ exit
+fi
+
+# Get the ID of the first active X11 session:
+uid_session=$(
+ck-list-sessions | \
+awk '
+/^Session[0-9]+:$/ { uid = active = x11 = "" ; next }
+{ gsub(/'\''/, "", $3) }
+$1 == "unix-user" { uid = $3 }
+$1 == "active" { active = $3 }
+$1 == "x11-display" { x11 = $3 }
+active == "TRUE" && x11 != "" {
+ print uid
+ exit
+}')
+
+# Check that there is a power manager, otherwise shut down.
+[ "$uid_session" ] &&
+ps axo uid,cmd | \
+awk '
+ $1 == '$uid_session' &&
+ ($2 == "gnome-power-manager" || $2 == "kpowersave" || $2 == "xfce4-power-manager" || $2 ~ /dawati-power-icon/ ) \
+ { found = 1; exit }
+ END { exit !found }
+' ||
+ echo "mem" > /sys/power/state
+
--- /dev/null
+# ACPID config to power down machine if powerbutton is pressed, but only if
+# no gnome-power-manager is running
+
+event=button/power.*
+action=/etc/acpi/actions/power.sh
--- /dev/null
+#!/bin/sh
+
+PATH=/sbin:/bin:/usr/bin
+
+# Get the ID of the first active X11 session:
+uid_session=$(
+ck-list-sessions | \
+awk '
+/^Session[0-9]+:$/ { uid = active = x11 = "" ; next }
+{ gsub(/'\''/, "", $3) }
+$1 == "unix-user" { uid = $3 }
+$1 == "active" { active = $3 }
+$1 == "x11-display" { x11 = $3 }
+active == "TRUE" && x11 != "" {
+ print uid
+ exit
+}')
+
+# Check that there is a power manager, otherwise shut down.
+[ "$uid_session" ] &&
+ps axo uid,cmd | \
+awk '
+ $1 == '$uid_session' &&
+ ($2 == "gnome-power-manager" || $2 == "kpowersave" || $2 == "xfce4-power-manager" || $2 ~ /dawati-power-icon/ ) \
+ { found = 1; exit }
+ END { exit !found }
+' ||
+ shutdown -h now
+
--- /dev/null
+[Unit]
+Description=ACPI event daemon
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/acpid
+ExecStart=/usr/sbin/acpid $OPTIONS
+
+[Install]
+WantedBy=multi-user.target
--- /dev/null
+Name: acpid
+Version: 2.0.14
+Release: 1
+License: GPLv2+ and BSD
+Summary: ACPI Event Daemon
+Group: System/Daemons
+Source: http://tedfelix.com/linux/acpid-%{version}.tar.gz
+Source2: acpid.video.conf
+Source3: acpid.power.conf
+Source4: acpid.power.sh
+Source5: acpid.lid.conf
+Source6: acpid.lid.sh
+Source7: acpid.battery.sh
+Source8: acpid.battery.conf
+Source9: acpid.ac.conf
+Source13: acpid-start-script
+Source14: acpid.start.sh
+Source15: acpid.service
+Source16: acpid
+# >> gbp-patch-tags # auto-added by gbp
+Patch1: acpid-2.0.9-makefile.patch
+# << gbp-patch-tags # auto-added by gbp
+
+Url: http://tedfelix.com/linux/acpid-netlink.html
+ExclusiveArch: ia64 x86_64 %{ix86}
+
+%description
+acpid is a daemon that dispatches ACPI events to user-space programs.
+
+%package extra-docs
+Summary: sample docs and sample scripts for apcid
+Group: Documentation
+Requires: %{name} = %{version}
+
+%description extra-docs
+Extra sample docs and scripts for acpid.
+
+%prep
+%setup -q
+# >> gbp-apply-patches # auto-added by gbp
+%patch1 -p1 -b .makefile
+# << gbp-apply-patches # auto-added by gbp
+
+%build
+make %{?_smp_mflags}
+
+
+%install
+make install DESTDIR=%{buildroot} DOCDIR=%{_docdir}/%{name}
+
+mkdir -p %{buildroot}%{_sysconfdir}/acpi/events
+mkdir -p %{buildroot}%{_sysconfdir}/acpi/actions
+mkdir -p %{buildroot}%{_datadir}/acpi
+chmod 755 %{buildroot}%{_sysconfdir}/acpi/events
+mkdir -p %{buildroot}%{_sysconfdir}/acpi/ac.d
+mkdir -p %{buildroot}%{_sysconfdir}/acpi/battery.d
+mkdir -p %{buildroot}%{_sysconfdir}/acpi/start.d
+mkdir -p %{buildroot}%{_sysconfdir}/pm/sleep.d
+mkdir -p %{buildroot}%{_localstatedir}/lib/acpi-support
+mkdir -p %{buildroot}/%{_lib}/systemd/system
+install -m 644 %{SOURCE2} %{buildroot}%{_sysconfdir}/acpi/events/videoconf
+install -m 644 %{SOURCE3} %{buildroot}%{_sysconfdir}/acpi/events/powerconf
+install -m 755 %{SOURCE4} %{buildroot}%{_sysconfdir}/acpi/actions/power.sh
+install -m 644 %{SOURCE5} %{buildroot}%{_sysconfdir}/acpi/events/lidconf
+install -m 755 %{SOURCE6} %{buildroot}%{_sysconfdir}/acpi/actions/lid.sh
+install -m 755 %{SOURCE7} %{buildroot}%{_sysconfdir}/acpi/actions/battery.sh
+install -m 644 %{SOURCE8} %{buildroot}%{_sysconfdir}/acpi/events/batteryconf
+install -m 644 %{SOURCE9} %{buildroot}%{_sysconfdir}/acpi/events/acconf
+install -m 755 %{SOURCE13} %{buildroot}%{_sbindir}/acpid-start-script
+install -m 755 %{SOURCE14} %{buildroot}%{_sysconfdir}/acpi/actions/start.sh
+install -D -m 644 %{SOURCE15} %{buildroot}/%{_lib}/systemd/system/acpid.service
+install -D -m 644 %{SOURCE16} %{buildroot}%{_sysconfdir}/sysconfig/acpid
+
+mkdir -p %{buildroot}/%{_lib}/systemd/system/sysinit.target.wants
+ln -s ../acpid.service %{buildroot}/%{_lib}/systemd/system/sysinit.target.wants/acpid.service
+
+
+%files
+%dir %{_sysconfdir}/acpi
+%dir %{_sysconfdir}/acpi/events
+%dir %{_sysconfdir}/acpi/actions
+%dir %{_sysconfdir}/acpi/ac.d
+%dir %{_sysconfdir}/acpi/battery.d
+%dir %{_localstatedir}/lib/acpi-support
+/%{_lib}/systemd/system/acpid.service
+/%{_lib}/systemd/system/sysinit.target.wants/acpid.service
+%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/acpi/events/videoconf
+%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/acpi/events/powerconf
+%config(noreplace) %attr(0755,root,root) %{_sysconfdir}/acpi/actions/power.sh
+%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/acpi/events/lidconf
+%config(noreplace) %attr(0755,root,root) %{_sysconfdir}/acpi/actions/lid.sh
+%config(noreplace) %attr(0755,root,root) %{_sysconfdir}/acpi/actions/battery.sh
+%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/acpi/events/batteryconf
+%config(noreplace) %attr(0644,root,root) %{_sysconfdir}/acpi/events/acconf
+%config(noreplace) %attr(0755,root,root) %{_sysconfdir}/acpi/actions/start.sh
+%config(noreplace) %attr(0755,root,root) %{_sysconfdir}/sysconfig/acpid
+
+%{_bindir}/acpi_listen
+%{_sbindir}/acpid
+%{_sbindir}/acpid-start-script
+%{_mandir}/man8/acpid.8.gz
+%{_mandir}/man8/acpi_listen.8.gz
+
+%files extra-docs
+%defattr(-,root,root)
+%doc %{_defaultdocdir}/acpid/COPYING
+%doc %{_defaultdocdir}/acpid/Changelog
+%doc %{_defaultdocdir}/acpid/README
+%doc %{_defaultdocdir}/acpid/TESTPLAN
+%doc %{_defaultdocdir}/acpid/TODO
+%doc %{_defaultdocdir}/acpid/samples/acpi_handler-conf
+%doc %{_defaultdocdir}/acpid/samples/acpi_handler.sh
+%doc %{_defaultdocdir}/acpid/samples/battery/battery-conf
+%doc %{_defaultdocdir}/acpid/samples/battery/battery.sh
+%doc %{_defaultdocdir}/acpid/samples/panasonic/ac_adapt.pl
+%doc %{_defaultdocdir}/acpid/samples/panasonic/ac_adapter
+%doc %{_defaultdocdir}/acpid/samples/panasonic/hotkey
+%doc %{_defaultdocdir}/acpid/samples/panasonic/hotkey.pl
+%doc %{_defaultdocdir}/acpid/samples/power
+%doc %{_defaultdocdir}/acpid/samples/power.sh
+%doc %{_defaultdocdir}/acpid/samples/powerbtn/powerbtn-conf
+%doc %{_defaultdocdir}/acpid/samples/powerbtn/powerbtn.sh
+%doc %{_defaultdocdir}/acpid/samples/powerbtn/powerbtn.sh.old
+%doc %{_defaultdocdir}/acpid/COPYING
--- /dev/null
+#!/bin/sh
+
+for SCRIPT in /etc/acpi/start.d/*.sh; do
+ if [ -x $SCRIPT ] ; then
+ . $SCRIPT
+ fi
+done
--- /dev/null
+# Configuration to turn on DPMS again on video activity, needed for some
+# laptops. Disabled by default, uncomment if your laptop display stays blank
+# after you close and open the lid.
+
+#event=video.*
+#action=/usr/sbin/vbetool dpms on
--- /dev/null
+/*
+ * proc.c - ACPI daemon proc filesystem interface
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "acpid.h"
+#include "log.h"
+#include "event.h"
+#include "connection_list.h"
+
+const char *eventfile = ACPID_EVENTFILE;
+
+static char *read_line(int fd);
+
+static void
+process_proc(int fd)
+{
+ char *event;
+
+ /* read an event */
+ event = read_line(fd);
+
+ /* if we're locked, don't process the event */
+ if (locked()) {
+ if (logevents && event != NULL) {
+ acpid_log(LOG_INFO,
+ "lockfile present, not processing "
+ "event \"%s\"", event);
+ }
+ return;
+ }
+
+ /* handle the event */
+ if (event) {
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "procfs received event \"%s\"", event);
+ }
+ acpid_handle_event(event);
+ if (logevents) {
+ acpid_log(LOG_INFO,
+ "procfs completed event \"%s\"", event);
+ }
+ } else if (errno == EPIPE) {
+ acpid_log(LOG_WARNING,
+ "events file connection closed");
+ exit(EXIT_FAILURE);
+ } else {
+ static int nerrs;
+ if (++nerrs >= ACPID_MAX_ERRS) {
+ acpid_log(LOG_ERR,
+ "too many errors reading "
+ "events file - aborting");
+ exit(EXIT_FAILURE);
+ }
+ }
+}
+
+int
+open_proc()
+{
+ int fd;
+ struct connection c;
+
+ fd = open(eventfile, O_RDONLY);
+ if (fd < 0) {
+ if (errno == ENOENT) {
+ acpid_log(LOG_DEBUG, "Deprecated %s was not found. "
+ "Trying netlink and the input layer...", eventfile);
+ } else {
+ acpid_log(LOG_ERR, "can't open %s: %s (%d)", eventfile,
+ strerror(errno), errno);
+ }
+ return -1;
+
+ }
+
+ /* Make sure scripts we exec() (in event.c) don't get our file
+ descriptors. */
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+
+ acpid_log(LOG_DEBUG, "proc fs opened successfully");
+
+ /* add a connection to the list */
+ c.fd = fd;
+ c.process = process_proc;
+ c.pathname = NULL;
+ c.kybd = 0;
+ add_connection(&c);
+
+ return 0;
+}
+
+/*
+ * This depends on fixes in linux ACPI after 2.4.8
+ */
+#define BUFLEN 1024
+static char *
+read_line(int fd)
+{
+ static char buf[BUFLEN];
+ int i = 0;
+ int r;
+ int searching = 1;
+
+ while (searching) {
+ memset(buf+i, 0, BUFLEN-i);
+
+ /* only go to BUFLEN-1 so there will always be a 0 at the end */
+ while (i < BUFLEN-1) {
+ r = read(fd, buf+i, 1);
+ if (r < 0 && errno != EINTR) {
+ /* we should do something with the data */
+ acpid_log(LOG_ERR, "read(): %s",
+ strerror(errno));
+ return NULL;
+ } else if (r == 0) {
+ /* signal this in an almost standard way */
+ errno = EPIPE;
+ return NULL;
+ } else if (r == 1) {
+ /* scan for a newline */
+ if (buf[i] == '\n') {
+ searching = 0;
+ buf[i] = '\0';
+ break;
+ }
+ i++;
+ }
+ }
+ if (i >= BUFLEN - 1)
+ break;
+ }
+
+ return buf;
+}
+
+#if 0
+/* This version leaks memory. The above version is simpler and leak-free. */
+/* Downside is that the above version always uses 1k of RAM. */
+/*
+ * This depends on fixes in linux ACPI after 2.4.8
+ */
+#define MAX_BUFLEN 1024
+static char *
+read_line(int fd)
+{
+ static char *buf;
+ int buflen = 64;
+ int i = 0;
+ int r;
+ int searching = 1;
+
+ while (searching) {
+ /* ??? This memory is leaked since it is never freed */
+ buf = realloc(buf, buflen);
+ if (!buf) {
+ acpid_log(LOG_ERR, "malloc(%d): %s",
+ buflen, strerror(errno));
+ return NULL;
+ }
+ memset(buf+i, 0, buflen-i);
+
+ while (i < buflen) {
+ r = read(fd, buf+i, 1);
+ if (r < 0 && errno != EINTR) {
+ /* we should do something with the data */
+ acpid_log(LOG_ERR, "read(): %s",
+ strerror(errno));
+ return NULL;
+ } else if (r == 0) {
+ /* signal this in an almost standard way */
+ errno = EPIPE;
+ return NULL;
+ } else if (r == 1) {
+ /* scan for a newline */
+ if (buf[i] == '\n') {
+ searching = 0;
+ buf[i] = '\0';
+ break;
+ }
+ i++;
+ }
+ }
+ if (buflen >= MAX_BUFLEN) {
+ break;
+ }
+ buflen *= 2;
+ }
+
+ return buf;
+}
+#endif
--- /dev/null
+/*
+ * proc.h - ACPI daemon proc filesystem interface
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * 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
+ */
+
+#ifndef PROC_H__
+#define PROC_H__
+
+extern const char *eventfile;
+
+extern int open_proc();
+
+#endif /* PROC_H__ */
--- /dev/null
+# This sample acpid configuration file goes with the acpi_handler.sh
+# handler script to show how one can handle all events within a single
+# script.
+
+event=*
+action=acpi_handler.sh "%e"
--- /dev/null
+#!/bin/sh
+# A sample skeleton for handling ACPI events.
+# See acpi_handler-conf for a configuration file that can be used to
+# run this script.
+
+if [ $# != 1 ]; then
+ exit 1
+fi
+set $*
+
+case "$1" in
+ button)
+ case "$2" in
+ power) /sbin/init 0
+ ;;
+ *) logger "ACPI action $2 is not defined"
+ ;;
+ esac
+ ;;
+
+ *)
+ logger "ACPI group $1 is not defined"
+ ;;
+esac
--- /dev/null
+# /etc/acpid/events/battery
+# This detects changes to AC connector status, and passes them to
+# /etc/acpi/battery.sh for further processing.
+
+# Optionally you can specify the placeholder %e. It will pass
+# through the whole kernel event message to the program you've
+# specified.
+
+event=battery
+action=/etc/acpi/battery.sh
--- /dev/null
+#!/bin/sh
+#
+# /etc/acpid/battery.sh
+#
+# written by Frank Dietrich <ablesoft@gmx.de>
+#
+# based on default.sh in the acpid package
+
+# Detect AC connector plugged in or unplugged and take appropriated actions.
+#
+# On my notebook no event triggered if AC connector plugged in or unplugged.
+# So I will use the battery event to detect new powerstate.
+
+# get the AC connector state from /proc filesystem.
+STATE=`sed -n 's/^.*\(off\|on\)-line.*/\1/p' /proc/acpi/ac_adapter/ACAD/state`
+
+case "$STATE" in
+ on)
+ # AC connector plugged in
+ # make an entry in /var/log/daemon.log
+ logger "acpid: AC connector plugged in."
+ # deactivate standby (spindown) timeout for the drive
+ /sbin/hdparm -q -S 0 /dev/hda
+ ;;
+ off)
+ # AC connector unplugged
+ logger "acpid: AC connector unplugged."
+ # activate standby (spindown) timeout for the drive
+ # timeout 5 minutes (man hdparm, for more informations)
+ /sbin/hdparm -q -S 60 /dev/hda
+ ;;
+ *)
+ # AC connector in undetermined state
+ logger "acpid: Could not determine new AC connector state."
+ ;;
+esac
--- /dev/null
+#!/usr/bin/perl -w
+# AC Power Handler v1.0
+# Handles AC power events for Panasonic notebooks
+#
+# Copyright (C) 2004 David Bronaugh
+#
+# Requires pcc_acpi driver
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# 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
+
+use strict;
+use POSIX qw(ceil floor);
+
+our($config);
+our($power_state);
+
+sub read_file {
+ my($file) = @_;
+ my($fh);
+ my($contents) = "";
+ if(open($fh, $file)) {
+ $/ = undef;
+ $contents = <$fh>;
+ close($fh);
+ } else {
+ print "Couldn't open file " . $file . "!\n";
+ }
+ return $contents;
+}
+
+sub write_file {
+ my($file, $contents) = @_;
+ my($fh);
+
+ if(open($fh, ">", $file)) {
+ print $fh $contents;
+ close($fh);
+ return 1;
+ } else {
+ print "Couldn't open file " . $file . "!\n";
+ return 0;
+ }
+}
+
+sub get_pcc_field {
+ my($field) = @_;
+ my($file) = $config->{'pcc_path'} . "/" . $power_state . "_" . $field;
+
+ return read_file($file);
+}
+
+sub set_pcc_field {
+ my($field, $contents) = @_;
+ my($file) = $config->{'pcc_path'} . "/" . $power_state . "_" . $field;
+
+ if(!write_file($file, $contents)) {
+ print "Couldn't set pcc " . $field . " field (are you root?)\n";
+ return 0;
+ }
+ return 1;
+}
+
+sub ac_disconnect {
+ $power_state = "dc";
+ set_pcc_field("brightness", get_pcc_field("brightness"));
+}
+
+sub ac_connect {
+ $power_state = "ac";
+ set_pcc_field("brightness", get_pcc_field("brightness"));
+}
+
+my($key) = $ARGV[3];
+
+my(%dispatch) = (
+ "00000000" => \&ac_disconnect,
+ "00000001" => \&ac_connect,
+ );
+
+$config = {
+ "pcc_path" => "/proc/acpi/pcc",
+ };
+
+$dispatch{$key}();
--- /dev/null
+# /etc/acpi/events/hotkey
+# This script handles hotkey events on Panasonic notebooks
+
+event=ac_adapter.*
+action=/etc/acpi/ac_adapt.pl %e
--- /dev/null
+# /etc/acpi/events/hotkey
+# This script handles hotkey events on Panasonic notebooks
+
+event=HKEY.*
+action=/etc/acpi/hotkey.pl %e
--- /dev/null
+#!/usr/bin/perl -w
+# Hotkey handler v1.0
+# Handles hotkey events for Panasonic notebooks
+#
+# Copyright (C) 2004 David Bronaugh
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation
+#
+# 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
+
+use strict;
+use POSIX qw(ceil floor);
+
+our($config);
+our($power_state);
+
+sub read_file {
+ my($file) = @_;
+ my($fh);
+ my($contents) = "";
+ if(open($fh, $file)) {
+ $/ = undef;
+ $contents = <$fh>;
+ close($fh);
+ } else {
+ print "Couldn't open file " . $file . "!\n";
+ }
+ return $contents;
+}
+
+sub write_file {
+ my($file, $contents) = @_;
+ my($fh);
+
+ if(open($fh, ">", $file)) {
+ print $fh $contents;
+ close($fh);
+ return 1;
+ } else {
+ print "Couldn't open file " . $file . "!\n";
+ return 0;
+ }
+}
+
+sub get_amixer_control_info {
+ my($control) = @_;
+ my($cmd) = $config->{'mixer_program'} . " cget name='" . $control . "'";
+ my(%info);
+ my($fh, $field);
+ my($contents) = "";
+ if(open($fh, $cmd . "|")) {
+ while(<$fh>) {
+ chomp;
+ $contents .= $_;
+ }
+ } else {
+ print "Couldn't run command " . $cmd . "!\n";
+ }
+
+ $contents =~ m/\; ([^\s]*)/;
+
+ foreach(split(/,/, $+)) {
+ my(@foo) = split(/=/, $_);
+ $info{$foo[0]} = $foo[1];
+ }
+
+ $contents =~ m/\: ([^\s]*)/;
+ my(@foo) = split(/=/, $+);
+ $info{$foo[0]} = [];
+ @{$info{$foo[0]}} = split(/,/, $foo[1]);
+
+ return \%info;
+}
+
+sub set_amixer_control_info {
+ my($control, $values) = @_;
+ my($cmd) = $config->{'mixer_program'} . " -q cset name='" . $control . "' " . $values;
+
+ if(system($cmd) == 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+sub get_pcc_field {
+ my($field) = @_;
+ my($file) = $config->{'pcc_path'} . "/" . $power_state . "_" . $field;
+
+ return read_file($file);
+}
+
+sub set_pcc_field {
+ my($field, $contents) = @_;
+ my($file) = $config->{'pcc_path'} . "/" . $power_state . "_" . $field;
+
+ if(!write_file($file, $contents)) {
+ print "Couldn't set pcc " . $field . " field (are you root?)\n";
+ return 0;
+ }
+ return 1;
+}
+
+sub get_brightness {
+ return (get_pcc_field("brightness_min"), get_pcc_field("brightness_max"), get_pcc_field("brightness"));
+}
+
+sub set_brightness {
+ my($value) = @_;
+ return set_pcc_field("brightness", $value);
+}
+
+sub get_mute {
+ my($info) = get_amixer_control_info($config->{'mute_switch'});
+
+ if($info->{'values'}[0] eq "on") {
+ return 0;
+ } elsif($info->{'values'}[0] eq "off") {
+ return 1;
+ } else {
+ print "Error getting mute status!\n";
+ return -1;
+ }
+}
+
+sub set_mute {
+ my($value) = @_;
+ if($value == 0) {
+ $value = "on";
+ } elsif($value == 1) {
+ $value = "off";
+ }
+
+ if(set_amixer_control_info($config->{'mute_switch'}, $value)) {
+ return 1;
+ } else {
+ print "Couldn't set mute status!\n";
+ return 0;
+ }
+}
+
+sub get_volume {
+ my($config) = @_;
+ my($info) = get_amixer_control_info($config->{'volume_ctl'});
+
+ return ($info->{'min'}, $info->{'max'}, $info->{'values'});
+}
+
+sub set_volume {
+ my($values) = @_;
+
+ return set_amixer_control_info($config->{'volume_ctl'}, join(",", @{$values}));
+}
+
+sub get_power_state {
+ my($data) = read_file($config->{"ac_state"});
+
+ if($data =~ /on-line/) {
+ return "ac";
+ } elsif($data =~ /off-line/) {
+ return "dc";
+ } else {
+ print "Couldn't get power state! (is ACPI enabled?)\n";
+ exit(1);
+ }
+}
+
+sub adjust_brightness {
+ my($adjust) = @_;
+ my($min, $max, $bright) = get_brightness($config);
+ my($threshold) = $config->{'max_bright_levels'};
+ my($divisor) = 1;
+
+ $bright -= $min;
+
+ if($max - $min > $threshold) {
+ $divisor = ($max - $min) / $threshold;
+ }
+
+ $bright = ceil($bright / $divisor);
+ $bright += $adjust;
+ $bright = floor($bright * $divisor);
+
+ $bright += $min;
+
+ if($bright < $min) {
+ $bright = $min;
+ }
+
+ if($bright > $max) {
+ $bright = $max;
+ }
+
+ if(!set_brightness($bright)) {
+ print "Couldn't adjust brightness!\n";
+ }
+
+ return;
+}
+
+sub adjust_volume {
+ my($increment) = @_;
+ my($min, $max, $volume) = get_volume($config);
+
+ $volume->[0] += $increment;
+ $volume->[1] += $increment;
+
+ $volume->[0] = ($volume->[0] < $min)?$min:$volume->[0];
+ $volume->[1] = ($volume->[1] < $min)?$min:$volume->[1];
+ $volume->[0] = ($volume->[0] > $max)?$max:$volume->[0];
+ $volume->[1] = ($volume->[1] > $max)?$max:$volume->[1];
+
+ if(!set_volume($volume)) {
+ print "Couldn't set volume!\n";
+ }
+
+ return;
+}
+
+# Functions which implement hotkey functions directly
+sub down_brightness {
+ adjust_brightness(-1);
+}
+
+sub up_brightness {
+ adjust_brightness(1);
+}
+
+sub switch_monitor {
+ #STUB
+}
+
+sub toggle_mute {
+ my($mute) = get_mute();
+
+ if($mute >= 0) {
+ set_mute($mute ^ 1);
+ }
+}
+
+sub volume_up {
+ adjust_volume($config->{"volume_increment"})
+}
+
+sub volume_down {
+ adjust_volume(-1 * $config->{"volume_increment"})
+}
+
+sub suspend_to_ram {
+ # This space intentionally left blank (because it doesn't work here)
+}
+
+sub spin_down_hd {
+ if(system("hdparm -q -y /dev/hda") != 0) {
+ print "Error running hdparm -- is it installed?\n";
+ }
+}
+
+sub suspend_to_disk {
+ system("hwclock --systohc");
+ write_file($config->{'suspend_control'}, "disk");
+ system("hwclock --hctosys");
+}
+
+my($key) = $ARGV[3];
+
+my(%dispatch) = (
+ "00000081" => \&down_brightness,
+ "00000082" => \&up_brightness,
+ "00000003" => \&switch_monitor,
+ "00000084" => \&toggle_mute,
+ "00000085" => \&volume_down,
+ "00000086" => \&volume_up,
+ "00000007" => \&suspend_to_ram,
+ "00000089" => \&spin_down_hd,
+ "0000000a" => \&suspend_to_disk
+ );
+
+$config = {
+ "pcc_path" => "/proc/acpi/pcc",
+ "mixer_program" => "amixer",
+ "ac_state" => "/proc/acpi/ac_adapter/AC/state",
+ "mute_switch" => "Master Playback Switch",
+ "volume_ctl" => "Master Playback Volume",
+ "max_bright_levels" => 20,
+ "volume_increment" => 2,
+ "suspend_control" => "/sys/power/state"
+ };
+
+$power_state = get_power_state();
+
+$dispatch{$key}();
--- /dev/null
+# This is a sample acpid configuration file. See the man page for
+# acpid which describes this configuration file.
+# When a button/power event is received, the power.sh script is run.
+
+event=button/power
+action=/etc/acpid/power.sh
+
--- /dev/null
+#!/bin/bash
+# This script is triggered by the "power" configuration file. This is
+# described in the acpid man page.
+
+# A much more comprehensive power button handler is provided in the
+# powerbtn directory in this samples directory. If you run gnome or KDE,
+# it's better to use that one instead of this one.
+
+/sbin/shutdown -h now "Power button pressed"
+
--- /dev/null
+# /etc/acpi/events/powerbtn
+# This is called when the user presses the power button and calls
+# /etc/acpi/powerbtn.sh for further processing.
+
+# Optionally you can specify the placeholder %e. It will pass
+# through the whole kernel event message to the program you've
+# specified.
+
+# We need to react on "button power.*" and "button/power.*" because
+# of kernel changes.
+
+event=button[ /]power
+action=/etc/acpi/powerbtn.sh
--- /dev/null
+#!/bin/sh
+# /etc/acpi/powerbtn.sh
+# Taken from Debian's 2.0.4-1 diff file. This version handles KDE4.
+# Power Button event handler.
+# Checks to see if gnome or KDE are already handling the power button.
+# If not, initiates a plain shutdown.
+
+# getXuser gets the X user belonging to the display in $displaynum.
+# If you want the foreground X user, use getXconsole!
+# Input:
+# displaynum - X display number
+# Output:
+# XUSER - the name of the user
+# XAUTHORITY - full pathname of the user's .Xauthority file
+getXuser() {
+ user=`pinky -fw | awk '{ if ($2 == ":'$displaynum'" || $(NF) == ":'$displaynum'" ) { print $1; exit; } }'`
+ if [ x"$user" = x"" ]; then
+ startx=`pgrep -n startx`
+ if [ x"$startx" != x"" ]; then
+ user=`ps -o user --no-headers $startx`
+ fi
+ fi
+ if [ x"$user" != x"" ]; then
+ userhome=`getent passwd $user | cut -d: -f6`
+ export XAUTHORITY=$userhome/.Xauthority
+ else
+ export XAUTHORITY=""
+ fi
+ export XUSER=$user
+}
+
+# Gets the X display number for the active virtual terminal.
+# Output:
+# DISPLAY - the X display number
+# See getXuser()'s output.
+getXconsole() {
+ console=`fgconsole`;
+ displaynum=`ps t tty$console | sed -n -re 's,.*/X .*:([0-9]+).*,\1,p'`
+ if [ x"$displaynum" != x"" ]; then
+ export DISPLAY=":$displaynum"
+ getXuser
+ fi
+}
+
+# Skip if we are just in the middle of resuming.
+test -f /var/lock/acpisleep && exit 0
+
+# If the current X console user is running a power management daemon that
+# handles suspend/resume requests, let them handle policy.
+
+getXconsole
+
+# A list of power management system process names.
+PMS="gnome-power-manager kpowersave xfce4-power-manager"
+PMS="$PMS guidance-power-manager.py dalston-power-applet"
+
+# If one of those is running or any of several others,
+if pidof x $PMS > /dev/null ||
+ ( test "$XUSER" != "" && pidof dcopserver > /dev/null && test -x /usr/bin/dcop && /usr/bin/dcop --user $XUSER kded kded loadedModules | grep -q klaptopdaemon) ||
+ ( test "$XUSER" != "" && test -x /usr/bin/qdbus && test -r /proc/$(pidof kded4)/environ && su - $XUSER -c "eval $(echo -n 'export '; cat /proc/$(pidof kded4)/environ |tr '\0' '\n'|grep DBUS_SESSION_BUS_ADDRESS); qdbus org.kde.kded" | grep -q powerdevil) ; then
+ # Get out as the power manager that is running will take care of things.
+ exit
+fi
+
+# No power managment system appears to be running. Just initiate a plain
+# shutdown.
+/sbin/shutdown -h now "Power button pressed"
+
--- /dev/null
+#!/bin/sh
+# /etc/acpi/powerbtn.sh
+# Taken from the 11/14/2008(ish) version from Debian.
+# Power Button event handler.
+# Checks to see if gnome or KDE are already handling the power button.
+# If not, initiates a plain shutdown.
+
+# This is an older version from Debian that does not handle KDE4.
+
+# Skip if we are in the middle of resuming. Otherwise we may power down the
+# system as it is coming back up.
+# See 98-acpi-unlock.sh and 05-acpi-lock.sh in Debian.
+test -f /var/lock/acpisleep && exit 0
+
+# If gnome-power-manager, kpowersave or klaptopdaemon are running...
+if pidof gnome-power-manager kpowersave > /dev/null ||
+ (pidof dcopserver > /dev/null && test -x /usr/bin/dcop && /usr/bin/dcop kded kded loadedModules | grep -q klaptopdaemon) ; then
+ # Let them handle the power button.
+ exit
+fi
+
+# If KDE is running...
+if ps -Af | grep -q '[k]desktop' && pidof dcopserver > /dev/null && test -x /usr/bin/dcop ; then
+ # Ask it to logout.
+ KDESES=`pidof dcopserver | wc -w`
+ if [ $KDESES -eq 1 ] ; then
+ # single KDE session -> ask user
+ /usr/bin/dcop --all-sessions --all-users ksmserver ksmserver logout 1 2 0
+ exit 0
+ else
+ # more than one KDE session - just send shutdown signal to all of them
+ /usr/bin/dcop --all-sessions --all-users ksmserver ksmserver logout 0 2 0 && exit 0
+ fi
+fi
+
+# Initiate a plain shutdown.
+/sbin/shutdown -h now "Power button pressed"
+
--- /dev/null
+/*
+ * sock.c - ACPI daemon socket interface
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <grp.h>
+
+#include "acpid.h"
+#include "log.h"
+#include "event.h"
+#include "ud_socket.h"
+#include "connection_list.h"
+
+const char *socketfile = ACPID_SOCKETFILE;
+const char *socketgroup;
+mode_t socketmode = ACPID_SOCKETMODE;
+int clientmax = ACPID_CLIENTMAX;
+
+/* the number of non-root clients that are connected */
+int non_root_clients;
+
+/* determine if a file descriptor is in fact a socket */
+int
+is_socket(int fd)
+{
+ int v;
+ socklen_t l = sizeof(int);
+
+ return (getsockopt(fd, SOL_SOCKET, SO_TYPE, (char *)&v, &l) == 0);
+}
+
+/* accept a new client connection */
+static void
+process_sock(int fd)
+{
+ int cli_fd;
+ struct ucred creds;
+ char buf[32];
+ static int accept_errors;
+
+ /* accept and add to our lists */
+ cli_fd = ud_accept(fd, &creds);
+ if (cli_fd < 0) {
+ acpid_log(LOG_ERR, "can't accept client: %s",
+ strerror(errno));
+ accept_errors++;
+ if (accept_errors >= 5) {
+ acpid_log(LOG_ERR, "giving up");
+ clean_exit_with_status(EXIT_FAILURE);
+ }
+ return;
+ }
+ accept_errors = 0;
+
+ /* don't allow too many non-root clients */
+ if (creds.uid != 0 && non_root_clients >= clientmax) {
+ close(cli_fd);
+ acpid_log(LOG_ERR, "too many non-root clients");
+ return;
+ }
+ if (creds.uid != 0) {
+ non_root_clients++;
+ }
+
+ /* don't leak fds when execing */
+ if (fcntl(cli_fd, F_SETFD, FD_CLOEXEC) < 0) {
+ close(cli_fd);
+ acpid_log(LOG_ERR, "fcntl() on client for FD_CLOEXEC: %s",
+ strerror(errno));
+ return;
+ }
+
+ /* don't allow clients to block this */
+ if (fcntl(cli_fd, F_SETFL, O_NONBLOCK) < 0) {
+ close(cli_fd);
+ acpid_log(LOG_ERR, "fcntl() on client for O_NONBLOCK: %s",
+ strerror(errno));
+ return;
+ }
+
+ snprintf(buf, sizeof(buf)-1, "%d[%d:%d]",
+ creds.pid, creds.uid, creds.gid);
+ acpid_add_client(cli_fd, buf);
+}
+
+/* set up the socket for client connections */
+void
+open_sock()
+{
+ int fd;
+ struct connection c;
+
+ /* if this is a socket passed in via stdin by systemd */
+ if (is_socket(STDIN_FILENO)) {
+ fd = STDIN_FILENO;
+ } else {
+ /* create our own socket */
+ fd = ud_create_socket(socketfile);
+ if (fd < 0) {
+ acpid_log(LOG_ERR, "can't open socket %s: %s",
+ socketfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ if (chmod(socketfile, socketmode) < 0) {
+ close(fd);
+ acpid_log(LOG_ERR, "chmod() on socket %s: %s",
+ socketfile, strerror(errno));
+ return;
+ }
+
+ /* if we need to change the socket's group, do so */
+ if (socketgroup) {
+ struct group *gr;
+ struct stat buf;
+
+ gr = getgrnam(socketgroup);
+ if (!gr) {
+ acpid_log(LOG_ERR, "group %s does not exist", socketgroup);
+ exit(EXIT_FAILURE);
+ }
+ if (stat(socketfile, &buf) < 0) {
+ acpid_log(LOG_ERR, "can't stat %s: %s",
+ socketfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ if (chown(socketfile, buf.st_uid, gr->gr_gid) < 0) {
+ acpid_log(LOG_ERR, "can't chown %s: %s",
+ socketfile, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* don't leak fds when execing */
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
+ close(fd);
+ acpid_log(LOG_ERR, "fcntl() on socket %s for FD_CLOEXEC: %s",
+ socketfile, strerror(errno));
+ return;
+ }
+
+ /* avoid a potential hang */
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ close(fd);
+ acpid_log(LOG_ERR, "fcntl() on socket %s for O_NONBLOCK: %s",
+ socketfile, strerror(errno));
+ return;
+ }
+
+ /* add a connection to the list */
+ c.fd = fd;
+ c.process = process_sock;
+ c.pathname = NULL;
+ c.kybd = 0;
+ add_connection(&c);
+}
--- /dev/null
+/*
+ * sock.h - ACPI daemon socket interface
+ *
+ * Portions Copyright (C) 2000 Andrew Henroid
+ * Portions Copyright (C) 2001 Sun Microsystems
+ * Portions Copyright (C) 2004 Tim Hockin (thockin@hockin.org)
+ *
+ * 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
+ */
+
+#ifndef SOCK_H__
+#define SOCK_H__
+
+extern const char *socketfile;
+extern const char *socketgroup;
+extern mode_t socketmode;
+extern int clientmax;
+extern int non_root_clients;
+
+extern int is_socket(int fd);
+extern void open_sock();
+
+#endif /* SOCK_H__ */
--- /dev/null
+/*
+ * $Id: ud_socket.c,v 1.6 2009/04/22 18:22:28 thockin Exp $
+ * A few routines for handling UNIX domain sockets
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+
+#include "acpid.h"
+#include "log.h"
+#include "ud_socket.h"
+
+int
+ud_create_socket(const char *name)
+{
+ int fd;
+ int r;
+ struct sockaddr_un uds_addr;
+
+ if (strnlen(name, sizeof(uds_addr.sun_path)) >
+ sizeof(uds_addr.sun_path) - 1) {
+ acpid_log(LOG_ERR, "ud_create_socket(): "
+ "socket filename longer than %u characters: %s",
+ sizeof(uds_addr.sun_path) - 1, name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* JIC */
+ unlink(name);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return fd;
+ }
+
+ /* setup address struct */
+ memset(&uds_addr, 0, sizeof(uds_addr));
+ uds_addr.sun_family = AF_UNIX;
+ strncpy(uds_addr.sun_path, name, sizeof(uds_addr.sun_path) - 1);
+
+ /* bind it to the socket */
+ r = bind(fd, (struct sockaddr *)&uds_addr, sizeof(uds_addr));
+ if (r < 0) {
+ return r;
+ }
+
+ /* listen - allow 10 to queue */
+ r = listen(fd, 10);
+ if (r < 0) {
+ return r;
+ }
+
+ return fd;
+}
+
+int
+ud_accept(int listenfd, struct ucred *cred)
+{
+ while (1) {
+ int newsock = 0;
+ struct sockaddr_un cliaddr;
+ socklen_t len = sizeof(struct sockaddr_un);
+
+ newsock = accept(listenfd, (struct sockaddr *)&cliaddr, &len);
+ if (newsock < 0) {
+ if (errno == EINTR) {
+ continue; /* signal */
+ }
+
+ return newsock;
+ }
+
+ if (cred) {
+ len = sizeof(struct ucred);
+ getsockopt(newsock,SOL_SOCKET,SO_PEERCRED,cred,&len);
+ }
+
+ return newsock;
+ }
+}
+
+int
+ud_connect(const char *name)
+{
+ int fd;
+ int r;
+ struct sockaddr_un addr;
+
+ if (strnlen(name, sizeof(addr.sun_path)) > sizeof(addr.sun_path) - 1) {
+ acpid_log(LOG_ERR, "ud_connect(): "
+ "socket filename longer than %u characters: %s",
+ sizeof(addr.sun_path) - 1, name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ return fd;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ sprintf(addr.sun_path, "%s", name);
+ /* safer: */
+ /*strncpy(addr.sun_path, name, sizeof(addr.sun_path) - 1);*/
+
+ r = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (r < 0) {
+ close(fd);
+ return r;
+ }
+
+ return fd;
+}
+
+int
+ud_get_peercred(int fd, struct ucred *cred)
+{
+ socklen_t len = sizeof(struct ucred);
+ getsockopt(fd, SOL_SOCKET, SO_PEERCRED, cred, &len);
+ return 0;
+}
--- /dev/null
+/*
+ *$Id: ud_socket.h,v 1.3 2009/04/22 18:22:28 thockin Exp $
+ */
+
+#ifndef UD_SOCKET_H__
+#define UD_SOCKET_H__
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+int ud_create_socket(const char *name);
+int ud_accept(int sock, struct ucred *cred);
+int ud_connect(const char *name);
+int ud_get_peercred(int fd, struct ucred *cred);
+
+#endif