Initial commit.
Signed-off-by: NeilBrown <neilb@suse.de>
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <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.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <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 Lesser General
+Public License instead of this License.
--- /dev/null
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+PROGS = lsusd lsused request_suspend wakealarmd
+TESTS = block_test watch_test event_test alarm_test
+LIBS = suspend_block.o watcher.o wakeevent.o wakealarm.o
+
+all: $(PROGS) $(TESTS)
+
+lsusd: lsusd.o
+
+lsused: lsused.o libsus.a
+ $(CC) -o lsused lsused.o libsus.a -levent
+
+wakealarmd: wakealarmd.o libsus.a
+ $(CC) -o wakealarmd wakealarmd.o libsus.a -levent
+
+request_suspend: request_suspend.o
+
+block_test: block_test.o libsus.a
+ $(CC) -o block_test block_test.o libsus.a
+watch_test: watch_test.o libsus.a
+ $(CC) -o watch_test watch_test.o libsus.a -levent
+event_test: event_test.o libsus.a
+ $(CC) -o event_test event_test.o libsus.a -levent
+alarm_test: alarm_test.o libsus.a
+ $(CC) -o alarm_test alarm_test.o libsus.a -levent
+
+libsus.a: $(LIBS)
+ ar cr libsus.a $(LIBS)
+
+clean:
+ rm -f *.o *.a *.pyc $(PROGS) $(TESTS)
+
--- /dev/null
+
+This directory contains a prototype proof-of-concept system
+for managing suspend in Linux.
+Thus the Linux SUSpend Daemon.
+
+It contains:
+
+ lsusd:
+ The main daemon. It is written to run a tight loop and blocks as
+ required. It obeys the wakeup_count protocol to get race-free
+ suspend and allows clients to register to find out about
+ suspend and to block it either briefly or longer term.
+ It uses files in /var/run/suspend for all communication.
+
+ File are:
+
+ disabled: This file always exists. If any process holds a
+ shared flock(), suspend will not happen.
+ immediate: If this file exists, lsusd will try to suspend whenever
+ possible.
+ request: If this is created, then lsusd will try to suspend
+ once, and will remove the file when suspend completes or aborts.
+ watching: This is normally empty. Any process wanting to know
+ about suspend should take a shared flock and check the file is
+ still empty, and should watch for modification.
+ When suspend is imminent, lsusd creates 'watching-next', writes
+ a byte to 'watching' and waits for an exclusive lock on 'watching'.
+ Clients should move their lock to 'watching-next' when ready for
+ suspend.
+ When suspend completes, another byte (or 2) is written to
+ 'watching', and 'watching-next' is renamed over it. Clients can
+ use either of these to know that resume has happened.
+
+ watching-next: The file that will be 'watching' in the next awake cycle.
+
+ lsusd does not try to be event-loop based because:
+ - /sys/power/wakeup_count is not pollable. This could probably be
+ 'fixed' but I want code to work with today's kernel. It will probably
+ only block 100msec at most, but that might be too long???
+ - I cannot get an event notification when a lock is removed from a
+ file. :-( And I think locks are an excellent light-weight
+ mechanism for blocking suspend.
+
+ lsused:
+ This is an event-loop based daemon that can therefore easily handle
+ socket connections and client protocols which need prompt
+ response. It communicates with lsusd and provides extra
+ services to client.
+
+ lsused (which needs a better name) listens on the socket
+ /var/run/suspend/registration
+ A client may connect and send a list of file descriptors.
+ When a suspend is immanent, if any file descriptor is readable,
+ lsused will send a 'S' message to the client and await an 'R' reply
+ (S == suspend, R == ready). When all replies are in, lsused will
+ allow the suspend to complete. When it does (or aborts), it will send
+ 'A' (awake) to those clients to which it sent 'S'.
+
+ This allows a client to get a chance to handle any wakeup events,
+ but not to be woken unnecessarily on every suspend.
+
+ wakealarmd:
+ This allows clients to register on the socket.
+ /var/run/suspend/wakealarm
+ They write a timestamp in seconds since epoch, and will receive
+ a 'Now' message when that time arrives.
+ Between the time the connection is made and the time a "seconds"
+ number is written, suspend will be blocked.
+ Also between the time that "Now" is sent and when the socket is
+ closed, suspend is also blocked.
+
+ request_suspend:
+ A simple tool to create the 'request' file and then wait for it
+ to be removed.
+
+ libsus.a: A library of client-side interfaces.
+ suspend_open, suspend_block, suspend_allow, suspend_close:
+ easy interface to blocking suspend
+ suspend_watch, suspend_unwatch:
+ For use in libevent program to get notifications of
+ suspend and resume via the 'watching' file.
+ wake_set, wake_destory:
+ create a libevent event for an fd which is protected from
+ suspend. Whenever it is readable, suspend will not be entered.
+ wakealarm_set, wakealarm_destroy:
+ create a libevent event for a particular time which will
+ trigger even if system is suspend, and will protect against
+ suspend while event is happening.
+
+
+ block_test watch_test event_test alarm_test
+ simple test programs for the above interfaces.
+
+
+ suspend.py dnotify.py:
+ Sample code for detecting suspend/resume from python
+ block.sh test_block.sh:
+ Sample code for disabling suspend from shell.
+
+All code is available under GPLv2+. However if you ask for a different
+license I am unlikely to refuse (at least with the early prototype).
+
+Patches and comment are welcome, but please also feel free to include
+any of this in some more complete framework.
--- /dev/null
+
+
+/* Test wakealarm.
+ * argv is a number of seconds to wait for.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "libsus.h"
+
+void callback(int fd, short ev, void *data)
+{
+ printf("Ping - got the event\n");
+ suspend_block(-1);
+ event_loopbreak();
+}
+
+int main(int argc, char *argv[])
+{
+ time_t now;
+ time_t then;
+ int diff;
+ struct event *ev;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: alarm_test seconds\n");
+ exit(1);
+ }
+ diff = atoi(argv[1]);
+ time(&now);
+ then = now + diff;
+ event_init();
+
+ ev = wakealarm_set(then, callback, NULL);
+
+ event_loop(0);
+ printf("Hold off suspend for a while...\n");
+ sleep(4);
+ printf("OK - done\n");
+ exit(0);
+}
--- /dev/null
+
+# shell functions to allow suspend to be blocked.
+#
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+block_suspend() {
+ exec 9< /var/run/suspend/disabled
+ flock --shared 9
+}
+
+enable_suspend() {
+ flock --unlock 9
+ exec 9<&-
+}
+
--- /dev/null
+/*
+ * block_test - test program for blocking suspend
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libsus.h"
+
+main(int argc, char *argv[])
+{
+ int sleep_time = 5;
+ int handle;
+ if (argc == 2)
+ sleep_time = atoi(argv[1]);
+
+ handle = suspend_block(-1);
+ printf("Have the block - waiting\n");
+ sleep(sleep_time);
+ printf("Releasing...\n");
+ suspend_allow(handle);
+ sleep(1);
+ handle = suspend_block(handle);
+ printf("Blocked again\n");
+ sleep(sleep_time);
+ suspend_close(handle);
+ printf("OK all done\n");
+ exit(1);
+}
--- /dev/null
+#!/usr/bin/env python
+
+# class to allow watching multiple files and
+# calling a callback when any change (size or mtime)
+#
+# We take exclusive use of SIGIO and maintain a global list of
+# watched files.
+# As we cannot get siginfo in python, we check every file
+# every time we get a signal.
+# we report change is size, mtime, or ino of the file (given by name)
+
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+import os, fcntl, signal
+
+
+dirlist = []
+def notified(sig, stack):
+ for d in dirlist:
+ fcntl.fcntl(d.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
+ fcntl.DN_CREATE|fcntl.DN_DELETE))
+ d.check()
+
+class dir():
+ def __init__(self, dname):
+ self.dname = dname
+ self.fd = os.open(dname, 0)
+ self.files = []
+ self.callbacks = []
+ fcntl.fcntl(self.fd, fcntl.F_NOTIFY, (fcntl.DN_MODIFY|fcntl.DN_RENAME|
+ fcntl.DN_CREATE|fcntl.DN_DELETE))
+ if not dirlist:
+ signal.signal(signal.SIGIO, notified)
+ dirlist.append(self)
+
+ def watch(self, fname, callback):
+ f = file(os.path.join(self.dname, fname), callback)
+ self.files.append(f)
+ return f
+
+ def watchall(self, callback):
+ self.callbacks.append(callback)
+
+ def check(self):
+ newlist = []
+ for c in self.callbacks:
+ if c():
+ newlist.append(c)
+ self.callbacks = newlist
+
+ for f in self.files:
+ f.check()
+
+ def cancel(self, victim):
+ if victim in self.files:
+ self.files.remove(victim)
+
+class file():
+ def __init__(self, fname, callback):
+ self.name = fname
+ try:
+ stat = os.stat(self.name)
+ except OSError:
+ self.ino = 0
+ self.size = 0
+ self.mtime = 0
+ else:
+ self.ino = stat.st_ino
+ self.size = stat.st_size
+ self.mtime = stat.st_mtime
+ self.callback = callback
+
+ def check(self):
+ try:
+ stat = os.stat(self.name)
+ except OSError:
+ if self.ino == 0:
+ return False
+ self.size = 0
+ self.mtime = 0
+ self.ino = 0
+ else:
+ if stat.st_size == self.size and stat.st_mtime == self.mtime \
+ and stat.st_ino == self.ino:
+ return False
+ self.size = stat.st_size
+ self.mtime = stat.st_mtime
+ self.ino = stat.st_ino
+
+ self.callback(self)
+ return True
+
+ def cancel(self):
+ global dirlist
+ for d in dirlist:
+ d.cancel(self)
+
+
+if __name__ == "__main__" :
+ import signal
+
+
+ ##
+ def ping(f): print "got ", f.name
+
+ d = dir("/tmp/n")
+ a = d.watch("a", ping)
+ b = d.watch("b", ping)
+ c = d.watch("c", ping)
+
+ while True:
+ signal.pause()
--- /dev/null
+
+/*
+ * test handling wakeup events on fd.
+ * You will need to strace things and watch to get any real
+ * feedback as this only does anything interesting when there
+ * is a race.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <event.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include "libsus.h"
+
+void read_event(int fd, short ev, void *data)
+{
+ char buf[80];
+ int n;
+ int i;
+
+ printf("Can read now .. give it a moment though\n");
+ sleep(5);
+ n = read(fd, buf, 80);
+ if (n < 0)
+ exit(1);
+
+ for (i = 0; i < n ; i++)
+ printf(" %02x", buf[i] & 0xff);
+ printf("\n");
+}
+
+main(int argc, char *argv[])
+{
+ int fd;
+ struct event *ev;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: event_test devicename\n");
+ exit(1);
+ }
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ perror(argv[1]);
+ exit(1);
+ }
+
+ event_init();
+ event_priority_init(3);
+ ev = wake_set(fd, read_event, NULL, 1);
+
+ event_loop(0);
+ exit(0);
+}
--- /dev/null
+/* headers for libsus - supporting suspend management.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+int suspend_open();
+int suspend_block(int handle);
+void suspend_allow(int handle);
+int suspend_close(int handle);
+
+
+void *suspend_watch(int (*will_suspend)(void *data),
+ void (*did_resume)(void *data),
+ void *data);
+void suspend_ok(void *han);
+void suspend_unwatch(void *v);
+
+struct event *wake_set(int fd, void(*fn)(int,short,void*),
+ void *data, int prio);
+void wake_destroy(struct event *ev);
+
+struct event *wakealarm_set(time_t when, void(*fn)(int, short, void*),
+ void *data);
+void wakealarm_destroy(struct event *ev);
+
+
--- /dev/null
+/*
+ * lsusd - Linus SUSpend daemon.
+ * This daemon enters suspend when required and allows clients
+ * to block suspend, request suspend, or be notified of suspend.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <dirent.h>
+
+static void alert_watchers(void)
+{
+ int fd;
+ char zero = 0;
+
+ fd = open("/var/run/suspend/watching-next",
+ O_RDWR|O_CREAT|O_TRUNC, 0640);
+ if (fd < 0)
+ return;
+ close(fd);
+ fd = open("/var/run/suspend/watching",
+ O_RDWR|O_CREAT|O_TRUNC, 0640);
+ if (fd < 0)
+ return;
+ if (write(fd, &zero, 1) != 1)
+ return;
+ flock(fd, LOCK_EX);
+ /* all watches must have moved to next file */
+ close(fd);
+}
+
+static void cycle_watchers(void)
+{
+ int fd;
+ char zero[2];
+
+ fd = open("/var/run/suspend/watching", O_RDWR|O_CREAT, 0640);
+ if (fd < 0)
+ return;
+ zero[0] = zero[1] = 0;
+ if (write(fd, zero, 2) != 2) {
+ close(fd);
+ return;
+ }
+ close(fd);
+ rename("/var/run/suspend/watching-next",
+ "/var/run/suspend/watching");
+}
+
+static int read_wakeup_count()
+{
+ int fd;
+ int n;
+ char buf[20];
+
+ fd = open("/sys/power/wakeup_count", O_RDONLY);
+ if (fd < 0)
+ return -1;
+ n = read(fd, buf, sizeof(buf)-1);
+ close(fd);
+ if (n < 0)
+ return -1;
+ buf[n] = 0;
+ return atoi(buf);
+}
+
+static int set_wakeup_count(int count)
+{
+ int fd;
+ char buf[20];
+ int n;
+
+ if (count < 0)
+ return 1; /* Something wrong - just suspend */
+
+ fd = open("/sys/power/wakeup_count", O_RDWR);
+ if (fd < 0)
+ return 1;
+
+ snprintf(buf, sizeof(buf), "%d", count);
+ n = write(fd, buf, strlen(buf));
+ close(fd);
+ if (n < 0)
+ return 0;
+ return 1;
+}
+
+static void catch(int sig)
+{
+ return;
+}
+
+static void wait_request(int dirfd)
+{
+ int found_immediate = 0;
+ int found_request = 0;
+
+ do {
+ DIR *dir;
+ struct dirent *de;
+ sigset_t set, oldset;
+ sigemptyset(&set);
+ sigaddset(&set, SIGIO);
+
+ sigprocmask(SIG_BLOCK, &set, &oldset);
+ signal(SIGIO, catch);
+
+ fcntl(dirfd, F_NOTIFY, DN_CREATE);
+
+ lseek(dirfd, 0L, 0);
+ dir = fdopendir(dup(dirfd));
+ while ((de = readdir(dir)) != NULL) {
+ if (strcmp(de->d_name, "immediate") == 0)
+ found_immediate = 1;
+ if (strcmp(de->d_name, "request") == 0)
+ found_request = 1;
+ }
+
+ if (!found_request && !found_immediate)
+ sigsuspend(&oldset);
+ closedir(dir);
+ signal(SIGIO, SIG_DFL);
+ sigprocmask(SIG_UNBLOCK, &set, &oldset);
+ } while (!found_immediate && !found_request);
+}
+
+static void do_suspend(void)
+{
+ int fd = open("/sys/power/state", O_RDWR);
+ if (fd >= 0) {
+ write(fd, "mem\n", 4);
+ close(fd);
+ } else
+ sleep(5);
+}
+
+main(int argc, char *argv)
+{
+ int dir;
+ int disable;
+
+ mkdir("/var/run/suspend", 0770);
+
+ dir = open("/var/run/suspend", O_RDONLY);
+ disable = open("/var/run/suspend/disabled", O_RDWR|O_CREAT, 0640);
+
+ if (dir < 0 || disable < 0)
+ exit(1);
+
+ while (1) {
+ int count;
+
+ /* Don't accept an old request */
+ unlink("/var/run/suspend/request");
+ wait_request(dir);
+ if (flock(disable, LOCK_EX|LOCK_NB) != 0) {
+ flock(disable, LOCK_EX);
+ flock(disable, LOCK_UN);
+ unlink("/var/run/suspend/request");
+ /* blocked - so need to ensure request still valid */
+ continue;
+ }
+ /* we got that without blocking but are not holding it */
+
+ /* Next two might block, but that doesn't abort suspend */
+ count = read_wakeup_count();
+ alert_watchers();
+
+ if (flock(disable, LOCK_EX|LOCK_NB) == 0
+ && set_wakeup_count(count))
+ do_suspend();
+ flock(disable, LOCK_UN);
+ cycle_watchers();
+ }
+}
--- /dev/null
+
+/*
+ * lsused - Linux SUSpend Event monitoring Daemon
+ *
+ * apps and services can send fds to this daemon.
+ * We will not allow suspend to happen if any fd is readable.
+ * A client should also notice that it is readable, take a
+ * shared lock on the suspend/disabled file and then read the event.
+ *
+ * The client opens connects on a unix domain socket to
+ * /var/run/suspend/registration
+ * It sets 'W' with some fds attached to be watched.
+ * On notification if any fds are readable we send by 'S' to say
+ * Suspend Soon and wait for 'R' to say 'Ready'.
+ * We don't bother checking the fds again until the next suspend
+ * attempt.
+ *
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <event.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "libsus.h"
+
+
+struct handle {
+ struct event ev;
+ int sent; /* 'S' has been sent */
+ int suspending; /* ... 'R' hasn't been received yet */
+ struct handle *next;
+ struct state *state;
+};
+
+struct state {
+ int waiting; /* Number of replies waiting for */
+ struct handle *handles; /* linked list of handles */
+ struct pollfd *fds; /* for 'poll' */
+ struct handle **hans; /* aligned with fds */
+ int nfds; /* number of active 'fds' */
+ int fdsize; /* allocated size of fds array */
+ void *sus; /* handle from suspend_watch */
+};
+
+static void del_fd(struct state *state, int i)
+{
+ state->fds[i] = state->fds[state->nfds - 1];
+ state->hans[i] = state->hans[state->nfds - 1];
+ state->nfds--;
+}
+
+static void add_fd(struct state *state, struct handle *han,
+ int fd, short events)
+{
+ int n = state->nfds;
+ if (state->nfds >= state->fdsize) {
+ /*need to make bigger */
+ int need = 16;
+ while (need < n+1)
+ need *= 2;
+ state->fds = realloc(state->fds,
+ need * sizeof(struct pollfd));
+ state->hans = realloc(state->hans,
+ need * sizeof(struct handle *));
+ }
+ state->hans[n] = han;
+ state->fds[n].fd = fd;
+ state->fds[n].events = events;
+ state->nfds++;
+}
+
+static void add_han(struct handle *han, struct state *state)
+{
+
+ han->next = state->handles;
+ state->handles = han;
+}
+
+static void del_han(struct handle *han)
+{
+ struct state *state = han->state;
+ struct handle **hanp = &state->handles;
+ struct handle *h;
+ int i;
+
+ /* First remove the fds; */
+ for (i = 0; i < state->nfds ; i++)
+ if (state->hans[i] == han) {
+ del_fd(state, i);
+ i--;
+ }
+
+ /* Then remove the han */
+ for (h = *hanp; h; hanp = &h->next, h = *hanp) {
+ if (h == han) {
+ *hanp = h->next;
+ break;
+ }
+ }
+}
+
+static void do_read(int fd, short ev, void *data)
+{
+ struct handle *han = data;
+ char buf;
+ struct msghdr msg;
+ struct cmsghdr *cm;
+ struct iovec iov;
+ char mbuf[100];
+
+ buf = 0;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ iov.iov_base = &buf;
+ iov.iov_len = 1;
+ msg.msg_control = mbuf;
+ msg.msg_controllen = sizeof(mbuf);
+ msg.msg_flags = 0;
+
+ if (recvmsg(fd, &msg, MSG_CMSG_CLOEXEC|MSG_DONTWAIT) < 0
+ && errno == EAGAIN)
+ return;
+
+ switch (buf) {
+ case 'W':
+ for (cm = CMSG_FIRSTHDR(&msg);
+ cm != NULL;
+ cm = CMSG_NXTHDR(&msg, cm))
+ if (cm->cmsg_level == SOL_SOCKET &&
+ cm->cmsg_type == SCM_RIGHTS) {
+ int *fdptr = (int*)CMSG_DATA(cm);
+ int n = (cm->cmsg_len -
+ CMSG_ALIGN(sizeof(struct cmsghdr)))
+ / sizeof(int);
+ int i;
+ for (i = 0; i < n; i++)
+ add_fd(han->state, han, fdptr[i],
+ POLLIN|POLLPRI);
+ }
+ write(fd, "A", 1);
+ break;
+
+ case 'R':
+ if (han->suspending) {
+ han->suspending = 0;
+ han->state->waiting--;
+ if (han->state->waiting == 0)
+ suspend_ok(han->state->sus);
+ }
+ break;
+
+ default:
+ event_del(&han->ev);
+ del_han(han);
+ close(fd);
+ }
+}
+
+static void do_accept(int fd, short ev, void *data)
+{
+ struct state *state = data;
+ struct handle *han;
+ int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+ if (newfd < 0)
+ return;
+
+ han = malloc(sizeof(*han));
+ if (!han) {
+ close(newfd);
+ return;
+ }
+ han->sent = 0;
+ han->suspending = 0;
+ han->state = state;
+ add_han(han, state);
+ event_set(&han->ev, newfd, EV_READ | EV_PERSIST, do_read, han);
+ event_add(&han->ev, NULL);
+ write(newfd, "A", 1);
+}
+
+static int do_suspend(void *data)
+{
+ struct state *state = data;
+ struct handle *han;
+ int n;
+ int i;
+
+ n = poll(state->fds, state->nfds, 0);
+ if (n == 0)
+ /* nothing happening */
+ return 1;
+ for (han = state->handles ; han ; han = han->next)
+ han->sent = 0;
+ state->waiting = 1;
+ for (i = 0; i < state->nfds; i++)
+ if (state->fds[i].revents) {
+ han = state->hans[i];
+ if (!han->sent) {
+ han->sent = 1;
+ han->suspending = 1;
+ write(EVENT_FD(&han->ev), "S", 1);
+ state->waiting++;
+ }
+ }
+ state->waiting--;
+ return (state->waiting == 0);
+}
+
+static void did_resume(void *data)
+{
+ struct state *state = data;
+ struct handle *han;
+
+ for (han = state->handles ; han ; han = han->next)
+ if (han->sent)
+ write(EVENT_FD(&han->ev), "A", 1);
+}
+
+main(int argc, char *argv[])
+{
+ struct sockaddr_un addr;
+ struct state state;
+ struct event ev;
+ int s;
+
+ memset(&state, 0, sizeof(state));
+
+ s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/var/run/suspend/registration");
+ unlink("/var/run/suspend/registration");
+ bind(s, (struct sockaddr *)&addr, sizeof(addr));
+ listen(s, 20);
+
+ event_init();
+
+ state.sus = suspend_watch(do_suspend, did_resume, &state);
+ event_set(&ev, s, EV_READ | EV_PERSIST, do_accept, &state);
+ event_add(&ev, NULL);
+
+ event_loop(0);
+ exit(0);
+}
--- /dev/null
+/* Request suspend
+ * Create the suspend-request file, then wait for it to be deleted.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#define _GNU_SOURCE
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+
+static void catch(int sig)
+{
+ return;
+}
+
+main(int argc, char *argv[])
+{
+ int dirfd = open("/var/run/suspend", O_RDONLY);
+ int fd_request = open("/var/run/suspend/request",
+ O_RDWR|O_CREAT, 0640);
+ if (fd_request < 0)
+ exit(1);
+
+
+ if (dirfd < 0)
+ exit(1);
+ /* Wait for unlink */
+ while (1) {
+ int fd;
+ struct stat stat;
+ sigset_t set, oldset;
+ sigemptyset(&set);
+ sigaddset(&set, SIGIO);
+
+ sigprocmask(SIG_BLOCK, &set, &oldset);
+ signal(SIGIO, catch);
+
+ fcntl(dirfd, F_NOTIFY, DN_DELETE);
+
+ if (fstat(fd_request, &stat) != 0
+ || stat.st_nlink == 0)
+ exit(0);
+ fd = open("/var/run/suspend/disabled", O_RDONLY);
+ if (fd >= 0) {
+ if (flock(fd, LOCK_EX|LOCK_NB) != 0) {
+ /* Someone is blocking suspend */
+ unlink("/var/run/suspend/request");
+ exit(1);
+ }
+ close(fd);
+ }
+
+ sigsuspend(&oldset);
+ sigprocmask(SIG_UNBLOCK, &set, &oldset);
+ }
+}
--- /dev/null
+
+#
+# interact with lsusd to provide suspend notification
+#
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+import dnotify, fcntl, os
+
+lock_watcher = None
+
+class monitor:
+ def __init__(self, suspend_callback, resume_callback):
+ """
+ Arrange that suspend_callback is called before we suspend, and
+ resume_callback is called when we resume.
+ If suspend_callback returns False, it must have arranged for
+ 'release' to be called soon to allow suspend to continue.
+ """
+ global lock_watcher
+ if not lock_watcher:
+ lock_watcher = dnotify.dir('/var/run/suspend')
+
+ self.f = open('/var/run/suspend/watching', 'r')
+ self.getlock()
+ while os.fstat(self.f.fileno()).st_nlink == 0:
+ self.f.close()
+ self.f = open('/var/run/suspend/watching', 'r')
+ self.getlock()
+
+ self.suspended = False
+ self.suspend = suspend_callback
+ self.resume = resume_callback
+ self.watch = lock_watcher.watch("watching", self.change)
+
+ def getlock(self):
+ # lock file, protecting againt getting IOError when we get signalled.
+ locked = False
+ while not locked:
+ try:
+ fcntl.flock(self.f, fcntl.LOCK_SH)
+ locked = True
+ except IOError:
+ pass
+
+ def change(self, watched):
+ if os.fstat(self.f.fileno()).st_size == 0:
+ if self.suspended and os.stat('/var/run/suspend/watching').st_size == 0:
+ self.suspended = False
+ if self.resume:
+ self.resume()
+ return
+ if not self.suspended and (not self.suspend or self.suspend()):
+ # ready for suspend
+ self.release()
+
+ def release(self):
+ # ready for suspend
+ old = self.f
+ self.f = open('/var/run/suspend/watching-next', 'r')
+ self.getlock()
+ self.suspended = True
+ fcntl.flock(old, fcntl.LOCK_UN)
+ old.close()
+
+
+
+if __name__ == '__main__':
+ import signal
+ def sus(): print "Suspending"; return True
+ def res(): print "Resuming"
+ monitor(sus, res)
+ print "ready"
+ while True:
+ signal.pause()
--- /dev/null
+/*
+ * Library routine to block and re-enable suspend.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include "libsus.h"
+
+int suspend_open()
+{
+ return open("/var/run/suspend/disabled", O_RDONLY|O_CLOEXEC);
+}
+
+int suspend_block(int handle)
+{
+ if (handle < 0)
+ handle = suspend_open();
+ if (handle < 0)
+ return handle;
+
+ flock(handle, LOCK_SH);
+ return handle;
+}
+
+void suspend_allow(int handle)
+{
+ flock(handle, LOCK_UN);
+}
+
+int suspend_close(int handle)
+{
+ if (handle >= 0)
+ close(handle);
+}
+
--- /dev/null
+#!/bin/sh
+# Copyright (C) 2011 Neil Brown <neilb@suse.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+source ./block.sh
+
+block_suspend
+
+echo now blocked.
+sleep 10
+echo allowing now.
+
+enable_suspend
--- /dev/null
+
+/*
+ * Library code to allow libevent app to register for a wake alarm
+ * and register with wakealarmd to keep suspend at bay for the time.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <event.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "libsus.h"
+
+struct han {
+ struct event ev;
+ int sock;
+ int disable;
+ void (*fn)(int,short,void*);
+ void *data;
+};
+
+static void alarm_clock(int fd, short ev, void *data)
+{
+ char buf[20];
+ int n;
+ struct han *h = data;
+
+ n = read(fd, buf, sizeof(buf)-1);
+ if (n < 0 && errno == EAGAIN)
+ return;
+ if (n <= 0 ||
+ strncmp(buf, "Now", 3) == 0) {
+ h->fn(-1, ev, h->data);
+ wakealarm_destroy(&h->ev);
+ }
+ /* Some other message, keep waiting */
+}
+
+struct event *wakealarm_set(time_t when, void(*fn)(int, short, void*),
+ void *data)
+{
+ struct sockaddr_un addr;
+ struct han *h = malloc(sizeof(*h));
+ char buf[20];
+
+ if (!h)
+ return NULL;
+
+ h->fn = fn;
+ h->data = data;
+ h->disable = suspend_open();
+ h->sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (h->sock < 0 || h->disable < 0)
+ goto abort;
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/var/run/suspend/wakealarm");
+ if (connect(h->sock, (struct sockaddr*)&addr, sizeof(addr)) != 0)
+ goto abort;
+
+ fcntl(h->sock, F_SETFL, fcntl(h->sock, F_GETFL, 0) | O_NONBLOCK);
+ sprintf(buf, "%lld\n", (long long)when);
+ write(h->sock, buf, strlen(buf));
+
+ event_set(&h->ev, h->sock, EV_READ|EV_PERSIST, alarm_clock, h);
+ event_add(&h->ev, NULL);
+
+ return &h->ev;
+
+abort:
+ suspend_close(h->disable);
+ if (h->sock >= 0)
+ close(h->sock);
+ free(h);
+ return NULL;
+}
+
+void wakealarm_destroy(struct event *ev)
+{
+ struct han *h = (struct han *)ev;
+ event_del(&h->ev);
+ close(h->sock);
+ suspend_close(h->disable);
+ free(h);
+}
--- /dev/null
+
+/* wake alarm service.
+ * We provide a wakeup service to use the rtc wakealarm
+ * to ensure the system is running at given times and to
+ * alert clients.
+ *
+ * Client can connect and register a time as seconds since epoch
+ * We echo back the time and then when the time comes we echo "Now".
+ * We keep system awake until another time is written, or until
+ * connection is closed.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <event.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "libsus.h"
+
+struct conn {
+ struct event ev;
+ struct event tev;
+ time_t stamp; /* When to wake */
+ int active; /* stamp has passed */
+ struct conn *next; /* sorted by 'stamp' */
+ struct state *state;
+};
+
+struct state {
+ struct event ev;
+ int disablefd;
+ int disabled;
+ void *watcher;
+ struct conn *conns;
+ int active_count;
+};
+
+static void add_han(struct conn *han)
+{
+ struct state *state = han->state;
+ struct conn **hanp = &state->conns;
+ struct timeval tv;
+ time_t now;
+
+ while (*hanp &&
+ (*hanp)->stamp < han->stamp)
+ hanp = &(*hanp)->next;
+ han->next = *hanp;
+ *hanp = han;
+
+ time(&now);
+ if (now < han->stamp) {
+ tv.tv_sec = han->stamp - now;
+ tv.tv_usec = 0;
+ evtimer_add(&han->tev, &tv);
+ han->active = 0;
+ } else {
+ if (!han->active) {
+ han->active = 1;
+ state->active_count++;
+ }
+ }
+}
+
+static void del_han(struct conn *han)
+{
+ struct state *state = han->state;
+ struct conn **hanp = &state->conns;
+
+ while (*hanp &&
+ *hanp != han)
+ hanp = &(*hanp)->next;
+ if (*hanp == han) {
+ *hanp = han->next;
+ if (han->active) {
+ state->active_count--;
+ if (state->active_count == 0
+ && state->disabled) {
+ suspend_allow(state->disablefd);
+ state->disabled = 0;
+ }
+ }
+ }
+}
+
+static void destroy_han(struct conn *han)
+{
+ event_del(&han->ev);
+ event_del(&han->tev);
+ free(han);
+}
+
+static void do_read(int fd, short ev, void *data)
+{
+ struct conn *han = data;
+ char buf[20];
+ int n;
+
+ n = read(fd, buf, sizeof(buf)-1);
+ if (n < 0 && errno == EAGAIN)
+ return;
+ if (n <= 0) {
+ del_han(han);
+ destroy_han(han);
+ return;
+ }
+ del_han(han);
+ han->stamp = atol(buf);
+ add_han(han);
+ sprintf(buf, "%lld\n", (long long)han->stamp);
+ write(fd, buf, strlen(buf));
+}
+
+static void do_timeout(int fd, short ev, void *data)
+{
+ struct conn *han = data;
+
+ if (!han->active) {
+ han->active = 1;
+ han->state->active_count++;
+ }
+ write(EVENT_FD(&han->ev), "Now\n", 4);
+}
+
+static void do_accept(int fd, short ev, void *data)
+{
+ struct state *state = data;
+ struct conn *han;
+ int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC);
+
+ if (newfd < 0)
+ return;
+ han = malloc(sizeof(*han));
+ if (!han) {
+ close(newfd);
+ return;
+ }
+ han->state = state;
+ han->stamp = 0;
+ han->active = 1;
+ state->active_count++;
+ han->next = state->conns;
+ state->conns = han;
+
+ evtimer_set(&han->tev, do_timeout, han);
+ event_set(&han->ev, newfd, EV_READ | EV_PERSIST, do_read, han);
+ event_add(&han->ev, NULL);
+ write(newfd, "0\n", 2);
+}
+
+static int do_suspend(void *data)
+{
+ struct state *state = data;
+ time_t now;
+
+ time(&now);
+
+ /* active_count must be zero */
+ if (state->conns == NULL)
+ return 1;
+
+ if (state->conns->stamp > now + 4) {
+ int fd = open("/sys/class/rtc/rtc0/wakealarm", O_WRONLY);
+ if (fd >= 0) {
+ char buf[20];
+ sprintf(buf, "%lld\n",
+ (long long)state->conns->stamp - 2);
+ write(fd, buf, strlen(buf));
+ close(fd);
+ }
+ return 1;
+ }
+ /* too close to next wakeup */
+ if (state->disabled) {
+ suspend_block(state->disablefd);
+ state->disabled = 1;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct state st;
+ struct sockaddr_un addr;
+ int s;
+
+ st.disablefd = suspend_open();
+ st.disabled = 0;
+ st.conns = NULL;
+ st.active_count = 0;
+
+ s = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK|SOCK_CLOEXEC, 0);
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/var/run/suspend/wakealarm");
+ unlink("/var/run/suspend/wakealarm");
+ bind(s, (struct sockaddr *)&addr, sizeof(addr));
+ listen(s, 20);
+
+ event_init();
+ st.watcher = suspend_watch(do_suspend, NULL, &st);
+ event_set(&st.ev, s, EV_READ | EV_PERSIST, do_accept, &st);
+ event_add(&st.ev, NULL);
+
+ event_loop(0);
+ exit(0);
+}
--- /dev/null
+
+/* Register an fd which produces wake events with
+ * eventlib.
+ * Whenever the fd is readable, we block suspend,
+ * call the handler, then allow suspend.
+ * Meanwhile we open a socket to the event daemon passing
+ * it the same fd.
+ * At a lower priority, when we read 'S' from the daemon we reply
+ * with 'R'.
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <event.h>
+#include <fcntl.h>
+#include <errno.h>
+#include "libsus.h"
+
+struct han {
+ struct event ev;
+ struct event sev;
+ int sock;
+ int disable;
+ void (*fn)(int,short,void*);
+ void *data;
+};
+
+static void wakeup_call(int fd, short ev, void *data)
+{
+ /* A (potential) wakeup event can be read from this fd.
+ * We won't go to sleep because we haven't replied to
+ * 'S' yet as that is handle with a lower priority.
+ */
+ struct han *han = data;
+ han->fn(fd, ev, han->data);
+}
+
+static void wakeup_sock(int fd, short ev, void *data)
+{
+ char buf;
+ struct han *han = data;
+ int n = read(fd, &buf, 1);
+
+ if (n < 0 && errno == EAGAIN)
+ return;
+ if (n != 1) {
+ /* How do I signal an error ?*/
+ event_del(&han->sev);
+ return;
+ }
+ if (buf == 'S')
+ /* As we are at a lower priority (higher number)
+ * than the main event, we must have handled everything
+ */
+ write(fd, "R", 1);
+}
+
+static void send_fd(int sock, int fd)
+{
+ struct msghdr msg = {0};
+ struct iovec iov;
+ struct cmsghdr *cmsg;
+ int myfds[1];
+ char buf[CMSG_SPACE(sizeof myfds)];
+ int *fdptr;
+
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof buf;
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+ fdptr = (int*)CMSG_DATA(cmsg);
+ fdptr[0] = fd;
+ msg.msg_controllen = cmsg->cmsg_len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ iov.iov_base = "W";
+ iov.iov_len = 1;
+ sendmsg(sock, &msg, 0);
+}
+
+struct event *wake_set(int fd, void(*fn)(int,short,void*), void *data, int prio)
+{
+ struct sockaddr_un addr;
+ struct han *h = malloc(sizeof(*h));
+
+ if (!h)
+ return NULL;
+
+ h->fn = fn;
+ h->data = data;
+ h->disable = suspend_open();
+ h->sock = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (h->sock < 0 || h->disable < 0)
+ goto abort;
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, "/var/run/suspend/registration");
+ if (connect(h->sock, (struct sockaddr*)&addr, sizeof(addr)) != 0)
+ goto abort;
+
+ fcntl(h->sock, F_SETFL, fcntl(h->sock, F_GETFL, 0) | O_NONBLOCK);
+
+ send_fd(h->sock, fd);
+
+ event_set(&h->ev, fd, EV_READ|EV_PERSIST, wakeup_call, h);
+ event_set(&h->sev, h->sock, EV_READ|EV_PERSIST, wakeup_sock, h);
+ event_priority_set(&h->ev, prio);
+ event_priority_set(&h->sev, prio+1);
+ event_add(&h->ev, NULL);
+ event_add(&h->sev, NULL);
+
+ return &h->ev;
+
+abort:
+ suspend_close(h->disable);
+ if (h->sock >= 0)
+ close(h->sock);
+ free(h);
+ return NULL;
+}
+
+void wake_destroy(struct event *ev)
+{
+ struct han *h = (struct han *)ev;
+ event_del(&h->ev);
+ event_del(&h->sev);
+ close(h->sock);
+ suspend_close(h->disable);
+ free(h);
+}
--- /dev/null
+
+/* Test watching for suspend notifications
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdlib.h>
+#include <event.h>
+#include <stdio.h>
+#include "libsus.h"
+
+static int suspend(void *m)
+{
+ char *msg = m;
+ printf("Suspend: %s\n", msg);
+ printf("Hang on while I tie my shoe laces...\n");
+ sleep(3);
+ printf("... OK, Done\n");
+ return 1;
+}
+
+static void resume(void *m)
+{
+ char *msg = m;
+ printf("Resume: %s\n", msg);
+}
+
+
+main(int argc, char *argv[])
+{
+
+ event_init();
+
+ suspend_watch(suspend, resume, "I saw that");
+
+ event_loop(0);
+ exit(0);
+}
--- /dev/null
+
+/*
+ * Use libevent to watch for suspends and take action.
+ * The calling program must already have a libevent loop running.
+ * One or two callbacks are registered with suspend_watch.
+ * The first is required and gets called just before suspend.
+ * It must return promptly but may call suspend_block first.
+ * The second is options and will get called after resume.
+ *
+ *
+ * Copyright (C) 2011 Neil Brown <neilb@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <event.h>
+#include <sys/stat.h>
+#include <malloc.h>
+
+struct cb {
+ int (*will_suspend)(void *data);
+ void (*did_resume)(void *data);
+ void *data;
+ int dirfd;
+ int fd, nextfd;
+ struct event ev;
+};
+
+static void checkdir(int efd, short ev, void *vp)
+{
+ struct cb *han = vp;
+ struct stat stb;
+ int fd;
+ int rv;
+
+ if (han->fd < 0)
+ /* too early */
+ return;
+
+ if (han->nextfd >= 0) {
+ /*suspended - maybe not any more */
+ fstat(han->fd, &stb);
+ if (stb.st_size <= 0)
+ /*false alarm */
+ return;
+ /* back from resume */
+ close(han->fd);
+ han->fd = han->nextfd;
+ han->nextfd = -1;
+ if (han->did_resume)
+ han->did_resume(han->data);
+ /* Fall through incase suspend has started again */
+ }
+
+ /* not suspended yet */
+ if (fstat(han->fd, &stb) == 0
+ && stb.st_size == 0)
+ /* false alarm */
+ return;
+
+ /* We need to move on now. */
+ fd = open("/var/run/suspend/watching-next", O_RDONLY|O_CLOEXEC);
+ flock(fd, LOCK_SH);
+ han->nextfd = fd;
+ rv = han->will_suspend(han->data);
+ if (rv)
+ suspend_ok(han);
+}
+
+int suspend_ok(void *v)
+{
+ struct cb *han = v;
+ flock(han->fd, LOCK_UN);
+ return 1;
+}
+
+void *suspend_watch(int (*will_suspend)(void *data),
+ void (*did_resume)(void *data),
+ void *data)
+{
+ struct cb *han = malloc(sizeof(*han));
+ struct stat stb;
+ int fd = -1;
+ if (!han)
+ return NULL;
+
+ han->data = data;
+ han->will_suspend = will_suspend;
+ han->did_resume = did_resume;
+ han->fd = -1;
+ han->nextfd = -1;
+ signal_set(&han->ev, SIGIO, checkdir, han);
+ signal_add(&han->ev, NULL);
+ han->dirfd = open("/var/run/suspend", O_RDONLY|O_CLOEXEC);
+ if (han->dirfd < 0)
+ goto abort;
+ fcntl(han->dirfd, F_NOTIFY, DN_MODIFY | DN_MULTISHOT);
+again:
+ fd = open("/var/run/suspend/watching", O_RDONLY|O_CLOEXEC);
+ flock(fd, LOCK_SH);
+ han->fd = fd;
+ checkdir(0, 0, han);
+ /* OK, he won't suspend until I say OK. */
+
+ return han;
+abort:
+ signal_del(&han->ev);
+ if (fd >= 0)
+ close(fd);
+ if (han->dirfd >= 0)
+ close(han->dirfd);
+ free(han);
+}
+
+void suspend_unwatch(void *v)
+{
+ struct cb *han = v;
+ if (han->dirfd >= 0)
+ close(han->dirfd);
+ signal_del(&han->ev);
+ if (han->fd >= 0)
+ close(han->fd);
+ if (han->nextfd >= 0)
+ close(han->nextfd);
+ free(han);
+}
+