Tizen 2.0 Release tizen_2.0 accepted/tizen_2.0/20130215.204525 submit/tizen_2.0/20130215.191540
authorHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 15:01:47 +0000 (00:01 +0900)
committerHyungKyu Song <hk76.song@samsung.com>
Fri, 15 Feb 2013 15:01:47 +0000 (00:01 +0900)
83 files changed:
COPYING [new file with mode: 0644]
Changelog [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
TESTPLAN [new file with mode: 0644]
TODO [new file with mode: 0644]
acpi_genetlink.h [new file with mode: 0644]
acpi_ids.c [new file with mode: 0644]
acpi_ids.h [new file with mode: 0644]
acpi_listen.8 [new file with mode: 0644]
acpi_listen.c [new file with mode: 0644]
acpid-design.odg [new file with mode: 0644]
acpid.8 [new file with mode: 0644]
acpid.c [new file with mode: 0644]
acpid.h [new file with mode: 0644]
connection_list.c [new file with mode: 0644]
connection_list.h [new file with mode: 0644]
event.c [new file with mode: 0644]
event.h [new file with mode: 0644]
genetlink.h [new file with mode: 0644]
inotify_handler.c [new file with mode: 0644]
inotify_handler.h [new file with mode: 0644]
input_layer.c [new file with mode: 0644]
input_layer.h [new file with mode: 0644]
kacpimon/README [new file with mode: 0644]
kacpimon/acpi_genetlink.h [new file with mode: 0644]
kacpimon/acpi_ids.c [new file with mode: 0644]
kacpimon/acpi_ids.h [new file with mode: 0644]
kacpimon/connection_list.c [new file with mode: 0644]
kacpimon/connection_list.h [new file with mode: 0644]
kacpimon/genetlink.h [new file with mode: 0644]
kacpimon/input_layer.c [new file with mode: 0644]
kacpimon/input_layer.h [new file with mode: 0644]
kacpimon/kacpimon.8 [new file with mode: 0644]
kacpimon/kacpimon.c [new file with mode: 0644]
kacpimon/kacpimon.h [new file with mode: 0644]
kacpimon/libnetlink.c [new file with mode: 0644]
kacpimon/libnetlink.h [new file with mode: 0644]
kacpimon/makefile [new file with mode: 0644]
kacpimon/netlink.c [new file with mode: 0644]
kacpimon/netlink.h [new file with mode: 0644]
kacpimon/sample.out [new file with mode: 0644]
libnetlink.c [new file with mode: 0644]
libnetlink.h [new file with mode: 0644]
log.c [new file with mode: 0644]
log.h [new file with mode: 0644]
netlink.c [new file with mode: 0644]
netlink.h [new file with mode: 0644]
packaging/acpid [new file with mode: 0644]
packaging/acpid-2.0.9-makefile.patch [new file with mode: 0644]
packaging/acpid-start-script [new file with mode: 0644]
packaging/acpid.ac.conf [new file with mode: 0644]
packaging/acpid.battery.conf [new file with mode: 0644]
packaging/acpid.battery.sh [new file with mode: 0644]
packaging/acpid.changes [new file with mode: 0644]
packaging/acpid.lid.conf [new file with mode: 0644]
packaging/acpid.lid.sh [new file with mode: 0644]
packaging/acpid.manifest [new file with mode: 0644]
packaging/acpid.power.conf [new file with mode: 0644]
packaging/acpid.power.sh [new file with mode: 0644]
packaging/acpid.service [new file with mode: 0644]
packaging/acpid.spec [new file with mode: 0644]
packaging/acpid.start.sh [new file with mode: 0644]
packaging/acpid.video.conf [new file with mode: 0644]
proc.c [new file with mode: 0644]
proc.h [new file with mode: 0644]
samples/acpi_handler-conf [new file with mode: 0644]
samples/acpi_handler.sh [new file with mode: 0755]
samples/battery/battery-conf [new file with mode: 0644]
samples/battery/battery.sh [new file with mode: 0644]
samples/panasonic/ac_adapt.pl [new file with mode: 0644]
samples/panasonic/ac_adapter [new file with mode: 0644]
samples/panasonic/hotkey [new file with mode: 0644]
samples/panasonic/hotkey.pl [new file with mode: 0644]
samples/power [new file with mode: 0644]
samples/power.sh [new file with mode: 0755]
samples/powerbtn/powerbtn-conf [new file with mode: 0644]
samples/powerbtn/powerbtn.sh [new file with mode: 0755]
samples/powerbtn/powerbtn.sh.old [new file with mode: 0755]
sock.c [new file with mode: 0644]
sock.h [new file with mode: 0644]
ud_socket.c [new file with mode: 0644]
ud_socket.h [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..a43ea21
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                          675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\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.
diff --git a/Changelog b/Changelog
new file mode 100644 (file)
index 0000000..b6d09d7
--- /dev/null
+++ b/Changelog
@@ -0,0 +1,393 @@
+* 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
+
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..a849fff
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,108 @@
+# 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
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..7552670
--- /dev/null
+++ b/README
@@ -0,0 +1,52 @@
+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"
diff --git a/TESTPLAN b/TESTPLAN
new file mode 100644 (file)
index 0000000..7d91413
--- /dev/null
+++ b/TESTPLAN
@@ -0,0 +1,89 @@
+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.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..347442d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,18 @@
+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
diff --git a/acpi_genetlink.h b/acpi_genetlink.h
new file mode 100644 (file)
index 0000000..ce24e57
--- /dev/null
@@ -0,0 +1,33 @@
+#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
diff --git a/acpi_ids.c b/acpi_ids.c
new file mode 100644 (file)
index 0000000..f275cfc
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ *  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;
+}
diff --git a/acpi_ids.h b/acpi_ids.h
new file mode 100644 (file)
index 0000000..52a9ff7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  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__ */
diff --git a/acpi_listen.8 b/acpi_listen.8
new file mode 100644 (file)
index 0000000..43e95a7
--- /dev/null
@@ -0,0 +1,47 @@
+.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
+
diff --git a/acpi_listen.c b/acpi_listen.c
new file mode 100644 (file)
index 0000000..de1da25
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ *  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;
+}
diff --git a/acpid-design.odg b/acpid-design.odg
new file mode 100644 (file)
index 0000000..d564962
Binary files /dev/null and b/acpid-design.odg differ
diff --git a/acpid.8 b/acpid.8
new file mode 100644 (file)
index 0000000..6d93e7d
--- /dev/null
+++ b/acpid.8
@@ -0,0 +1,170 @@
+.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
+
diff --git a/acpid.c b/acpid.c
new file mode 100644 (file)
index 0000000..962e577
--- /dev/null
+++ b/acpid.c
@@ -0,0 +1,474 @@
+/*
+ *  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);
+}
+
diff --git a/acpid.h b/acpid.h
new file mode 100644 (file)
index 0000000..350edf1
--- /dev/null
+++ b/acpid.h
@@ -0,0 +1,53 @@
+/*
+ *  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__ */
diff --git a/connection_list.c b/connection_list.c
new file mode 100644 (file)
index 0000000..0fc7c53
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ *  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;
+}
diff --git a/connection_list.h b/connection_list.h
new file mode 100644 (file)
index 0000000..16271b1
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ *  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__ */
diff --git a/event.c b/event.c
new file mode 100644 (file)
index 0000000..1c4cbc2
--- /dev/null
+++ b/event.c
@@ -0,0 +1,796 @@
+/*
+ *  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;
+}
diff --git a/event.h b/event.h
new file mode 100644 (file)
index 0000000..1dc0a91
--- /dev/null
+++ b/event.h
@@ -0,0 +1,32 @@
+/*
+ *  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__ */
diff --git a/genetlink.h b/genetlink.h
new file mode 100644 (file)
index 0000000..7b1ae9a
--- /dev/null
@@ -0,0 +1,81 @@
+#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 */
diff --git a/inotify_handler.c b/inotify_handler.c
new file mode 100644 (file)
index 0000000..f2c19d2
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ *  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);
+}
+
diff --git a/inotify_handler.h b/inotify_handler.h
new file mode 100644 (file)
index 0000000..b808727
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  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__ */
diff --git a/input_layer.c b/input_layer.c
new file mode 100644 (file)
index 0000000..e9b40f4
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ *  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);
+}
+
diff --git a/input_layer.h b/input_layer.h
new file mode 100644 (file)
index 0000000..d6dccd0
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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__ */
diff --git a/kacpimon/README b/kacpimon/README
new file mode 100644 (file)
index 0000000..f4674c2
--- /dev/null
@@ -0,0 +1,3 @@
+Moved to the man page.  Try this:
+
+  man -l kacpimon.8
diff --git a/kacpimon/acpi_genetlink.h b/kacpimon/acpi_genetlink.h
new file mode 100644 (file)
index 0000000..ce24e57
--- /dev/null
@@ -0,0 +1,33 @@
+#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
diff --git a/kacpimon/acpi_ids.c b/kacpimon/acpi_ids.c
new file mode 100644 (file)
index 0000000..389eacc
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ *  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;
+}
diff --git a/kacpimon/acpi_ids.h b/kacpimon/acpi_ids.h
new file mode 100644 (file)
index 0000000..52a9ff7
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ *  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__ */
diff --git a/kacpimon/connection_list.c b/kacpimon/connection_list.c
new file mode 100644 (file)
index 0000000..bd3c200
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  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;
+}
diff --git a/kacpimon/connection_list.h b/kacpimon/connection_list.h
new file mode 100644 (file)
index 0000000..eeb836e
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ *  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__ */
diff --git a/kacpimon/genetlink.h b/kacpimon/genetlink.h
new file mode 100644 (file)
index 0000000..7b1ae9a
--- /dev/null
@@ -0,0 +1,81 @@
+#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 */
diff --git a/kacpimon/input_layer.c b/kacpimon/input_layer.c
new file mode 100644 (file)
index 0000000..509d3e0
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ *  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);
+}
+
diff --git a/kacpimon/input_layer.h b/kacpimon/input_layer.h
new file mode 100644 (file)
index 0000000..399ef23
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ *  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__ */
diff --git a/kacpimon/kacpimon.8 b/kacpimon/kacpimon.8
new file mode 100644 (file)
index 0000000..bb3448f
--- /dev/null
@@ -0,0 +1,68 @@
+.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.
+
diff --git a/kacpimon/kacpimon.c b/kacpimon/kacpimon.c
new file mode 100644 (file)
index 0000000..af8176c
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ *  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;
+}
diff --git a/kacpimon/kacpimon.h b/kacpimon/kacpimon.h
new file mode 100644 (file)
index 0000000..fc64420
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ *  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__ */
diff --git a/kacpimon/libnetlink.c b/kacpimon/libnetlink.c
new file mode 100644 (file)
index 0000000..dc1f853
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * 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;
+}
diff --git a/kacpimon/libnetlink.h b/kacpimon/libnetlink.h
new file mode 100644 (file)
index 0000000..6185cbc
--- /dev/null
@@ -0,0 +1,91 @@
+#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__ */
+
diff --git a/kacpimon/makefile b/kacpimon/makefile
new file mode 100644 (file)
index 0000000..d5c188c
--- /dev/null
@@ -0,0 +1,56 @@
+# 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)
+
diff --git a/kacpimon/netlink.c b/kacpimon/netlink.c
new file mode 100644 (file)
index 0000000..86e2612
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ *  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);
+}
diff --git a/kacpimon/netlink.h b/kacpimon/netlink.h
new file mode 100644 (file)
index 0000000..9921ac2
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ *  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__ */
diff --git a/kacpimon/sample.out b/kacpimon/sample.out
new file mode 100644 (file)
index 0000000..227bd0a
--- /dev/null
@@ -0,0 +1,16 @@
+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
diff --git a/libnetlink.c b/libnetlink.c
new file mode 100644 (file)
index 0000000..045d2d1
--- /dev/null
@@ -0,0 +1,591 @@
+/*
+ * 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;
+}
diff --git a/libnetlink.h b/libnetlink.h
new file mode 100644 (file)
index 0000000..8f9cb5e
--- /dev/null
@@ -0,0 +1,91 @@
+#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__ */
+
diff --git a/log.c b/log.c
new file mode 100644 (file)
index 0000000..36974f9
--- /dev/null
+++ b/log.c
@@ -0,0 +1,56 @@
+/*
+ *  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;
+}
diff --git a/log.h b/log.h
new file mode 100644 (file)
index 0000000..971f76e
--- /dev/null
+++ b/log.h
@@ -0,0 +1,37 @@
+/*
+ *  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__ */
diff --git a/netlink.c b/netlink.c
new file mode 100644 (file)
index 0000000..0eb493b
--- /dev/null
+++ b/netlink.c
@@ -0,0 +1,240 @@
+/*
+ *  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);
+}
+
diff --git a/netlink.h b/netlink.h
new file mode 100644 (file)
index 0000000..9921ac2
--- /dev/null
+++ b/netlink.h
@@ -0,0 +1,31 @@
+/*
+ *  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__ */
diff --git a/packaging/acpid b/packaging/acpid
new file mode 100644 (file)
index 0000000..f0d2bfb
--- /dev/null
@@ -0,0 +1 @@
+OPTIONS=
diff --git a/packaging/acpid-2.0.9-makefile.patch b/packaging/acpid-2.0.9-makefile.patch
new file mode 100644 (file)
index 0000000..b05f23f
--- /dev/null
@@ -0,0 +1,16 @@
+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)
diff --git a/packaging/acpid-start-script b/packaging/acpid-start-script
new file mode 100644 (file)
index 0000000..c75d5a5
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+/usr/sbin/acpid &
+/etc/acpi/actions/start.sh
diff --git a/packaging/acpid.ac.conf b/packaging/acpid.ac.conf
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/packaging/acpid.battery.conf b/packaging/acpid.battery.conf
new file mode 100644 (file)
index 0000000..bbddf8e
--- /dev/null
@@ -0,0 +1,5 @@
+# /etc/acpi/events/battery
+# Called when AC power goes away and we switch to battery
+
+event=battery.*
+action=/etc/acpi/actions/battery.sh
diff --git a/packaging/acpid.battery.sh b/packaging/acpid.battery.sh
new file mode 100644 (file)
index 0000000..817ab44
--- /dev/null
@@ -0,0 +1,39 @@
+#!/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
+
diff --git a/packaging/acpid.changes b/packaging/acpid.changes
new file mode 100644 (file)
index 0000000..7aa8510
--- /dev/null
@@ -0,0 +1,14 @@
+* Tue Jun 26 2012 William Douglas <william.douglas@intel.com> ca3c303
+- fix systemd unit install path
+
+* Thu Jun 21 2012 William Douglas <william.douglas@intel.com> 305e215
+- Remove chmod use in build
+
+* Tue May 29 2012 Ryan Ware <ryan.r.ware@intel.com> bd6b5e9
+- Add default Smack manifest for acpid.spec
+
+* Fri May 18 2012 Zhang Qiang <qiang.z.zhang@intel.com> 97875af
+- Initial code release
+
+* Fri Apr 27 2012 eunmee moon <eunmee.moon@samsung.com> 1320dfb
+- Initial empty repository
diff --git a/packaging/acpid.lid.conf b/packaging/acpid.lid.conf
new file mode 100644 (file)
index 0000000..0545faf
--- /dev/null
@@ -0,0 +1,5 @@
+# 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
diff --git a/packaging/acpid.lid.sh b/packaging/acpid.lid.sh
new file mode 100644 (file)
index 0000000..9805df4
--- /dev/null
@@ -0,0 +1,37 @@
+#!/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
+
diff --git a/packaging/acpid.manifest b/packaging/acpid.manifest
new file mode 100644 (file)
index 0000000..017d22d
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+ <request>
+    <domain name="_"/>
+ </request>
+</manifest>
diff --git a/packaging/acpid.power.conf b/packaging/acpid.power.conf
new file mode 100644 (file)
index 0000000..b654aa3
--- /dev/null
@@ -0,0 +1,5 @@
+# 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
diff --git a/packaging/acpid.power.sh b/packaging/acpid.power.sh
new file mode 100644 (file)
index 0000000..0996d2a
--- /dev/null
@@ -0,0 +1,29 @@
+#!/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
+
diff --git a/packaging/acpid.service b/packaging/acpid.service
new file mode 100644 (file)
index 0000000..d245e67
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=ACPI event daemon
+
+[Service]
+Type=forking
+EnvironmentFile=/etc/sysconfig/acpid
+ExecStart=/usr/sbin/acpid $OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/packaging/acpid.spec b/packaging/acpid.spec
new file mode 100644 (file)
index 0000000..6a8de5a
--- /dev/null
@@ -0,0 +1,145 @@
+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:        packaging/acpid.video.conf
+Source3:        packaging/acpid.power.conf
+Source4:        packaging/acpid.power.sh
+Source5:        packaging/acpid.lid.conf
+Source6:        packaging/acpid.lid.sh
+Source7:        packaging/acpid.battery.sh
+Source8:        packaging/acpid.battery.conf
+Source9:        packaging/acpid.ac.conf
+Source13:       packaging/acpid-start-script
+Source14:       packaging/acpid.start.sh
+Source15:       packaging/acpid.service
+Source16:       packaging/acpid
+Source1001:     packaging/acpid.manifest 
+# >> gbp-patch-tags         # auto-added by gbp
+Patch1:         acpid-2.0.9-makefile.patch
+# << gbp-patch-tags         # auto-added by gbp
+
+Requires(post): /usr/bin/systemctl
+Requires(postun): /usr/bin/systemctl
+Requires(preun): /usr/bin/systemctl
+
+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
+cp %{SOURCE1001} .
+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
+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}/%{_libdir}/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}/%{_libdir}/systemd/system/acpid.service
+install -D -m 644 %{SOURCE16} %{buildroot}%{_sysconfdir}/sysconfig/acpid
+
+mkdir -p %{buildroot}/%{_libdir}/systemd/system/sysinit.target.wants
+ln -s ../acpid.service %{buildroot}/%{_libdir}/systemd/system/sysinit.target.wants/acpid.service
+
+
+%preun
+if [ $1 == 0 ]; then
+    systemctl stop acpid.service
+fi
+
+%post
+systemctl daemon-reload
+if [ $1 == 1 ]; then
+    systemctl restart acpid.service
+fi
+
+%postun
+systemctl daemon-reload
+
+
+%files
+%manifest acpid.manifest
+%dir %{_sysconfdir}/acpi
+%attr(0755,root,root) %dir %{_sysconfdir}/acpi/events
+%dir %{_sysconfdir}/acpi/actions
+%dir %{_sysconfdir}/acpi/ac.d
+%dir %{_sysconfdir}/acpi/battery.d
+%dir %{_localstatedir}/lib/acpi-support
+%{_libdir}/systemd/system/acpid.service
+%{_libdir}/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
+%manifest acpid.manifest
+%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
diff --git a/packaging/acpid.start.sh b/packaging/acpid.start.sh
new file mode 100644 (file)
index 0000000..b849881
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+for SCRIPT in /etc/acpi/start.d/*.sh; do
+    if [ -x $SCRIPT ] ; then
+       . $SCRIPT
+    fi
+done
diff --git a/packaging/acpid.video.conf b/packaging/acpid.video.conf
new file mode 100644 (file)
index 0000000..97507bf
--- /dev/null
@@ -0,0 +1,6 @@
+# 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
diff --git a/proc.c b/proc.c
new file mode 100644 (file)
index 0000000..7d9fcd7
--- /dev/null
+++ b/proc.c
@@ -0,0 +1,217 @@
+/*
+ *  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
diff --git a/proc.h b/proc.h
new file mode 100644 (file)
index 0000000..fbc9022
--- /dev/null
+++ b/proc.h
@@ -0,0 +1,30 @@
+/*
+ *  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__ */
diff --git a/samples/acpi_handler-conf b/samples/acpi_handler-conf
new file mode 100644 (file)
index 0000000..a48b68b
--- /dev/null
@@ -0,0 +1,6 @@
+# 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"
diff --git a/samples/acpi_handler.sh b/samples/acpi_handler.sh
new file mode 100755 (executable)
index 0000000..b2585a5
--- /dev/null
@@ -0,0 +1,24 @@
+#!/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
diff --git a/samples/battery/battery-conf b/samples/battery/battery-conf
new file mode 100644 (file)
index 0000000..246d36d
--- /dev/null
@@ -0,0 +1,10 @@
+# /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
diff --git a/samples/battery/battery.sh b/samples/battery/battery.sh
new file mode 100644 (file)
index 0000000..cb86156
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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
diff --git a/samples/panasonic/ac_adapt.pl b/samples/panasonic/ac_adapt.pl
new file mode 100644 (file)
index 0000000..2d4367c
--- /dev/null
@@ -0,0 +1,95 @@
+#!/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}();
diff --git a/samples/panasonic/ac_adapter b/samples/panasonic/ac_adapter
new file mode 100644 (file)
index 0000000..7a75ecc
--- /dev/null
@@ -0,0 +1,5 @@
+# /etc/acpi/events/hotkey
+# This script handles hotkey events on Panasonic notebooks
+
+event=ac_adapter.*
+action=/etc/acpi/ac_adapt.pl %e
diff --git a/samples/panasonic/hotkey b/samples/panasonic/hotkey
new file mode 100644 (file)
index 0000000..6562fbc
--- /dev/null
@@ -0,0 +1,5 @@
+# /etc/acpi/events/hotkey
+# This script handles hotkey events on Panasonic notebooks
+
+event=HKEY.*
+action=/etc/acpi/hotkey.pl %e
diff --git a/samples/panasonic/hotkey.pl b/samples/panasonic/hotkey.pl
new file mode 100644 (file)
index 0000000..30f110d
--- /dev/null
@@ -0,0 +1,301 @@
+#!/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}();
diff --git a/samples/power b/samples/power
new file mode 100644 (file)
index 0000000..72c1dbd
--- /dev/null
@@ -0,0 +1,7 @@
+# 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
+
diff --git a/samples/power.sh b/samples/power.sh
new file mode 100755 (executable)
index 0000000..2e02157
--- /dev/null
@@ -0,0 +1,10 @@
+#!/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"
+
diff --git a/samples/powerbtn/powerbtn-conf b/samples/powerbtn/powerbtn-conf
new file mode 100644 (file)
index 0000000..8347570
--- /dev/null
@@ -0,0 +1,13 @@
+# /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
diff --git a/samples/powerbtn/powerbtn.sh b/samples/powerbtn/powerbtn.sh
new file mode 100755 (executable)
index 0000000..39a3d9b
--- /dev/null
@@ -0,0 +1,68 @@
+#!/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"
+
diff --git a/samples/powerbtn/powerbtn.sh.old b/samples/powerbtn/powerbtn.sh.old
new file mode 100755 (executable)
index 0000000..e5a3e35
--- /dev/null
@@ -0,0 +1,38 @@
+#!/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"
+
diff --git a/sock.c b/sock.c
new file mode 100644 (file)
index 0000000..2359fd9
--- /dev/null
+++ b/sock.c
@@ -0,0 +1,181 @@
+/*
+ *  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);
+}
diff --git a/sock.h b/sock.h
new file mode 100644 (file)
index 0000000..fe37028
--- /dev/null
+++ b/sock.h
@@ -0,0 +1,35 @@
+/*
+ *  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__ */
diff --git a/ud_socket.c b/ud_socket.c
new file mode 100644 (file)
index 0000000..9c8ac8d
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * $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;
+}
diff --git a/ud_socket.h b/ud_socket.h
new file mode 100644 (file)
index 0000000..a59db95
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ *$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