diff --git a/man/sbd.8.pod b/man/sbd.8.pod
index ffd01c2..dbb3855 100644
--- a/man/sbd.8.pod
+++ b/man/sbd.8.pod
@@ -1,649 +1,668 @@
 =head1 NAME
 
 sbd - STONITH Block Device daemon
 
 =head1 SYNOPSIS
 
 sbd <-d F</dev/...>> [options] C<command>
 
 =head1 SUMMARY
 
 SBD provides a node fencing mechanism (Shoot the other node in the head,
 STONITH) for Pacemaker-based clusters through the exchange of messages
 via shared block storage such as for example a SAN, iSCSI, FCoE. This
 isolates the fencing mechanism from changes in firmware version or
 dependencies on specific firmware controllers, and it can be used as a
 STONITH mechanism in all configurations that have reliable shared
 storage.
 
 SBD can also be used without any shared storage. In this mode, the
 watchdog device will be used to reset the node if it loses quorum, if
 any monitored daemon is lost and not recovered or if Pacemaker decides
 that the node requires fencing.
 
 The F<sbd> binary implements both the daemon that watches the message
 slots as well as the management tool for interacting with the block
 storage device(s). This mode of operation is specified via the
 C<command> parameter; some of these modes take additional parameters.
 
 To use SBD with shared storage, you must first C<create> the messaging
 layout on one to three block devices. Second, configure
 F</etc/sysconfig/sbd> to list those devices (and possibly adjust other
 options), and restart the cluster stack on each node to ensure that
 C<sbd> is started. Third, configure the C<external/sbd> fencing
 resource in the Pacemaker CIB.
 
 Each of these steps is documented in more detail below the description
 of the command options.
 
 C<sbd> can only be used as root.
 
 =head2 GENERAL OPTIONS
 
 =over
 
 =item B<-d> F</dev/...>
 
 Specify the block device(s) to be used. If you have more than one,
 specify this option up to three times. This parameter is mandatory for
 all modes, since SBD always needs a block device to interact with.
 
 This man page uses F</dev/sda1>, F</dev/sdb1>, and F</dev/sdc1> as
 example device names for brevity. However, in your production
 environment, you should instead always refer to them by using the long,
 stable device name (e.g.,
 F</dev/disk/by-id/dm-uuid-part1-mpath-3600508b400105b5a0001500000250000>).
 
 =item B<-v>
 
 Enable some verbose debug logging.
 
 =item B<-h>
 
 Display a concise summary of C<sbd> options.
 
 =item B<-n> I<node>
 
 Set local node name; defaults to C<uname -n>. This should not need to be
 set.
 
 =item B<-R>
 
 Do B<not> enable realtime priority. By default, C<sbd> runs at realtime
 priority, locks itself into memory, and also acquires highest IO
 priority to protect itself against interference from other processes on
 the system. This is a debugging-only option.
 
 =item B<-I> I<N>
 
 Async IO timeout (defaults to 3 seconds, optional). You should not need
 to adjust this unless your IO setup is really very slow.
 
 (In daemon mode, the watchdog is refreshed when the majority of devices
 could be read within this time.)
 
 =back
 
 =head2 create
 
 Example usage:
 
 	sbd -d /dev/sdc2 -d /dev/sdd3 create
 
 If you specify the I<create> command, sbd will write a metadata header
 to the device(s) specified and also initialize the messaging slots for
 up to 255 nodes.
 
 B<Warning>: This command will not prompt for confirmation. Roughly the
 first megabyte of the specified block device(s) will be overwritten
 immediately and without backup.
 
 This command accepts a few options to adjust the default timings that
 are written to the metadata (to ensure they are identical across all
 nodes accessing the device).
 
 =over
 
 =item B<-1> I<N>
 
 Set watchdog timeout to N seconds. This depends mostly on your storage
 latency; the majority of devices must be successfully read within this
 time, or else the node will self-fence.
 
 If your sbd device(s) reside on a multipath setup or iSCSI, this should
 be the time required to detect a path failure. You may be able to reduce
 this if your device outages are independent, or if you are using the
 Pacemaker integration.
 
 =item B<-2> I<N>
 
 Set slot allocation timeout to N seconds. You should not need to tune
 this.
 
 =item B<-3> I<N>
 
 Set daemon loop timeout to N seconds. You should not need to tune this.
 
 =item B<-4> I<N>
 
 Set I<msgwait> timeout to N seconds. This should be twice the I<watchdog>
 timeout. This is the time after which a message written to a node's slot
 will be considered delivered. (Or long enough for the node to detect
 that it needed to self-fence.)
 
 This also affects the I<stonith-timeout> in Pacemaker's CIB; see below.
 
 =back
 
 =head2 list
 
 Example usage:
 
 	# sbd -d /dev/sda1 list
 	0	hex-0	clear
 	1	hex-7	clear
 	2	hex-9	clear
 
 List all allocated slots on device, and messages. You should see all
 cluster nodes that have ever been started against this device. Nodes
 that are currently running should have a I<clear> state; nodes that have
 been fenced, but not yet restarted, will show the appropriate fencing
 message.
 
 =head2 dump
 
 Example usage:
 
 	# sbd -d /dev/sda1 dump
 	==Dumping header on disk /dev/sda1
 	Header version     : 2
 	Number of slots    : 255
 	Sector size        : 512
 	Timeout (watchdog) : 15
 	Timeout (allocate) : 2
 	Timeout (loop)     : 1
 	Timeout (msgwait)  : 30
 	==Header on disk /dev/sda1 is dumped
 
 Dump meta-data header from device.
 
 =head2 watch
 
 Example usage:
 
 	sbd -d /dev/sdc2 -d /dev/sdd3 -P watch
 
 This command will make C<sbd> start in daemon mode. It will constantly monitor
 the message slot of the local node for incoming messages, reachability, and
 optionally take Pacemaker's state into account.
 
 C<sbd> B<must> be started on boot before the cluster stack! See below
 for enabling this according to your boot environment.
 
 The options for this mode are rarely specified directly on the
 commandline directly, but most frequently set via F</etc/sysconfig/sbd>.
 
 It also constantly monitors connectivity to the storage device, and
 self-fences in case the partition becomes unreachable, guaranteeing that it
 does not disconnect from fencing messages.
 
 A node slot is automatically allocated on the device(s) the first time
 the daemon starts watching the device; hence, manual allocation is not
 usually required.
 
 If a watchdog is used together with the C<sbd> as is strongly
 recommended, the watchdog is activated at initial start of the sbd
 daemon. The watchdog is refreshed every time the majority of SBD devices
 has been successfully read. Using a watchdog provides additional
 protection against C<sbd> crashing.
 
 If the Pacemaker integration is activated, C<sbd> will B<not> self-fence
 if device majority is lost, if:
 
 =over
 
 =item 1.
 
 The partition the node is in is still quorate according to the CIB;
 
 =item 2.
 
 it is still quorate according to Corosync's node count;
 
 =item 3.
 
 the node itself is considered online and healthy by Pacemaker.
 
 =back
 
 This allows C<sbd> to survive temporary outages of the majority of
 devices. However, while the cluster is in such a degraded state, it can
 neither successfully fence nor be shutdown cleanly (as taking the
 cluster below the quorum threshold will immediately cause all remaining
 nodes to self-fence). In short, it will not tolerate any further faults.
 Please repair the system before continuing.
 
 There is one C<sbd> process that acts as a master to which all watchers
 report; one per device to monitor the node's slot; and, optionally, one
 that handles the Pacemaker integration.
 
 =over
 
 =item B<-W>
 
 Enable or disable use of the system watchdog to protect against the sbd
 processes failing and the node being left in an undefined state. Specify
 this once to enable, twice to disable.
 
 Defaults to I<enabled>.
 
 =item B<-w> F</dev/watchdog>
 
 This can be used to override the default watchdog device used and should not
 usually be necessary.
 
 =item B<-p> F</var/run/sbd.pid>
 
 This option can be used to specify a pidfile for the main sbd process.
 
 =item B<-F> I<N>
 
 Number of failures before a failing servant process will not be restarted
 immediately until the dampening delay has expired. If set to zero, servants
 will be restarted immediately and indefinitely. If set to one, a failed
 servant will be restarted once every B<-t> seconds. If set to a different
 value, the servant will be restarted that many times within the dampening
 period and then delay.
 
 Defaults to I<1>.
 
 =item B<-t> I<N>
 
 Dampening delay before faulty servants are restarted. Combined with C<-F 1>,
 the most logical way to tune the restart frequency of servant processes.
 Default is 5 seconds.
 
 If set to zero, processes will be restarted indefinitely and immediately.
 
 =item B<-P>
 
 Enable Pacemaker integration which checks Pacemaker quorum and node health.
 Specify this once to enable, twice to disable.
 
 Defaults to I<enabled>.
 
 =item B<-S> I<N>
 
 Set the start mode. (Defaults to I<0>.)
 
 If this is set to zero, sbd will always start up unconditionally,
 regardless of whether the node was previously fenced or not.
 
 If set to one, sbd will only start if the node was previously shutdown
 cleanly (as indicated by an exit request message in the slot), or if the
 slot is empty. A reset, crashdump, or power-off request in any slot will
 halt the start up.
 
 This is useful to prevent nodes from rejoining if they were faulty. The
 node must be manually "unfenced" by sending an empty message to it:
 
 	sbd -d /dev/sda1 message node1 clear
 
 =item B<-s> I<N>
 
 Set the start-up wait time for devices. (Defaults to I<120>.)
 
 Dynamic block devices such as iSCSI might not be fully initialized and
 present yet. This allows to set a timeout for waiting for devices to
 appear on start-up. If set to 0, start-up will be aborted immediately if
 no devices are available.
 
 =item B<-Z>
 
 Enable trace mode. B<Warning: this is unsafe for production, use at your
 own risk!> Specifying this once will turn all reboots or power-offs, be
 they caused by self-fence decisions or messages, into a crashdump.
 Specifying this twice will just log them but not continue running.
 
 =item B<-T>
 
 By default, the daemon will set the watchdog timeout as specified in the
 device metadata. However, this does not work for every watchdog device.
 In this case, you must manually ensure that the watchdog timeout used by
 the system correctly matches the SBD settings, and then specify this
 option to allow C<sbd> to continue with start-up.
 
 =item B<-5> I<N>
 
 Warn if the time interval for tickling the watchdog exceeds this many seconds.
 Since the node is unable to log the watchdog expiry (it reboots immediately
 without a chance to write its logs to disk), this is very useful for getting
 an indication that the watchdog timeout is too short for the IO load of the
 system.
 
 Default is 3 seconds, set to zero to disable.
 
 =item B<-C> I<N>
 
 Watchdog timeout to set before crashdumping. If SBD is set to crashdump
 instead of reboot - either via the trace mode settings or the I<external/sbd>
 fencing agent's parameter -, SBD will adjust the watchdog timeout to this
 setting before triggering the dump. Otherwise, the watchdog might trigger and
 prevent a successful crashdump from ever being written.
 
 Defaults to 240 seconds. Set to zero to disable.
 
+=item B<-r> I<N>
+
+Actions to be executed when the watchers don't timely report to the sbd
+master process or one of the watchers detects that the master process
+has died.
+
+Set timeout-action to comma-separated combination of
+noflush|flush plus reboot|crashdump|off.
+If just one of both is given the other stays at the default.
+
+This doesn't affect actions like off, crashdump, reboot explicitly
+triggered via message slots.
+And it does as well not configure the action a watchdog would
+trigger should it run off (there is no generic interface).
+
+Defaults to flush,reboot.
+
 =back
 
 =head2 allocate
 
 Example usage:
 
 	sbd -d /dev/sda1 allocate node1
 
 Explicitly allocates a slot for the specified node name. This should
 rarely be necessary, as every node will automatically allocate itself a
 slot the first time it starts up on watch mode.
 
 =head2 message
 
 Example usage:
 
 	sbd -d /dev/sda1 message node1 test
 
 Writes the specified message to node's slot. This is rarely done
 directly, but rather abstracted via the C<external/sbd> fencing agent
 configured as a cluster resource.
 
 Supported message types are:
 
 =over
 
 =item test
 
 This only generates a log message on the receiving node and can be used
 to check if SBD is seeing the device. Note that this could overwrite a
 fencing request send by the cluster, so should not be used during
 production.
 
 =item reset
 
 Reset the target upon receipt of this message.
 
 =item off
 
 Power-off the target.
 
 =item crashdump
 
 Cause the target node to crashdump.
 
 =item exit
 
 This will make the C<sbd> daemon exit cleanly on the target. You should
 B<not> send this message manually; this is handled properly during
 shutdown of the cluster stack. Manually stopping the daemon means the
 node is unprotected!
 
 =item clear
 
 This message indicates that no real message has been sent to the node.
 You should not set this manually; C<sbd> will clear the message slot
 automatically during start-up, and setting this manually could overwrite
 a fencing message by the cluster.
 
 =back
 
 =head2 query-watchdog
 
 Example usage:
 
 	sbd query-watchdog
 
 Check for available watchdog devices and print some info.
 
 B<Warning>: This command will arm the watchdog during query, and if your
 watchdog refuses disarming (for example, if its kernel module has the
 'nowayout' parameter set) this will reset your system.
 
 =head2 test-watchdog
 
 Example usage:
 
 	sbd test-watchdog [-w /dev/watchdog3]
 
 Test specified watchdog device (/dev/watchdog by default).
 
 B<Warning>: This command will arm the watchdog and have your system reset
 in case your watchdog is working properly! If issued from an interactive
 session, it will prompt for confirmation.
 
 =head1 Base system configuration
 
 =head2 Configure a watchdog
 
 It is highly recommended that you configure your Linux system to load a
 watchdog driver with hardware assistance (as is available on most modern
 systems), such as I<hpwdt>, I<iTCO_wdt>, or others. As a fall-back, you
 can use the I<softdog> module.
 
 No other software must access the watchdog timer; it can only be
 accessed by one process at any given time. Some hardware vendors ship
 systems management software that use the watchdog for system resets
 (f.e. HP ASR daemon). Such software has to be disabled if the watchdog
 is to be used by SBD.
 
 =head2 Choosing and initializing the block device(s)
 
 First, you have to decide if you want to use one, two, or three devices.
 
 If you are using multiple ones, they should reside on independent
 storage setups. Putting all three of them on the same logical unit for
 example would not provide any additional redundancy.
 
 The SBD device can be connected via Fibre Channel, Fibre Channel over
 Ethernet, or even iSCSI. Thus, an iSCSI target can become a sort-of
 network-based quorum server; the advantage is that it does not require
 a smart host at your third location, just block storage.
 
 The SBD partitions themselves B<must not> be mirrored (via MD,
 DRBD, or the storage layer itself), since this could result in a
 split-mirror scenario. Nor can they reside on cLVM2 volume groups, since
 they must be accessed by the cluster stack before it has started the
 cLVM2 daemons; hence, these should be either raw partitions or logical
 units on (multipath) storage.
 
 The block device(s) must be accessible from all nodes. (While it is not
 necessary that they share the same path name on all nodes, this is
 considered a very good idea.)
 
 SBD will only use about one megabyte per device, so you can easily
 create a small partition, or very small logical units.  (The size of the
 SBD device depends on the block size of the underlying device. Thus, 1MB
 is fine on plain SCSI devices and SAN storage with 512 byte blocks. On
 the IBM s390x architecture in particular, disks default to 4k blocks,
 and thus require roughly 4MB.)
 
 The number of devices will affect the operation of SBD as follows:
 
 =over
 
 =item One device
 
 In its most simple implementation, you use one device only. This is
 appropriate for clusters where all your data is on the same shared
 storage (with internal redundancy) anyway; the SBD device does not
 introduce an additional single point of failure then.
 
 If the SBD device is not accessible, the daemon will fail to start and
 inhibit openais startup.
 
 =item Two devices
 
 This configuration is a trade-off, primarily aimed at environments where
 host-based mirroring is used, but no third storage device is available.
 
 SBD will not commit suicide if it loses access to one mirror leg; this
 allows the cluster to continue to function even in the face of one outage.
 
 However, SBD will not fence the other side while only one mirror leg is
 available, since it does not have enough knowledge to detect an asymmetric
 split of the storage. So it will not be able to automatically tolerate a
 second failure while one of the storage arrays is down. (Though you
 can use the appropriate crm command to acknowledge the fence manually.)
 
 It will not start unless both devices are accessible on boot.
 
 =item Three devices
 
 In this most reliable and recommended configuration, SBD will only
 self-fence if more than one device is lost; hence, this configuration is
 resilient against temporary single device outages (be it due to failures
 or maintenance).  Fencing messages can still be successfully relayed if
 at least two devices remain accessible.
 
 This configuration is appropriate for more complex scenarios where
 storage is not confined to a single array. For example, host-based
 mirroring solutions could have one SBD per mirror leg (not mirrored
 itself), and an additional tie-breaker on iSCSI.
 
 It will only start if at least two devices are accessible on boot.
 
 =back
 
 After you have chosen the devices and created the appropriate partitions
 and perhaps multipath alias names to ease management, use the C<sbd create>
 command described above to initialize the SBD metadata on them.
 
 =head3 Sharing the block device(s) between multiple clusters
 
 It is possible to share the block devices between multiple clusters,
 provided the total number of nodes accessing them does not exceed I<255>
 nodes, and they all must share the same SBD timeouts (since these are
 part of the metadata).
 
 If you are using multiple devices this can reduce the setup overhead
 required. However, you should B<not> share devices between clusters in
 different security domains.
 
 =head2 Configure SBD to start on boot
 
 On systems using C<sysvinit>, the C<openais> or C<corosync> system
 start-up scripts must handle starting or stopping C<sbd> as required
 before starting the rest of the cluster stack.
 
 For C<systemd>, sbd simply has to be enabled using
 
 	systemctl enable sbd.service
 
 The daemon is brought online on each node before corosync and Pacemaker
 are started, and terminated only after all other cluster components have
 been shut down - ensuring that cluster resources are never activated
 without SBD supervision.
 
 =head2 Configuration via sysconfig
 
 The system instance of C<sbd> is configured via F</etc/sysconfig/sbd>.
 In this file, you must specify the device(s) used, as well as any
 options to pass to the daemon:
 
 	SBD_DEVICE="/dev/sda1;/dev/sdb1;/dev/sdc1"
 	SBD_PACEMAKER="true"
 
 C<sbd> will fail to start if no C<SBD_DEVICE> is specified. See the
 installed template for more options that can be configured here.
+In general configuration done via parameters takes precedence over
+the configuration from the configuration file.
 
 =head2 Testing the sbd installation
 
 After a restart of the cluster stack on this node, you can now try
 sending a test message to it as root, from this or any other node:
 
 	sbd -d /dev/sda1 message node1 test
 
 The node will acknowledge the receipt of the message in the system logs:
 
 	Aug 29 14:10:00 node1 sbd: [13412]: info: Received command test from node2
 
 This confirms that SBD is indeed up and running on the node, and that it
 is ready to receive messages.
 
 Make B<sure> that F</etc/sysconfig/sbd> is identical on all cluster
 nodes, and that all cluster nodes are running the daemon.
 
 =head1 Pacemaker CIB integration
 
 =head2 Fencing resource
 
 Pacemaker can only interact with SBD to issue a node fence if there is a
 configure fencing resource. This should be a primitive, not a clone, as
 follows:
 
 	primitive fencing-sbd stonith:external/sbd \
 		params pcmk_delay_max=30
 
 This will automatically use the same devices as configured in
 F</etc/sysconfig/sbd>.
 
 While you should not configure this as a clone (as Pacemaker will register
 the fencing device on each node automatically), the I<pcmk_delay_max>
 setting enables random fencing delay which ensures, in a scenario where a
 split-brain scenario did occur in a two node cluster, that one of the nodes
 has a better chance to survive to avoid double fencing.
 
 SBD also supports turning the reset request into a crash request, which
 may be helpful for debugging if you have kernel crashdumping configured;
 then, every fence request will cause the node to dump core. You can
 enable this via the C<crashdump="true"> parameter on the fencing
 resource. This is B<not> recommended for production use, but only for
 debugging phases.
 
 =head2 General cluster properties
 
 You must also enable STONITH in general, and set the STONITH timeout to
 be at least twice the I<msgwait> timeout you have configured, to allow
 enough time for the fencing message to be delivered. If your I<msgwait>
 timeout is 60 seconds, this is a possible configuration:
 
 	property stonith-enabled="true"
 	property stonith-timeout="120s"
 
 B<Caution>: if I<stonith-timeout> is too low for I<msgwait> and the
 system overhead, sbd will never be able to successfully complete a fence
 request. This will create a fencing loop.
 
 Note that the sbd fencing agent will try to detect this and
 automatically extend the I<stonith-timeout> setting to a reasonable
 value, on the assumption that sbd modifying your configuration is
 preferable to not fencing.
 
 =head1 Management tasks
 
 =head2 Recovering from temporary SBD device outage
 
 If you have multiple devices, failure of a single device is not immediately
 fatal. C<sbd> will retry to restart the monitor for the device every 5
 seconds by default. However, you can tune this via the options to the
 I<watch> command.
 
 In case you wish the immediately force a restart of all currently
 disabled monitor processes, you can send a I<SIGUSR1> to the SBD
 I<inquisitor> process.
 
 
 =head1 LICENSE
 
 Copyright (C) 2008-2013 Lars Marowsky-Bree
 
 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 software 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.
 
 For details see the GNU General Public License at
 http://www.gnu.org/licenses/gpl-2.0.html (version 2) and/or
 http://www.gnu.org/licenses/gpl.html (the newest as per "any later").
diff --git a/src/sbd-common.c b/src/sbd-common.c
index cc84cd0..0e8be65 100644
--- a/src/sbd-common.c
+++ b/src/sbd-common.c
@@ -1,1065 +1,1075 @@
 /*
  * Copyright (C) 2013 Lars Marowsky-Bree <lmb@suse.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 software 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 "sbd.h"
 #include <sys/reboot.h>
 #include <sys/types.h>
 #ifdef __GLIBC__
 #include <sys/sysmacros.h>
 #endif
 #include <sys/stat.h>
 #include <pwd.h>
 #include <unistd.h>
 #include <dirent.h>
 
 #ifdef _POSIX_MEMLOCK
 #  include <sys/mman.h>
 #endif
 
 /* Tunable defaults: */
 #if  defined(__s390__) || defined(__s390x__)
 unsigned long	timeout_watchdog 	= 15;
 int		timeout_msgwait		= 30;
 #else
 unsigned long	timeout_watchdog 	= 5;
 int		timeout_msgwait		= 10;
 #endif
 unsigned long	timeout_watchdog_warn 	= 3;
 int		timeout_allocate 	= 2;
 int		timeout_loop	    	= 1;
 int		timeout_io		= 3;
 int		timeout_startup		= 120;
 
 int	watchdog_use		= 1;
 int	watchdog_set_timeout	= 1;
 unsigned long	timeout_watchdog_crashdump = 240;
 int	skip_rt			= 0;
 int	debug			= 0;
 int	debug_mode		= 0;
 char *watchdogdev		= NULL;
 bool watchdogdev_is_default = false;
 char *	local_uname;
 
 /* Global, non-tunable variables: */
 int	sector_size		= 0;
 int	watchdogfd 		= -1;
 int     servant_health          = 0;
 
 /*const char	*devname;*/
 const char	*cmdname;
 
 void
 usage(void)
 {
 	fprintf(stderr,
 "Shared storage fencing tool.\n"
 "Syntax:\n"
 "	%s <options> <command> <cmdarguments>\n"
 "Options:\n"
 "-d <devname>	Block device to use (mandatory; can be specified up to 3 times)\n"
 "-h		Display this help.\n"
 "-n <node>	Set local node name; defaults to uname -n (optional)\n"
 "\n"
 "-R		Do NOT enable realtime priority (debugging only)\n"
 "-W		Use watchdog (recommended) (watch only)\n"
 "-w <dev>	Specify watchdog device (optional) (watch only)\n"
 "-T		Do NOT initialize the watchdog timeout (watch only)\n"
 "-S <0|1>	Set start mode if the node was previously fenced (watch only)\n"
 "-p <path>	Write pidfile to the specified path (watch only)\n"
 "-v		Enable some verbose debug logging (optional)\n"
 "\n"
 "-1 <N>		Set watchdog timeout to N seconds (optional, create only)\n"
 "-2 <N>		Set slot allocation timeout to N seconds (optional, create only)\n"
 "-3 <N>		Set daemon loop timeout to N seconds (optional, create only)\n"
 "-4 <N>		Set msgwait timeout to N seconds (optional, create only)\n"
 "-5 <N>		Warn if loop latency exceeds threshold (optional, watch only)\n"
 "			(default is 3, set to 0 to disable)\n"
 "-C <N>		Watchdog timeout to set before crashdumping (def: 240s, optional)\n"
 "-I <N>		Async IO read timeout (defaults to 3 * loop timeout, optional)\n"
 "-s <N>		Timeout to wait for devices to become available (def: 120s)\n"
 "-t <N>		Dampening delay before faulty servants are restarted (optional)\n"
 "			(default is 5, set to 0 to disable)\n"
 "-F <N>		# of failures before a servant is considered faulty (optional)\n"
 "			(default is 1, set to 0 to disable)\n"
 "-P		Check Pacemaker quorum and node health (optional, watch only)\n"
 "-Z		Enable trace mode. WARNING: UNSAFE FOR PRODUCTION!\n"
+"-r		Set timeout-action to comma-separated combination of\n"
+"		noflush|flush plus reboot|crashdump|off (default is flush,reboot)\n"
 "Commands:\n"
 #if SUPPORT_SHARED_DISK
 "create		initialize N slots on <dev> - OVERWRITES DEVICE!\n"
 "list		List all allocated slots on device, and messages.\n"
 "dump		Dump meta-data header from device.\n"
 "allocate <node>\n"
 "		Allocate a slot for node (optional)\n"
 "message <node> (test|reset|off|clear|exit)\n"
 "		Writes the specified message to node's slot.\n"
 #endif
 "watch		Loop forever, monitoring own slot\n"
 "query-watchdog	Check for available watchdog-devices and print some info\n"
 "test-watchdog	Test the watchdog-device selected.\n"
 "		Attention: This will arm the watchdog and have your system reset\n"
 "		           in case your watchdog is working properly!\n"
                 , cmdname);
 }
 
 static int
 watchdog_init_interval_fd(int wdfd, int timeout)
 {
 	if (ioctl(wdfd, WDIOC_SETTIMEOUT, &timeout) < 0) {
 		cl_perror( "WDIOC_SETTIMEOUT"
 				": Failed to set watchdog timer to %u seconds.",
 				timeout);
 		cl_log(LOG_CRIT, "Please validate your watchdog configuration!");
 		cl_log(LOG_CRIT, "Choose a different watchdog driver or specify -T to skip this if you are completely sure.");
 		return -1;
 	}
 	return 0;
 }
 
 int
 watchdog_init_interval(void)
 {
 	if (watchdogfd < 0) {
 		return 0;
 	}
 
 	if (watchdog_set_timeout == 0) {
 		cl_log(LOG_INFO, "NOT setting watchdog timeout on explicit user request!");
 		return 0;
 	}
 
 	if (watchdog_init_interval_fd(watchdogfd, timeout_watchdog) < 0) {
 		return -1;
 	}
 	cl_log(LOG_INFO, "Set watchdog timeout to %u seconds.", (int) timeout_watchdog);
 	return 0;
 }
 
 static int
 watchdog_tickle_fd(int wdfd, char *wddev)
 {
 	if (write(wdfd, "", 1) != 1) {
 			cl_perror("Watchdog write failure: %s!", wddev);
 			return -1;
 		}
 	return 0;
 }
 
 int
 watchdog_tickle(void)
 {
 	if (watchdogfd >= 0) {
 		return watchdog_tickle_fd(watchdogfd, watchdogdev);
 	}
 	return 0;
 }
 
 static int
 watchdog_init_fd(char *wddev, int timeout)
 {
 	int wdfd;
 
 	wdfd = open(wddev, O_WRONLY);
 	if (wdfd >= 0) {
 		if (((timeout >= 0) && (watchdog_init_interval_fd(wdfd, timeout) < 0))
 					|| (watchdog_tickle_fd(wdfd, wddev) < 0)) {
 			close(wdfd);
 			return -1;
 		}
 	} else {
 		cl_perror("Cannot open watchdog device '%s'", wddev);
 		return -1;
 	}
 	return wdfd;
 }
 
 int
 watchdog_init(void)
 {
 	if (watchdogfd < 0 && watchdogdev != NULL) {
 		int timeout = timeout_watchdog;
 
 		if (watchdog_set_timeout == 0) {
 			cl_log(LOG_INFO, "NOT setting watchdog timeout on explicit user request!");
 			timeout = -1;
 		}
 		watchdogfd = watchdog_init_fd(watchdogdev, timeout);
 		if (watchdogfd >= 0) {
 			cl_log(LOG_NOTICE, "Using watchdog device '%s'", watchdogdev);
 			if (watchdog_set_timeout) {
 				cl_log(LOG_INFO, "Set watchdog timeout to %u seconds.", (int) timeout_watchdog);
 			}
 		} else {
 			return -1;
 		}
 	}
 	return 0;
 }
 
 static void
 watchdog_close_fd(int wdfd, char *wddev, bool disarm)
 {
     if (disarm) {
         int r;
         int flags = WDIOS_DISABLECARD;;
 
         /* Explicitly disarm it */
         r = ioctl(wdfd, WDIOC_SETOPTIONS, &flags);
         if (r < 0) {
             cl_perror("Failed to disable hardware watchdog %s", wddev);
         }
 
         /* To be sure, use magic close logic, too */
         for (;;) {
             if (write(wdfd, "V", 1) > 0) {
                 break;
             }
             cl_perror("Cannot disable watchdog device %s", wddev);
         }
     }
 
     if (close(wdfd) < 0) {
         cl_perror("Watchdog close(%d) failed", wdfd);
     }
 }
 
 void
 watchdog_close(bool disarm)
 {
     if (watchdogfd < 0) {
         return;
     }
 
     watchdog_close_fd(watchdogfd, watchdogdev, disarm);
     watchdogfd = -1;
 }
 
 #define MAX_WATCHDOGS 64
 #define SYS_CLASS_WATCHDOG "/sys/class/watchdog"
 #define SYS_CHAR_DEV_DIR "/sys/dev/char"
 #define WATCHDOG_NODEDIR "/dev/"
 #define WATCHDOG_NODEDIR_LEN 5
 
 struct watchdog_list_item {
 	dev_t dev;
 	char *dev_node;
 	char *dev_ident;
 	char *dev_driver;
 	struct watchdog_list_item *next;
 };
 
 struct link_list_item {
 	char *dev_node;
 	char *link_name;
 	struct link_list_item *next;
 };
 
 static struct watchdog_list_item *watchdog_list = NULL;
 static int watchdog_list_items = 0;
 
 static void
 watchdog_populate_list(void)
 {
 	dev_t watchdogs[MAX_WATCHDOGS + 1] =
 		{makedev(10,130), 0};
 	int num_watchdogs = 1;
 	struct dirent *entry;
 	char entry_name[280];
 	DIR *dp;
 	char buf[280] = "";
 	struct link_list_item *link_list = NULL;
 
 	if (watchdog_list != NULL) {
 		return;
 	}
 
 	/* get additional devices from /sys/class/watchdog */
 	dp = opendir(SYS_CLASS_WATCHDOG);
 	if (dp) {
 		while ((entry = readdir(dp))) {
 			if (entry->d_type == DT_LNK) {
 				FILE *file;
 
 				snprintf(entry_name, sizeof(entry_name),
 				         SYS_CLASS_WATCHDOG "/%s/dev", entry->d_name);
 				file = fopen(entry_name, "r");
 				if (file) {
 					int major, minor;
 
 					if (fscanf(file, "%d:%d", &major, &minor) == 2) {
 						watchdogs[num_watchdogs++] = makedev(major, minor);
 					}
 					fclose(file);
 					if (num_watchdogs == MAX_WATCHDOGS) {
 						break;
 					}
 				}
 			}
 		}
 		closedir(dp);
 	}
 
 	/* search for watchdog nodes in /dev */
 	dp = opendir(WATCHDOG_NODEDIR);
 	if (dp) {
 		/* first go for links and memorize them */
 		while ((entry = readdir(dp))) {
 			if (entry->d_type == DT_LNK) {
 				int len;
 
 				snprintf(entry_name, sizeof(entry_name),
 				         WATCHDOG_NODEDIR "%s", entry->d_name);
 
 				/* !realpath(entry_name, buf) unfortunately does a stat on
 				 * target so we can't really use it to check if links stay
 				 * within /dev without triggering e.g. AVC-logs (with
 				 * SELinux policy that just allows stat within /dev).
 				 * Without canonicalization that doesn't actually touch the
 				 * filesystem easily available introduce some limitations
 				 * for simplicity:
 				 * - just simple path without '..'
 				 * - just one level of symlinks (avoid e.g. loop-checking)
 				 */
 				len = readlink(entry_name, buf, sizeof(buf) - 1);
 				if ((len < 1) ||
 				    (len > sizeof(buf) - WATCHDOG_NODEDIR_LEN - 1)) {
 					continue;
 				}
 				buf[len] = '\0';
 				if (buf[0] != '/') {
 					memmove(&buf[WATCHDOG_NODEDIR_LEN], buf, len+1);
 					memcpy(buf, WATCHDOG_NODEDIR, WATCHDOG_NODEDIR_LEN);
 					len += WATCHDOG_NODEDIR_LEN;
 				}
 				if (strstr(buf, "/../") ||
 				    strncmp(WATCHDOG_NODEDIR, buf, WATCHDOG_NODEDIR_LEN)) {
 					continue;
 				} else {
 					/* just memorize to avoid statting the target - SELinux */
 					struct link_list_item *lli =
 						calloc(1, sizeof(struct link_list_item));
 
 					lli->dev_node = strdup(buf);
 					lli->link_name = strdup(entry_name);
 					lli->next = link_list;
 					link_list = lli;
 				}
 			}
 		}
 
 		rewinddir(dp);
 
 		while ((entry = readdir(dp))) {
 			if (entry->d_type == DT_CHR) {
 				struct stat statbuf;
 
 				snprintf(entry_name, sizeof(entry_name),
 				         WATCHDOG_NODEDIR "%s", entry->d_name);
 				if(!stat(entry_name, &statbuf) && S_ISCHR(statbuf.st_mode)) {
 					int i;
 
 					for (i=0; i<num_watchdogs; i++) {
 						if (statbuf.st_rdev == watchdogs[i]) {
 							int wdfd = watchdog_init_fd(entry_name, -1);
 							struct watchdog_list_item *wdg =
 								calloc(1, sizeof(struct watchdog_list_item));
 							int len;
 							struct link_list_item *tmp_list = NULL;
 
 							wdg->dev = watchdogs[i];
 							wdg->dev_node = strdup(entry_name);
 							wdg->next = watchdog_list;
 							watchdog_list = wdg;
 							watchdog_list_items++;
 
 							if (wdfd >= 0) {
 								struct watchdog_info ident;
 
 								ident.identity[0] = '\0';
 								ioctl(wdfd, WDIOC_GETSUPPORT, &ident);
 								watchdog_close_fd(wdfd, entry_name, true);
 								if (ident.identity[0]) {
 									wdg->dev_ident = strdup((char *) ident.identity);
 								}
 							}
 
 							snprintf(entry_name, sizeof(entry_name),
 							         SYS_CHAR_DEV_DIR "/%d:%d/device/driver",
 							         major(watchdogs[i]), minor(watchdogs[i]));
 							len = readlink(entry_name, buf, sizeof(buf) - 1);
 							if (len > 0) {
 								buf[len] = '\0';
 								wdg->dev_driver = strdup(basename(buf));
 							} else if ((wdg->dev_ident) &&
 							           (strcmp(wdg->dev_ident,
 							                   "Software Watchdog") == 0)) {
 								wdg->dev_driver = strdup("softdog");
 							}
 
 							/* create dupes if we have memorized links
 							 * to this node
 							 */
 							for (tmp_list = link_list; tmp_list;
 							     tmp_list = tmp_list->next) {
 								if (!strcmp(tmp_list->dev_node,
 								            wdg->dev_node)) {
 									struct watchdog_list_item *dupe_wdg =
 										calloc(1, sizeof(struct watchdog_list_item));
 
 									/* as long as we never purge watchdog_list
 									 * there is no need to dupe strings
 									 */
 									*dupe_wdg = *wdg;
 									dupe_wdg->dev_node = strdup(tmp_list->link_name);
 									dupe_wdg->next = watchdog_list;
 									watchdog_list = dupe_wdg;
 									watchdog_list_items++;
 								}
 								/* for performance reasons we could remove
 								 * the link_list entry
 								 */
 							}
 							break;
 						}
 					}
 				}
 			}
 		}
 
 		closedir(dp);
 	}
 
 	/* cleanup link list */
 	while (link_list) {
 		struct link_list_item *tmp_list = link_list;
 
 		link_list = link_list->next;
 		free(tmp_list->dev_node);
 		free(tmp_list->link_name);
 		free(tmp_list);
 	}
 }
 
 int watchdog_info(void)
 {
 	struct watchdog_list_item *wdg;
 	int wdg_cnt = 0;
 
 	watchdog_populate_list();
 	printf("\nDiscovered %d watchdog devices:\n", watchdog_list_items);
 	for (wdg = watchdog_list; wdg != NULL; wdg = wdg->next) {
 		wdg_cnt++;
 		printf("\n[%d] %s\nIdentity: %s\nDriver: %s\n",
 				wdg_cnt, wdg->dev_node,
 				wdg->dev_ident?wdg->dev_ident:"Error: Check if hogged by e.g. sbd-daemon!",
 				wdg->dev_driver?wdg->dev_driver:"<unknown>");
 		if ((wdg->dev_driver) && (strcmp(wdg->dev_driver, "softdog") == 0)) {
 			printf("CAUTION: Not recommended for use with sbd.\n"); 
 		}
 	}
 
 	return 0;
 }
 
 int watchdog_test(void)
 {
 	int i;
 
 	if ((watchdog_set_timeout == 0) || !watchdog_use) {
 		printf("\nWatchdog is disabled - aborting test!!!\n");
 		return 0;
 	}
 	if (watchdogdev_is_default) {
 		watchdog_populate_list();
 		if (watchdog_list_items > 1) {
 			printf("\nError: Multiple watchdog devices discovered.\n"
 				   "       Use -w <watchdog> or SBD_WATCHDOG_DEV to specify\n"
 				   "       which device to reset the system with\n");
 			watchdog_info();
 			return -1;
 		}
 	}
 	if ((isatty(fileno(stdin)))) {
 		char buffer[16];
 		printf("\nWARNING: This operation is expected to force-reboot this system\n"
 			   "         without following any shutdown procedures.\n\n"
 			   "Proceed? [NO/Proceed] ");
 
 		if ((fgets(buffer, 16, stdin) == NULL) ||
 			strcmp(buffer, "Proceed\n")) {
 			printf("\nAborting watchdog test!!!\n");
 			return 0;
 		}
 		printf("\n");
 	}
 	printf("Initializing %s with a reset countdown of %d seconds ...\n",
 		watchdogdev, (int) timeout_watchdog);
 	if ((watchdog_init() < 0) || (watchdog_init_interval() < 0)) {
 		printf("Failed to initialize watchdog!!!\n");
 		return -1;
 	}
 	printf("\n");
 	printf("NOTICE: The watchdog device is expected to reset the system\n"
 		   "        in %d seconds.  If system remains active beyond that time,\n"
 		   "        watchdog may not be functional.\n\n", (int) timeout_watchdog);
 	for (i=timeout_watchdog; i>1; i--) {
 		printf("Reset countdown ... %d seconds\n", i);
 		sleep(1);
 	}
 	for (i=2; i>0; i--) {
 		printf("System expected to reset any moment ...\n");
 		sleep(1);
 	}
 	for (i=5; i>0; i--) {
 		printf("System should have reset ...\n");
 		sleep(1);
 	}
 	printf("Error: The watchdog device has failed to reboot the system,\n"
 		   "       and it may not be suitable for usage with sbd.\n");
 
 	/* test should trigger a reboot thus returning is actually bad */
 	return -1;
 }
 
 /* This duplicates some code from linux/ioprio.h since these are not included
  * even in linux-kernel-headers. Sucks. See also
  * /usr/src/linux/Documentation/block/ioprio.txt and ioprio_set(2) */
 extern int sys_ioprio_set(int, int, int);
 int ioprio_set(int which, int who, int ioprio);
 inline int ioprio_set(int which, int who, int ioprio)
 {
         return syscall(__NR_ioprio_set, which, who, ioprio);
 }
 
 enum {
         IOPRIO_CLASS_NONE,
         IOPRIO_CLASS_RT,
         IOPRIO_CLASS_BE,
         IOPRIO_CLASS_IDLE,
 };
 
 enum {
         IOPRIO_WHO_PROCESS = 1,
         IOPRIO_WHO_PGRP,
         IOPRIO_WHO_USER,
 };
 
 #define IOPRIO_BITS             (16)
 #define IOPRIO_CLASS_SHIFT      (13)
 #define IOPRIO_PRIO_MASK        ((1UL << IOPRIO_CLASS_SHIFT) - 1)
 
 #define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT)
 #define IOPRIO_PRIO_DATA(mask)  ((mask) & IOPRIO_PRIO_MASK)
 #define IOPRIO_PRIO_VALUE(class, data)  (((class) << IOPRIO_CLASS_SHIFT) | data)
 
 static unsigned char
 sbd_stack_hogger(unsigned char * inbuf, int kbytes)
 {
     unsigned char buf[1024];
 
     if(kbytes <= 0) {
         return HOG_CHAR;
     }
 
     if (inbuf == NULL) {
         memset(buf, HOG_CHAR, sizeof(buf));
     } else {
         memcpy(buf, inbuf, sizeof(buf));
     }
 
     if (kbytes > 0) {
         return sbd_stack_hogger(buf, kbytes-1);
     } else {
         return buf[sizeof(buf)-1];
     }
 }
 
 static void
 sbd_malloc_hogger(int kbytes)
 {
     int	j;
     void**chunks;
     int	 chunksize = 1024;
 
     if(kbytes <= 0) {
         return;
     }
 
     /*
      * We could call mallopt(M_MMAP_MAX, 0) to disable it completely,
      * but we've already called mlockall()
      *
      * We could also call mallopt(M_TRIM_THRESHOLD, -1) to prevent malloc
      * from giving memory back to the system, but we've already called
      * mlockall(MCL_FUTURE), so there's no need.
      */
 
     chunks = malloc(kbytes * sizeof(void *));
     if (chunks == NULL) {
         cl_log(LOG_WARNING, "Could not preallocate chunk array");
         return;
     }
 
     for (j=0; j < kbytes; ++j) {
         chunks[j] = malloc(chunksize);
         if (chunks[j] == NULL) {
             cl_log(LOG_WARNING, "Could not preallocate block %d", j);
 
         } else {
             memset(chunks[j], 0, chunksize);
         }
     }
 
     for (j=0; j < kbytes; ++j) {
         free(chunks[j]);
     }
 
     free(chunks);
 }
 
 static void sbd_memlock(int stackgrowK, int heapgrowK) 
 {
 
 #ifdef _POSIX_MEMLOCK
     /*
      * We could call setrlimit(RLIMIT_MEMLOCK,...) with a large
      * number, but the mcp runs as root and mlock(2) says:
      *
      * Since Linux 2.6.9, no limits are placed on the amount of memory
      * that a privileged process may lock, and this limit instead
      * governs the amount of memory that an unprivileged process may
      * lock.
      */
     if (mlockall(MCL_CURRENT|MCL_FUTURE) >= 0) {
         cl_log(LOG_INFO, "Locked ourselves in memory");
 
         /* Now allocate some extra pages (MCL_FUTURE will ensure they stay around) */
         sbd_malloc_hogger(heapgrowK);
         sbd_stack_hogger(NULL, stackgrowK);
 
     } else {
         cl_perror("Unable to lock ourselves into memory");
     }
 
 #else
     cl_log(LOG_ERR, "Unable to lock ourselves into memory");
 #endif
 }
 
 void
 sbd_make_realtime(int priority, int stackgrowK, int heapgrowK)
 {
     if(priority < 0) {
         return;
     }
 
 #ifdef SCHED_RR
     {
         int pcurrent = 0;
         int pmin = sched_get_priority_min(SCHED_RR);
         int pmax = sched_get_priority_max(SCHED_RR);
 
         if (priority == 0) {
             priority = pmax;
         } else if (priority < pmin) {
             priority = pmin;
         } else if (priority > pmax) {
             priority = pmax;
         }
 
         pcurrent = sched_getscheduler(0);
         if (pcurrent < 0) {
             cl_perror("Unable to get scheduler priority");
 
         } else if(pcurrent < priority) {
             struct sched_param sp;
 
             memset(&sp, 0, sizeof(sp));
             sp.sched_priority = priority;
 
             if (sched_setscheduler(0, SCHED_RR, &sp) < 0) {
                 cl_perror("Unable to set scheduler priority to %d", priority);
             } else {
                 cl_log(LOG_INFO, "Scheduler priority is now %d", priority);
             }
         }
     }
 #else
     cl_log(LOG_ERR, "System does not support updating the scheduler priority");
 #endif
 
     sbd_memlock(heapgrowK, stackgrowK);
 }
 
 void
 maximize_priority(void)
 {
 	if (skip_rt) {
 		cl_log(LOG_INFO, "Not elevating to realtime (-R specified).");
 		return;
 	}
 
         sbd_make_realtime(0, 256, 256);
 
 	if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(),
 			IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 1)) != 0) {
 		cl_perror("ioprio_set() call failed.");
 	}
 }
 
 void
 sysrq_init(void)
 {
 	FILE* procf;
 	int c;
 	procf = fopen("/proc/sys/kernel/sysrq", "r");
 	if (!procf) {
 		cl_perror("cannot open /proc/sys/kernel/sysrq for read.");
 		return;
 	}
 	if (fscanf(procf, "%d", &c) != 1) {
 		cl_perror("Parsing sysrq failed");
 		c = 0;
 	}
 	fclose(procf);
 	if (c == 1)
 		return;
 	/* 8 for debugging dumps of processes, 
 	   128 for reboot/poweroff */
 	c |= 136; 
 	procf = fopen("/proc/sys/kernel/sysrq", "w");
 	if (!procf) {
 		cl_perror("cannot open /proc/sys/kernel/sysrq for writing");
 		return;
 	}
 	fprintf(procf, "%d", c);
 	fclose(procf);
 	return;
 }
 
 void
 sysrq_trigger(char t)
 {
 	FILE *procf;
 
 	procf = fopen("/proc/sysrq-trigger", "a");
 	if (!procf) {
 		cl_perror("Opening sysrq-trigger failed.");
 		return;
 	}
 	cl_log(LOG_INFO, "sysrq-trigger: %c\n", t);
 	fprintf(procf, "%c\n", t);
 	fclose(procf);
 	return;
 }
 
 
 static void
-do_exit(char kind) 
+do_exit(char kind, bool do_flush)
 {
     /* TODO: Turn debug_mode into a bit field? Delay + kdump for example */
     const char *reason = NULL;
 
     if (kind == 'c') {
         cl_log(LOG_NOTICE, "Initiating kdump");
 
     } else if (debug_mode == 1) {
         cl_log(LOG_WARNING, "Initiating kdump instead of panicing the node (debug mode)");
         kind = 'c';
     }
 
     if (debug_mode == 2) {
         cl_log(LOG_WARNING, "Shutting down SBD instead of panicing the node (debug mode)");
         watchdog_close(true);
         exit(0);
     }
 
     if (debug_mode == 3) {
         /* Give the system some time to flush logs to disk before rebooting. */
         cl_log(LOG_WARNING, "Delaying node panic by 10s (debug mode)");
 
         watchdog_close(true);
         sync();
 
         sleep(10);
     }
 
     switch(kind) {
         case 'b':
             reason = "reboot";
             break;
         case 'c':
             reason = "crashdump";
             break;
         case 'o':
             reason = "off";
             break;
         default:
             reason = "unknown";
             break;
     }
 
     cl_log(LOG_EMERG, "Rebooting system: %s", reason);
-    sync();
+    if (do_flush) {
+        sync();
+    }
 
     if(kind == 'c') {
         watchdog_close(true);
         sysrq_trigger(kind);
 
     } else {
         watchdog_close(false);
         sysrq_trigger(kind);
         if (reboot((kind == 'o')?RB_POWER_OFF:RB_AUTOBOOT) < 0) {
             cl_perror("%s failed", (kind == 'o')?"Poweroff":"Reboot");
         }
     }
 
     exit(1);
 }
 
 void
 do_crashdump(void)
 {
-    do_exit('c');
+    do_exit('c', true);
 }
 
 void
 do_reset(void)
 {
-    do_exit('b');
+    do_exit('b', true);
 }
 
 void
 do_off(void)
 {
-    do_exit('o');
+    do_exit('o', true);
+}
+
+void
+do_timeout_action(void)
+{
+	do_exit(timeout_sysrq_char, do_flush);
 }
 
 /*
  * Change directory to the directory our core file needs to go in
  * Call after you establish the userid you're running under.
  */
 int
 sbd_cdtocoredir(void)
 {
 	int		rc;
 	static const char *dir = NULL;
 
 	if (dir == NULL) {
 		dir = CRM_CORE_DIR;
 	}
 	if ((rc=chdir(dir)) < 0) {
 		int errsave = errno;
 		cl_perror("Cannot chdir to [%s]", dir);
 		errno = errsave;
 	}
 	return rc;
 }
 
 pid_t
 make_daemon(void)
 {
 	pid_t			pid;
 	const char *		devnull = "/dev/null";
 
 	pid = fork();
 	if (pid < 0) {
 		cl_log(LOG_ERR, "%s: could not start daemon\n",
 				cmdname);
 		cl_perror("fork");
 		exit(1);
 	}else if (pid > 0) {
 		return pid;
 	}
 
         qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
 
 	/* This is the child; ensure privileges have not been lost. */
 	maximize_priority();
 	sysrq_init();
 
 	umask(022);
 	close(0);
 	(void)open(devnull, O_RDONLY);
 	close(1);
 	(void)open(devnull, O_WRONLY);
 	close(2);
 	(void)open(devnull, O_WRONLY);
 	sbd_cdtocoredir();
 	return 0;
 }
 
 void
 sbd_get_uname(void)
 {
 	struct utsname		uname_buf;
 	int i;
 
 	if (uname(&uname_buf) < 0) {
 		cl_perror("uname() failed?");
 		exit(1);
 	}
 
 	local_uname = strdup(uname_buf.nodename);
 
 	for (i = 0; i < strlen(local_uname); i++)
 		local_uname[i] = tolower(local_uname[i]);
 }
 
 
 #define FMT_MAX 256
 void
 sbd_set_format_string(int method, const char *daemon)
 {
     int offset = 0;
     char fmt[FMT_MAX];
     struct utsname res;
 
     switch(method) {
         case QB_LOG_STDERR:
             break;
 
         case QB_LOG_SYSLOG:
             if(daemon && strcmp(daemon, "sbd") != 0) {
                 offset += snprintf(fmt + offset, FMT_MAX - offset, "%10s: ", daemon);
             }
             break;
 
         default:
             /* When logging to a file */
             if (uname(&res) == 0) {
                 offset +=
                     snprintf(fmt + offset, FMT_MAX - offset, "%%t [%d] %s %10s: ", getpid(),
                              res.nodename, daemon);
             } else {
                 offset += snprintf(fmt + offset, FMT_MAX - offset, "%%t [%d] %10s: ", getpid(), daemon);
             }
     }
 
     if (debug && method >= QB_LOG_STDERR) {
         offset += snprintf(fmt + offset, FMT_MAX - offset, "(%%-12f:%%5l %%g) %%-7p: %%n: ");
     } else {
         offset += snprintf(fmt + offset, FMT_MAX - offset, "%%g %%-7p: %%n: ");
     }
 
     if (method == QB_LOG_SYSLOG) {
         offset += snprintf(fmt + offset, FMT_MAX - offset, "%%b");
     } else {
         offset += snprintf(fmt + offset, FMT_MAX - offset, "\t%%b");
     }
 
     if(offset > 0) {
         qb_log_format_set(method, fmt);
     }
 }
 
 void
 notify_parent(void)
 {
     pid_t		ppid;
     union sigval	signal_value;
 
     memset(&signal_value, 0, sizeof(signal_value));
     ppid = getppid();
 
     if (ppid == 1) {
         /* Our parent died unexpectedly. Triggering
          * self-fence. */
         cl_log(LOG_WARNING, "Our parent is dead.");
-        do_reset();
+        do_timeout_action();
     }
 
     switch (servant_health) {
         case pcmk_health_pending:
         case pcmk_health_shutdown:
         case pcmk_health_transient:
             DBGLOG(LOG_DEBUG, "Not notifying parent: state transient (%d)", servant_health);
             break;
 
         case pcmk_health_unknown:
         case pcmk_health_unclean:
         case pcmk_health_noquorum:
             DBGLOG(LOG_WARNING, "Notifying parent: UNHEALTHY (%d)", servant_health);
             sigqueue(ppid, SIG_PCMK_UNHEALTHY, signal_value);
             break;
 
         case pcmk_health_online:
             DBGLOG(LOG_DEBUG, "Notifying parent: healthy");
             sigqueue(ppid, SIG_LIVENESS, signal_value);
             break;
 
         default:
             DBGLOG(LOG_WARNING, "Notifying parent: UNHEALTHY %d", servant_health);
             sigqueue(ppid, SIG_PCMK_UNHEALTHY, signal_value);
             break;
     }
 }
 
 void
 set_servant_health(enum pcmk_health state, int level, char const *format, ...)
 {
     if (servant_health != state) {
         va_list ap;
         int len = 0;
         char *string = NULL;
 
         servant_health = state;
 
         va_start(ap, format);
         len = vasprintf (&string, format, ap);
 
         if(len > 0) {
             cl_log(level, "%s", string);
         }
         
         va_end(ap);
         free(string);
     }
 }
 
 bool
 sbd_is_disk(struct servants_list_item *servant)
 {
     if ((servant != NULL) &&
         (servant->devname != NULL) &&
         (servant->devname[0] == '/')) {
         return true;
     }
     return false;
 }
 
 bool
 sbd_is_cluster(struct servants_list_item *servant)
 {
     if ((servant != NULL) &&
         (servant->devname != NULL) &&
         (strcmp("cluster", servant->devname) == 0)) {
         return true;
     }
     return false;
 }
 
 bool
 sbd_is_pcmk(struct servants_list_item *servant)
 {
     if ((servant != NULL) &&
         (servant->devname != NULL) &&
         (strcmp("pcmk", servant->devname) == 0)) {
         return true;
     }
     return false;
 }
diff --git a/src/sbd-inquisitor.c b/src/sbd-inquisitor.c
index 9b193d4..8e0bc87 100644
--- a/src/sbd-inquisitor.c
+++ b/src/sbd-inquisitor.c
@@ -1,1177 +1,1224 @@
 /*
  * Copyright (C) 2013 Lars Marowsky-Bree <lmb@suse.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 software 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 <pacemaker/crm/common/util.h>
 #include "sbd.h"
 #define	LOCKSTRLEN	11
 
 static struct servants_list_item *servants_leader = NULL;
 
 int     disk_priority = 1;
 int	check_pcmk = 1;
 int	check_cluster = 1;
 int	disk_count	= 0;
 int	servant_count	= 0;
 int	servant_restart_interval = 5;
 int	servant_restart_count = 1;
 int	start_mode = 0;
 char*	pidfile = NULL;
+bool do_flush = true;
+char timeout_sysrq_char = 'b';
 
 int parse_device_line(const char *line);
 
 void recruit_servant(const char *devname, pid_t pid)
 {
 	struct servants_list_item *s = servants_leader;
 	struct servants_list_item *newbie;
 
 	if (lookup_servant_by_dev(devname)) {
 		cl_log(LOG_DEBUG, "Servant %s already exists", devname);
 		return;
 	}
 
 	newbie = malloc(sizeof(*newbie));
 	if (!newbie) {
 		fprintf(stderr, "malloc failed in recruit_servant.\n");
 		exit(1);
 	}
 	memset(newbie, 0, sizeof(*newbie));
 	newbie->devname = strdup(devname);
 	newbie->pid = pid;
 	newbie->first_start = 1;
 
 	if (!s) {
 		servants_leader = newbie;
 	} else {
 		while (s->next)
 			s = s->next;
 		s->next = newbie;
 	}
 
 	servant_count++;
         if(sbd_is_disk(newbie)) {
             cl_log(LOG_INFO, "Monitoring %s", devname);
             disk_count++;
         } else {
             newbie->outdated = 1;
         }
 }
 
 int assign_servant(const char* devname, functionp_t functionp, int mode, const void* argp)
 {
 	pid_t pid = 0;
 	int rc = 0;
 
 	pid = fork();
 	if (pid == 0) {		/* child */
 		maximize_priority();
                 sbd_set_format_string(QB_LOG_SYSLOG, devname);
 		rc = (*functionp)(devname, mode, argp);
 		if (rc == -1)
 			exit(1);
 		else
 			exit(0);
 	} else if (pid != -1) {		/* parent */
 		return pid;
 	} else {
 		cl_log(LOG_ERR,"Failed to fork servant");
 		exit(1);
 	}
 }
 
 struct servants_list_item *lookup_servant_by_dev(const char *devname)
 {
 	struct servants_list_item *s;
 
 	for (s = servants_leader; s; s = s->next) {
 		if (strcasecmp(s->devname, devname) == 0)
 			break;
 	}
 	return s;
 }
 
 struct servants_list_item *lookup_servant_by_pid(pid_t pid)
 {
 	struct servants_list_item *s;
 
 	for (s = servants_leader; s; s = s->next) {
 		if (s->pid == pid)
 			break;
 	}
 	return s;
 }
 
 int check_all_dead(void)
 {
 	struct servants_list_item *s;
 	int r = 0;
 	union sigval svalue;
 
 	for (s = servants_leader; s; s = s->next) {
 		if (s->pid != 0) {
 			r = sigqueue(s->pid, 0, svalue);
 			if (r == -1 && errno == ESRCH)
 				continue;
 			return 0;
 		}
 	}
 	return 1;
 }
 
 void servant_start(struct servants_list_item *s)
 {
 	int r = 0;
 	union sigval svalue;
 
 	if (s->pid != 0) {
 		r = sigqueue(s->pid, 0, svalue);
 		if ((r != -1 || errno != ESRCH))
 			return;
 	}
 	s->restarts++;
 	if (sbd_is_disk(s)) {
 #if SUPPORT_SHARED_DISK
 		DBGLOG(LOG_INFO, "Starting servant for device %s", s->devname);
 		s->pid = assign_servant(s->devname, servant, start_mode, s);
 #else
                 cl_log(LOG_ERR, "Shared disk functionality not supported");
                 return;
 #endif
 	} else if(sbd_is_pcmk(s)) {
 		DBGLOG(LOG_INFO, "Starting Pacemaker servant");
 		s->pid = assign_servant(s->devname, servant_pcmk, start_mode, NULL);
 
 	} else if(sbd_is_cluster(s)) {
 		DBGLOG(LOG_INFO, "Starting Cluster servant");
 		s->pid = assign_servant(s->devname, servant_cluster, start_mode, NULL);
 
         } else {
             cl_log(LOG_ERR, "Unrecognized servant: %s", s->devname);
         }        
 
 	clock_gettime(CLOCK_MONOTONIC, &s->t_started);
 	return;
 }
 
 void servants_start(void)
 {
 	struct servants_list_item *s;
 
 	for (s = servants_leader; s; s = s->next) {
 		s->restarts = 0;
 		servant_start(s);
 	}
 }
 
 void servants_kill(void)
 {
 	struct servants_list_item *s;
 	union sigval svalue;
 
 	for (s = servants_leader; s; s = s->next) {
 		if (s->pid != 0)
 			sigqueue(s->pid, SIGKILL, svalue);
 	}
 }
 
 static inline void cleanup_servant_by_pid(pid_t pid)
 {
 	struct servants_list_item* s;
 
 	s = lookup_servant_by_pid(pid);
 	if (s) {
 		cl_log(LOG_WARNING, "Servant for %s (pid: %i) has terminated",
 				s->devname, s->pid);
 		s->pid = 0;
 	} else {
 		/* This most likely is a stray signal from somewhere, or
 		 * a SIGCHLD for a process that has previously
 		 * explicitly disconnected. */
 		DBGLOG(LOG_INFO, "cleanup_servant: Nothing known about pid %i",
 				pid);
 	}
 }
 
 int inquisitor_decouple(void)
 {
 	pid_t ppid = getppid();
 	union sigval signal_value;
 
 	/* During start-up, we only arm the watchdog once we've got
 	 * quorum at least once. */
 	if (watchdog_use) {
 		if (watchdog_init() < 0) {
 			return -1;
 		}
 	}
 
 	if (ppid > 1) {
 		sigqueue(ppid, SIG_LIVENESS, signal_value);
 	}
 	return 0;
 }
 
 static int sbd_lock_running(long pid)
 {
 	int rc = 0;
 	long mypid;
 	int running = 0;
 	char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
 
 	/* check if pid is running */
 	if (kill(pid, 0) < 0 && errno == ESRCH) {
 		goto bail;
 	}
 
 #ifndef HAVE_PROC_PID
 	return 1;
 #endif
 
 	/* check to make sure pid hasn't been reused by another process */
 	snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
 	rc = readlink(proc_path, exe_path, PATH_MAX-1);
 	if(rc < 0) {
 		cl_perror("Could not read from %s", proc_path);
 		goto bail;
 	}
 	exe_path[rc] = 0;
 	mypid = (unsigned long) getpid();
 	snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", mypid);
 	rc = readlink(proc_path, myexe_path, PATH_MAX-1);
 	if(rc < 0) {
 		cl_perror("Could not read from %s", proc_path);
 		goto bail;
 	}
 	myexe_path[rc] = 0;
 
 	if(strcmp(exe_path, myexe_path) == 0) {
 		running = 1;
 	}
 
   bail:
 	return running;
 }
 
 static int
 sbd_lock_pidfile(const char *filename)
 {
 	char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
 	int fd;
 	long	pid, mypid;
 	int rc;
 	struct stat sbuf;
 
 	if (filename == NULL) {
 		errno = EFAULT;
 		return -1;
 	}
 
 	mypid = (unsigned long) getpid();
 	snprintf(lf_name, sizeof(lf_name), "%s",filename);
 	snprintf(tf_name, sizeof(tf_name), "%s.%lu",
 		 filename, mypid);
 
 	if ((fd = open(lf_name, O_RDONLY)) >= 0) {
 		if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
 			sleep(1); /* if someone was about to create one,
 			   	   * give'm a sec to do so
 				   * Though if they follow our protocol,
 				   * this won't happen.  They should really
 				   * put the pid in, then link, not the
 				   * other way around.
 				   */
 		}
 		if (read(fd, buf, sizeof(buf)) < 1) {
 			/* lockfile empty -> rm it and go on */;
 		} else {
 			if (sscanf(buf, "%ld", &pid) < 1) {
 				/* lockfile screwed up -> rm it and go on */
 			} else {
 				if (pid > 1 && (getpid() != pid)
 				&&	sbd_lock_running(pid)) {
 					/* is locked by existing process
 					 * -> give up */
 					close(fd);
 					return -1;
 				} else {
 					/* stale lockfile -> rm it and go on */
 				}
 			}
 		}
 		unlink(lf_name);
 		close(fd);
 	}
 	if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
 		/* Hmmh, why did we fail? Anyway, nothing we can do about it */
 		return -3;
 	}
 
 	/* Slight overkill with the %*d format ;-) */
 	snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
 
 	if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
 		/* Again, nothing we can do about this */
 		rc = -3;
 		close(fd);
 		goto out;
 	}
 	close(fd);
 
 	switch (link(tf_name, lf_name)) {
 	case 0:
 		if (stat(tf_name, &sbuf) < 0) {
 			/* something weird happened */
 			rc = -3;
 			break;
 		}
 		if (sbuf.st_nlink < 2) {
 			/* somehow, it didn't get through - NFS trouble? */
 			rc = -2;
 			break;
 		}
 		rc = 0;
 		break;
 	case EEXIST:
 		rc = -1;
 		break;
 	default:
 		rc = -3;
 	}
  out:
 	unlink(tf_name);
 	return rc;
 }
 
 
 /*
  * Unlock a file (remove its lockfile) 
  * do we need to check, if its (still) ours? No, IMHO, if someone else
  * locked our line, it's his fault  -tho
  * returns 0 on success
  * <0 if some failure occured
  */
 
 static int
 sbd_unlock_pidfile(const char *filename)
 {
 	char lf_name[256];
 
 	if (filename == NULL) {
 		errno = EFAULT;
 		return -1;
 	}
 
 	snprintf(lf_name, sizeof(lf_name), "%s", filename);
 
 	return unlink(lf_name);
 }
 
 int cluster_alive(bool all)
 {
     int alive = 1;
     struct servants_list_item* s;
 
     if(servant_count == disk_count) {
         return 0;
     }
 
     for (s = servants_leader; s; s = s->next) {
         if (sbd_is_cluster(s) || sbd_is_pcmk(s)) {
             if(s->outdated) {
                 alive = 0;
             } else if(all == false) {
                 return 1;
             }
         }
     }
 
     return alive;
 }
 
 int quorum_read(int good_servants)
 {
 	if (disk_count > 2) 
 		return (good_servants > disk_count/2);
 	else
 		return (good_servants > 0);
 }
 
 void inquisitor_child(void)
 {
 	int sig, pid;
 	sigset_t procmask;
 	siginfo_t sinfo;
 	int status;
 	struct timespec timeout;
 	int exiting = 0;
 	int decoupled = 0;
 	int cluster_appeared = 0;
 	int pcmk_override = 0;
 	time_t latency;
 	struct timespec t_last_tickle, t_now;
 	struct servants_list_item* s;
 
 	if (debug_mode) {
             cl_log(LOG_ERR, "DEBUG MODE %d IS ACTIVE - DO NOT RUN IN PRODUCTION!", debug_mode);
 	}
 
 	set_proc_title("sbd: inquisitor");
 
 	if (pidfile) {
 		if (sbd_lock_pidfile(pidfile) < 0) {
 			exit(1);
 		}
 	}
 
 	sigemptyset(&procmask);
 	sigaddset(&procmask, SIGCHLD);
 	sigaddset(&procmask, SIGTERM);
 	sigaddset(&procmask, SIG_LIVENESS);
 	sigaddset(&procmask, SIG_EXITREQ);
 	sigaddset(&procmask, SIG_TEST);
 	sigaddset(&procmask, SIG_PCMK_UNHEALTHY);
 	sigaddset(&procmask, SIG_RESTART);
 	sigaddset(&procmask, SIGUSR1);
 	sigaddset(&procmask, SIGUSR2);
 	sigprocmask(SIG_BLOCK, &procmask, NULL);
 
 	servants_start();
 
 	timeout.tv_sec = timeout_loop;
 	timeout.tv_nsec = 0;
 	clock_gettime(CLOCK_MONOTONIC, &t_last_tickle);
 
 	while (1) {
                 bool tickle = 0;
                 bool can_detach = 0;
 		int good_servants = 0;
 
 		sig = sigtimedwait(&procmask, &sinfo, &timeout);
 
 		clock_gettime(CLOCK_MONOTONIC, &t_now);
 
 		if (sig == SIG_EXITREQ || sig == SIGTERM) {
 			servants_kill();
 			watchdog_close(true);
 			exiting = 1;
 		} else if (sig == SIGCHLD) {
 			while ((pid = waitpid(-1, &status, WNOHANG))) {
 				if (pid == -1 && errno == ECHILD) {
 					break;
 				} else {
 					s = lookup_servant_by_pid(pid);
 					if (sbd_is_disk(s)) {
 						if (WIFEXITED(status)) {
 							switch(WEXITSTATUS(status)) {
 								case EXIT_MD_IO_FAIL:
 									DBGLOG(LOG_INFO, "Servant for %s requests to be disowned",
 										s->devname);
 									break;
 								case EXIT_MD_REQUEST_RESET:
 									cl_log(LOG_WARNING, "%s requested a reset", s->devname);
 									do_reset();
 									break;
 								case EXIT_MD_REQUEST_SHUTOFF:
 									cl_log(LOG_WARNING, "%s requested a shutoff", s->devname);
 									do_off();
 									break;
 								case EXIT_MD_REQUEST_CRASHDUMP:
 									cl_log(LOG_WARNING, "%s requested a crashdump", s->devname);
 									do_crashdump();
 									break;
 								default:
 									break;
 							}
 						}
 					}
 					cleanup_servant_by_pid(pid);
 				}
 			}
 		} else if (sig == SIG_PCMK_UNHEALTHY) {
 			s = lookup_servant_by_pid(sinfo.si_pid);
 			if (sbd_is_cluster(s) || sbd_is_pcmk(s)) {
                 if (s->outdated == 0) {
                     cl_log(LOG_WARNING, "%s health check: UNHEALTHY", s->devname);
                 }
                 s->t_last.tv_sec = 1;
             } else {
                 cl_log(LOG_WARNING, "Ignoring SIG_PCMK_UNHEALTHY from unknown source");
             }
 		} else if (sig == SIG_LIVENESS) {
 			s = lookup_servant_by_pid(sinfo.si_pid);
 			if (s) {
 				s->first_start = 0;
 				clock_gettime(CLOCK_MONOTONIC, &s->t_last);
 			}
 
 		} else if (sig == SIG_TEST) {
 		} else if (sig == SIGUSR1) {
 			if (exiting)
 				continue;
 			servants_start();
 		}
 
 		if (exiting) {
 			if (check_all_dead()) {
 				if (pidfile) {
 					sbd_unlock_pidfile(pidfile);
 				}
 				exit(0);
 			} else
 				continue;
 		}
 
 		good_servants = 0;
 		for (s = servants_leader; s; s = s->next) {
 			int age = t_now.tv_sec - s->t_last.tv_sec;
 
 			if (!s->t_last.tv_sec)
 				continue;
 
 			if (age < (int)(timeout_io+timeout_loop)) {
 				if (sbd_is_disk(s)) {
                                     good_servants++;
 				}
                                 if (s->outdated) {
                                     cl_log(LOG_NOTICE, "Servant %s is healthy (age: %d)", s->devname, age);
 				}
 				s->outdated = 0;
 
 			} else if (!s->outdated) {
                                 if (!s->restart_blocked) {
                                     cl_log(LOG_WARNING, "Servant %s is outdated (age: %d)", s->devname, age);
 				}
                                 s->outdated = 1;
 			}
 		}
 
                 if(disk_count == 0) {
                     /* NO disks, everything is up to the cluster */
                     
                     if(cluster_alive(true)) {
                         /* We LIVE! */
                         if(cluster_appeared == false) {
                             cl_log(LOG_INFO, "Active cluster detected");
                         }
                         tickle = 1;
                         can_detach = 1;
                         cluster_appeared = 1;
 
                     } else if(cluster_alive(false)) {
                         if(!decoupled) {
                             /* On the way up, detach and arm the watchdog */
                             cl_log(LOG_INFO, "Partial cluster detected, detaching");
                         }
 
                         can_detach = 1;
                         tickle = !cluster_appeared;
 
                     } else if(!decoupled) {
                         /* Stay alive until the cluster comes up */
                         tickle = !cluster_appeared;
                     }
 
                 } else if(disk_priority == 1 || servant_count == disk_count) {
                     if (quorum_read(good_servants)) {
                         /* There are disks and we're connected to the majority of them */
                         tickle = 1;
                         can_detach = 1;
                         pcmk_override = 0;
 
                     } else if (servant_count > disk_count && cluster_alive(true)) {
                         tickle = 1;
                     
                         if(!pcmk_override) {
                             cl_log(LOG_WARNING, "Majority of devices lost - surviving on pacemaker");
                             pcmk_override = 1; /* Only log this message once */
                         }
                     }
 
                 } else if(cluster_alive(true) && quorum_read(good_servants)) {
                     /* Both disk and cluster servants are healthy */
                     tickle = 1;
                     can_detach = 1;
                     cluster_appeared = 1;
 
                 } else if(quorum_read(good_servants)) {
                     /* The cluster takes priority but only once
                      * connected for the first time.
                      *
                      * Until then, we tickle based on disk quorum.
                      */
                     can_detach = 1;
                     tickle = !cluster_appeared;
                 }
 
                 /* cl_log(LOG_DEBUG, "Tickle: q=%d, g=%d, p=%d, s=%d", */
                 /*        quorum_read(good_servants), good_servants, tickle, disk_count); */
 
                 if(tickle) {
                     watchdog_tickle();
                     clock_gettime(CLOCK_MONOTONIC, &t_last_tickle);
                 }
 
                 if (!decoupled && can_detach) {
                     /* We only do this at the point either the disk or
                      * cluster servants become healthy
                      */
                     cl_log(LOG_DEBUG, "Decoupling");
                     if (inquisitor_decouple() < 0) {
                         servants_kill();
                         exiting = 1;
                         continue;
                     } else {
                         decoupled = 1;
                     }
                 }
 
 		/* Note that this can actually be negative, since we set
 		 * last_tickle after we set now. */
 		latency = t_now.tv_sec - t_last_tickle.tv_sec;
 		if (timeout_watchdog && (latency > (int)timeout_watchdog)) {
 			if (!decoupled) {
 				/* We're still being watched by our
 				 * parent. We don't fence, but exit. */
 				cl_log(LOG_ERR, "SBD: Not enough votes to proceed. Aborting start-up.");
 				servants_kill();
 				exiting = 1;
 				continue;
 			}
 			if (debug_mode < 2) {
 				/* At level 2 or above, we do nothing, but expect
 				 * things to eventually return to
 				 * normal. */
-				do_reset();
+				do_timeout_action();
 			} else {
 				cl_log(LOG_ERR, "SBD: DEBUG MODE: Would have fenced due to timeout!");
 			}
 		}
 
 		if (timeout_watchdog_warn && (latency > (int)timeout_watchdog_warn)) {
 			cl_log(LOG_WARNING,
 			       "Latency: No liveness for %d s exceeds threshold of %d s (healthy servants: %d)",
 			       (int)latency, (int)timeout_watchdog_warn, good_servants);
 
                         if (debug_mode && watchdog_use) {
                             /* In debug mode, trigger a reset before the watchdog can panic the machine */
-                            do_reset();
+                            do_timeout_action();
                         }
 		}
 
 		for (s = servants_leader; s; s = s->next) {
 			int age = t_now.tv_sec - s->t_started.tv_sec;
 
 			if (age > servant_restart_interval) {
 				s->restarts = 0;
 				s->restart_blocked = 0;
 			}
 
 			if (servant_restart_count
 					&& (s->restarts >= servant_restart_count)
 					&& !s->restart_blocked) {
 				if (servant_restart_count > 1) {
 					cl_log(LOG_WARNING, "Max retry count (%d) reached: not restarting servant for %s",
 							(int)servant_restart_count, s->devname);
 				}
 				s->restart_blocked = 1;
 			}
 
 			if (!s->restart_blocked) {
 				servant_start(s);
 			}
 		}
 	}
 	/* not reached */
 	exit(0);
 }
 
 int inquisitor(void)
 {
 	int sig, pid, inquisitor_pid;
 	int status;
 	sigset_t procmask;
 	siginfo_t sinfo;
 
 	/* Where's the best place for sysrq init ?*/
 	sysrq_init();
 
 	sigemptyset(&procmask);
 	sigaddset(&procmask, SIGCHLD);
 	sigaddset(&procmask, SIG_LIVENESS);
 	sigprocmask(SIG_BLOCK, &procmask, NULL);
 
 	inquisitor_pid = make_daemon();
 	if (inquisitor_pid == 0) {
 		inquisitor_child();
 	} 
 	
 	/* We're the parent. Wait for a happy signal from our child
 	 * before we proceed - we either get "SIG_LIVENESS" when the
 	 * inquisitor has completed the first successful round, or
 	 * ECHLD when it exits with an error. */
 
 	while (1) {
 		sig = sigwaitinfo(&procmask, &sinfo);
 		if (sig == SIGCHLD) {
 			while ((pid = waitpid(-1, &status, WNOHANG))) {
 				if (pid == -1 && errno == ECHILD) {
 					break;
 				}
 				/* We got here because the inquisitor
 				 * did not succeed. */
 				return -1;
 			}
 		} else if (sig == SIG_LIVENESS) {
 			/* Inquisitor started up properly. */
 			return 0;
 		} else {
 			fprintf(stderr, "Nobody expected the spanish inquisition!\n");
 			continue;
 		}
 	}
 	/* not reached */
 	return -1;
 }
 
 
 int
 parse_device_line(const char *line)
 {
     int lpc = 0;
     int last = 0;
     int max = 0;
     int found = 0;
 
     if(line) {
         max = strlen(line);
     }
 
     if (max <= 0) {
         return found;
     }
 
     cl_log(LOG_DEBUG, "Processing %d bytes: [%s]", max, line);
     /* Skip initial whitespace */
     for (lpc = 0; lpc <= max && isspace(line[lpc]); lpc++) {
         last = lpc + 1;
     }
 
     /* Now the actual content */
     for (lpc = 0; lpc <= max; lpc++) {
         int a_space = isspace(line[lpc]);
 
         if (a_space && lpc < max && isspace(line[lpc + 1])) {
             /* fast-forward to the end of the spaces */
 
         } else if (a_space || line[lpc] == ';' || line[lpc] == 0) {
             int rc = 1;
             char *entry = NULL;
 
             if (lpc > last) {
                 entry = calloc(1, 1 + lpc - last);
                 rc = sscanf(line + last, "%[^;]", entry);
             }
 
             if (entry == NULL) {
                 /* Skip */
             } else if (rc != 1) {
                 cl_log(LOG_WARNING, "Could not parse (%d %d): %s", last, lpc, line + last);
             } else {
                 cl_log(LOG_DEBUG, "Adding '%s'", entry);
                 recruit_servant(entry, 0);
                 found++;
             }
 
             free(entry);
             last = lpc + 1;
         }
     }
     return found;
 }
 
 #define SBD_SOURCE_FILES "sbd-cluster.c,sbd-common.c,sbd-inquisitor.c,sbd-md.c,sbd-pacemaker.c,setproctitle.c"
 
 static void
 sbd_log_filter_ctl(const char *files, uint8_t priority)
 {
 	if (files == NULL) {
 		files = SBD_SOURCE_FILES;
 	}
 
 	qb_log_filter_ctl(QB_LOG_SYSLOG, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, files, priority);
 	qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD, QB_LOG_FILTER_FILE, files, priority);
 }
 
 int
 arg_enabled(int arg_count)
 {
     return arg_count % 2;
 }
 
 int main(int argc, char **argv, char **envp)
 {
 	int exit_status = 0;
 	int c;
 	int W_count = 0;
 	int c_count = 0;
 	int P_count = 0;
         int qb_facility;
         const char *value = NULL;
         bool delay_start = false;
         long delay = 0;
+        char *timeout_action = NULL;
 
 	if ((cmdname = strrchr(argv[0], '/')) == NULL) {
 		cmdname = argv[0];
 	} else {
 		++cmdname;
 	}
 
         watchdogdev = strdup("/dev/watchdog");
         watchdogdev_is_default = true;
         qb_facility = qb_log_facility2int("daemon");
         qb_log_init(cmdname, qb_facility, LOG_WARNING);
         sbd_set_format_string(QB_LOG_SYSLOG, "sbd");
 
         qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_TRUE);
         qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_FALSE);
         sbd_log_filter_ctl(NULL, LOG_NOTICE);
 
 	sbd_get_uname();
 
         value = getenv("SBD_DEVICE");
         if(value) {
 #if SUPPORT_SHARED_DISK
             int devices = parse_device_line(value);
             if(devices < 1) {
                 fprintf(stderr, "Invalid device line: %s\n", value);
 		exit_status = -2;
                 goto out;
             }
 #else
             fprintf(stderr, "Shared disk functionality not supported\n");
             exit_status = -2;
             goto out;
 #endif
         }
 
         value = getenv("SBD_PACEMAKER");
         if(value) {
             check_pcmk = crm_is_true(value);
             check_cluster = crm_is_true(value);
         }
         cl_log(LOG_INFO, "Enable pacemaker checks: %d (%s)", (int)check_pcmk, value?value:"default");
 
         value = getenv("SBD_STARTMODE");
         if(value == NULL) {
         } else if(strcmp(value, "clean") == 0) {
             start_mode = 1;
         } else if(strcmp(value, "always") == 0) {
             start_mode = 0;
         }
         cl_log(LOG_INFO, "Start mode set to: %d (%s)", (int)start_mode, value?value:"default");
 
         value = getenv("SBD_WATCHDOG_DEV");
         if(value) {
             free(watchdogdev);
             watchdogdev = strdup(value);
             watchdogdev_is_default = false;
         }
 
         /* SBD_WATCHDOG has been dropped from sbd.sysconfig example.
          * This is for backward compatibility. */
         value = getenv("SBD_WATCHDOG");
         if(value) {
             watchdog_use = crm_is_true(value);
         }
 
         value = getenv("SBD_WATCHDOG_TIMEOUT");
         if(value) {
             timeout_watchdog = crm_get_msec(value) / 1000;
             if(timeout_watchdog > 5) {
                 timeout_watchdog_warn = (int)timeout_watchdog / 5 * 3;
             }
         }
 
         value = getenv("SBD_PIDFILE");
         if(value) {
             pidfile = strdup(value);
             cl_log(LOG_INFO, "pidfile set to %s", pidfile);
         }
 
         value = getenv("SBD_DELAY_START");
         if(value) {
             delay_start = crm_is_true(value);
 
             if (!delay_start) {
                 delay = crm_get_msec(value) / 1000;
                 if (delay > 0) {
                     delay_start = true;
                 }
             }
         }
         cl_log(LOG_DEBUG, "Delay start: %s%s%s",
                delay_start? "yes (" : "no",
                delay_start? (delay > 0 ? value: "msgwait") : "",
                delay_start? ")" : "");
 
-	while ((c = getopt(argc, argv, "czC:DPRTWZhvw:d:n:p:1:2:3:4:5:t:I:F:S:s:")) != -1) {
+        value = getenv("SBD_TIMEOUT_ACTION");
+        if(value) {
+            timeout_action = strdup(value);
+        }
+
+	while ((c = getopt(argc, argv, "czC:DPRTWZhvw:d:n:p:1:2:3:4:5:t:I:F:S:s:r:")) != -1) {
 		switch (c) {
 		case 'D':
 			break;
 		case 'Z':
 			debug_mode++;
 			cl_log(LOG_INFO, "Debug mode now at level %d", (int)debug_mode);
 			break;
 		case 'R':
 			skip_rt = 1;
 			cl_log(LOG_INFO, "Realtime mode deactivated.");
 			break;
 		case 'S':
 			start_mode = atoi(optarg);
 			cl_log(LOG_INFO, "Start mode set to: %d", (int)start_mode);
 			break;
 		case 's':
 			timeout_startup = atoi(optarg);
 			cl_log(LOG_INFO, "Start timeout set to: %d", (int)timeout_startup);
 			break;
 		case 'v':
                     debug++;
                     if(debug == 1) {
                         sbd_log_filter_ctl(NULL, LOG_INFO);
                         cl_log(LOG_INFO, "Verbose mode enabled.");
 
                     } else if(debug == 2) {
                         sbd_log_filter_ctl(NULL, LOG_DEBUG);
                         cl_log(LOG_INFO, "Debug mode enabled.");
 
                     } else if(debug == 3) {
                         /* Go nuts, turn on pacemaker's logging too */
                         sbd_log_filter_ctl("*", LOG_DEBUG);
                         cl_log(LOG_INFO, "Debug library mode enabled.");
                     }
                     break;
 		case 'T':
 			watchdog_set_timeout = 0;
 			cl_log(LOG_INFO, "Setting watchdog timeout disabled; using defaults.");
 			break;
 		case 'W':
 			W_count++;
 			break;
 		case 'w':
                         cl_log(LOG_NOTICE, "Using watchdog device '%s'", watchdogdev);
                         free(watchdogdev);
                         watchdogdev = strdup(optarg);
                         watchdogdev_is_default = false;
 			break;
 		case 'd':
 #if SUPPORT_SHARED_DISK
 			recruit_servant(optarg, 0);
 #else
                         fprintf(stderr, "Shared disk functionality not supported\n");
 			exit_status = -2;
 			goto out;
 #endif
 			break;
 		case 'c':
 			c_count++;
 			break;
 		case 'P':
 			P_count++;
 			break;
 		case 'z':
 			disk_priority = 0;
 			break;
 		case 'n':
 			local_uname = strdup(optarg);
 			cl_log(LOG_INFO, "Overriding local hostname to %s", local_uname);
 			break;
 		case 'p':
 			pidfile = strdup(optarg);
 			cl_log(LOG_INFO, "pidfile set to %s", pidfile);
 			break;
 		case 'C':
 			timeout_watchdog_crashdump = atoi(optarg);
 			cl_log(LOG_INFO, "Setting crashdump watchdog timeout to %d",
 					(int)timeout_watchdog_crashdump);
 			break;
 		case '1':
 			timeout_watchdog = atoi(optarg);
                         if(timeout_watchdog > 5) {
                             timeout_watchdog_warn = (int)timeout_watchdog / 5 * 3;
                         }
 			break;
 		case '2':
 			timeout_allocate = atoi(optarg);
 			break;
 		case '3':
 			timeout_loop = atoi(optarg);
 			break;
 		case '4':
 			timeout_msgwait = atoi(optarg);
 			break;
 		case '5':
 			timeout_watchdog_warn = atoi(optarg);
 			cl_log(LOG_INFO, "Setting latency warning to %d",
 					(int)timeout_watchdog_warn);
 			break;
 		case 't':
 			servant_restart_interval = atoi(optarg);
 			cl_log(LOG_INFO, "Setting servant restart interval to %d",
 					(int)servant_restart_interval);
 			break;
 		case 'I':
 			timeout_io = atoi(optarg);
 			cl_log(LOG_INFO, "Setting IO timeout to %d",
 					(int)timeout_io);
 			break;
 		case 'F':
 			servant_restart_count = atoi(optarg);
 			cl_log(LOG_INFO, "Servant restart count set to %d",
 					(int)servant_restart_count);
 			break;
+		case 'r':
+			if (timeout_action) {
+				free(timeout_action);
+			}
+			timeout_action = strdup(optarg);
+			break;
 		case 'h':
 			usage();
 			return (0);
 		default:
 			exit_status = -2;
 			goto out;
 			break;
 		}
 	}
 
 	if (watchdogdev == NULL || strcmp(watchdogdev, "/dev/null") == 0) {
             watchdog_use = 0;
 
 	} else if (W_count > 0) {
             watchdog_use = arg_enabled(W_count);
         }
 
 	if (watchdog_use) {
 		cl_log(LOG_INFO, "Watchdog enabled.");
 	} else {
 		cl_log(LOG_INFO, "Watchdog disabled.");
 	}
 
 	if (c_count > 0) {
 		check_cluster = arg_enabled(c_count);
 	}
 
 	if (P_count > 0) {
 		check_pcmk = arg_enabled(P_count);
 	}
 
 	if ((disk_count > 0) && (strlen(local_uname) > SECTOR_NAME_MAX)) {
 		fprintf(stderr, "Node name mustn't be longer than %d chars.\n",
 			SECTOR_NAME_MAX);
 		fprintf(stderr, "If uname is longer define a name to be used by sbd.\n");
 		exit_status = -1;
 		goto out;
 	}
 
 	if (disk_count > 3) {
 		fprintf(stderr, "You can specify up to 3 devices via the -d option.\n");
 		exit_status = -1;
 		goto out;
 	}
 
 	/* There must at least be one command following the options: */
 	if ((argc - optind) < 1) {
 		fprintf(stderr, "Not enough arguments.\n");
 		exit_status = -2;
 		goto out;
 	}
 
 	if (init_set_proc_title(argc, argv, envp) < 0) {
 		fprintf(stderr, "Allocation of proc title failed.\n");
 		exit_status = -1;
 		goto out;
 	}
 
+	if (timeout_action) {
+		char *p[2];
+		int i;
+		char c;
+		int nrflags = sscanf(timeout_action, "%m[a-z],%m[a-z]%c", &p[0], &p[1], &c);
+		bool parse_error = (nrflags < 1) || (nrflags > 2);
+
+		for (i = 0; (i < nrflags) && (i < 2); i++) {
+			if (!strcmp(p[i], "reboot")) {
+				timeout_sysrq_char = 'b';
+			} else if (!strcmp(p[i], "crashdump")) {
+				timeout_sysrq_char = 'c';
+			} else if (!strcmp(p[i], "off")) {
+				timeout_sysrq_char = 'o';
+			} else if (!strcmp(p[i], "flush")) {
+				do_flush = true;
+			} else if (!strcmp(p[i], "noflush")) {
+				do_flush = false;
+			} else {
+				parse_error = true;
+			}
+			free(p[i]);
+		}
+		if (parse_error) {
+			fprintf(stderr, "Failed to parse timeout-action \"%s\".\n",
+				timeout_action);
+			exit_status = -1;
+			goto out;
+		}
+	}
+	cl_log(LOG_NOTICE, "%s flush + writing \'%c\' to sysrq on timeout",
+		do_flush?"Doing":"Skipping", timeout_sysrq_char);
+
 #if SUPPORT_SHARED_DISK
 	if (strcmp(argv[optind], "create") == 0) {
 		exit_status = init_devices(servants_leader);
 
         } else if (strcmp(argv[optind], "dump") == 0) {
 		exit_status = dump_headers(servants_leader);
 
         } else if (strcmp(argv[optind], "allocate") == 0) {
             exit_status = allocate_slots(argv[optind + 1], servants_leader);
 
         } else if (strcmp(argv[optind], "list") == 0) {
 		exit_status = list_slots(servants_leader);
 
         } else if (strcmp(argv[optind], "message") == 0) {
             exit_status = messenger(argv[optind + 1], argv[optind + 2], servants_leader);
 
         } else if (strcmp(argv[optind], "ping") == 0) {
             exit_status = ping_via_slots(argv[optind + 1], servants_leader);
 
         } else if (strcmp(argv[optind], "watch") == 0) {
                 if(disk_count > 0) {
                     /* If no devices are specified, its not an error to be unable to find one */
                     open_any_device(servants_leader);
                 }
 
                 if (delay_start) {
                     if (delay <= 0) {
                         delay = get_first_msgwait(servants_leader);
                     }
 
                     sleep((unsigned long) delay);
                 }
 
 	} else {
 		exit_status = -2;
 	}
 #endif
 
         if (strcmp(argv[optind], "query-watchdog") == 0) {
             exit_status = watchdog_info();
         } else if (strcmp(argv[optind], "test-watchdog") == 0) {
             exit_status = watchdog_test();
         } else if (strcmp(argv[optind], "watch") == 0) {
             /* sleep $(sbd $SBD_DEVICE_ARGS dump | grep -m 1 msgwait | awk '{print $4}') 2>/dev/null */
 
                 /* We only want this to have an effect during watch right now;
                  * pinging and fencing would be too confused */
                 cl_log(LOG_INFO, "Turning on pacemaker checks: %d", check_pcmk);
                 if (check_pcmk) {
                         recruit_servant("pcmk", 0);
 #if SUPPORT_PLUGIN
                         check_cluster = 1;
 #endif
                 }
 
                 cl_log(LOG_INFO, "Turning on cluster checks: %d", check_cluster);
                 if (check_cluster) {
                         recruit_servant("cluster", 0);
                 }
 
                 exit_status = inquisitor();
         }
         
   out:
 	if (exit_status < 0) {
 		if (exit_status == -2) {
 			usage();
 		} else {
 			fprintf(stderr, "sbd failed; please check the logs.\n");
 		}
 		return (1);
 	}
 	return (0);
 }
diff --git a/src/sbd-md.c b/src/sbd-md.c
index a736118..579d273 100644
--- a/src/sbd-md.c
+++ b/src/sbd-md.c
@@ -1,1239 +1,1239 @@
 /*
  * Copyright (C) 2013 Lars Marowsky-Bree <lmb@suse.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 software 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 "sbd.h"
 
 #define SBD_MSG_EMPTY	0x00
 #define SBD_MSG_TEST	0x01
 #define SBD_MSG_RESET	0x02
 #define SBD_MSG_OFF	0x03
 #define SBD_MSG_EXIT	0x04
 #define SBD_MSG_CRASHDUMP	0x05
 
 #define SLOT_TO_SECTOR(slot) (1+slot*2)
 #define MBOX_TO_SECTOR(mbox) (2+mbox*2)
 
 extern int disk_count;
 
 /* These have to match the values in the header of the partition */
 static char		sbd_magic[8] = "SBD_SBD_";
 static char		sbd_version  = 0x02;
 
 struct slot_msg_arg_t {
 	const char* name;
 	const char* msg;
 };
 
 static signed char
 cmd2char(const char *cmd)
 {
 	if (strcmp("clear", cmd) == 0) {
 		return SBD_MSG_EMPTY;
 	} else if (strcmp("test", cmd) == 0) {
 		return SBD_MSG_TEST;
 	} else if (strcmp("reset", cmd) == 0) {
 		return SBD_MSG_RESET;
 	} else if (strcmp("off", cmd) == 0) {
 		return SBD_MSG_OFF;
 	} else if (strcmp("exit", cmd) == 0) {
 		return SBD_MSG_EXIT;
 	} else if (strcmp("crashdump", cmd) == 0) {
 		return SBD_MSG_CRASHDUMP;
 	}
 	return -1;
 }
 
 static const char*
 char2cmd(const char cmd)
 {
 	switch (cmd) {
 		case SBD_MSG_EMPTY:
 			return "clear";
 			break;
 		case SBD_MSG_TEST:
 			return "test";
 			break;
 		case SBD_MSG_RESET:
 			return "reset";
 			break;
 		case SBD_MSG_OFF:
 			return "off";
 			break;
 		case SBD_MSG_EXIT:
 			return "exit";
 			break;
 		case SBD_MSG_CRASHDUMP:
 			return "crashdump";
 			break;
 		default:
 			return "undefined";
 			break;
 	}
 }
 
 static void
 close_device(struct sbd_context *st)
 {
 	close(st->devfd);
 	free(st);
 }
 
 static struct sbd_context *
 open_device(const char* devname, int loglevel)
 {
 	struct sbd_context *st;
 
 	if (!devname)
 		return NULL;
 
 	st = malloc(sizeof(struct sbd_context));
 	if (!st)
 		return NULL;
 	memset(st, 0, sizeof(struct sbd_context));
 
 	if (io_setup(1, &st->ioctx) != 0) {
 		cl_perror("io_setup failed");
 		free(st);
 		return NULL;
 	}
 	
 	st->devfd = open(devname, O_SYNC|O_RDWR|O_DIRECT);
 
 	if (st->devfd == -1) {
 		if (loglevel == LOG_DEBUG) {
 			DBGLOG(loglevel, "Opening device %s failed.", devname);
 		} else {
 			cl_log(loglevel, "Opening device %s failed.", devname);
 		}
 		free(st);
 		return NULL;
 	}
 
 	ioctl(st->devfd, BLKSSZGET, &sector_size);
 
 	if (sector_size == 0) {
 		cl_perror("Get sector size failed.\n");
 		close_device(st);
 		return NULL;
 	}
 
 	return st;
 }
 
 static void *
 sector_alloc(void)
 {
 	void *x;
 
 	x = valloc(sector_size);
 	if (!x) {
 		exit(1);
 	}
 	memset(x, 0, sector_size);
 
 	return x;
 }
 
 static int
 sector_io(struct sbd_context *st, int sector, void *data, int rw)
 {
 	struct timespec	timeout;
 	struct io_event event;
 	struct iocb	*ios[1] = { &st->io };
 	long		r;
 
 	timeout.tv_sec  = timeout_io;
 	timeout.tv_nsec = 0;
 
 	memset(&st->io, 0, sizeof(struct iocb));
 	if (rw) {
 		io_prep_pwrite(&st->io, st->devfd, data, sector_size, sector_size * sector);
 	} else {
 		io_prep_pread(&st->io, st->devfd, data, sector_size, sector_size * sector);
 	}
 
 	if (io_submit(st->ioctx, 1, ios) != 1) {
 		cl_log(LOG_ERR, "Failed to submit IO request! (rw=%d)", rw);
 		return -1;
 	}
 
 	errno = 0;
 	r = io_getevents(st->ioctx, 1L, 1L, &event, &timeout);
 
 	if (r < 0 ) {
 		cl_log(LOG_ERR, "Failed to retrieve IO events (rw=%d)", rw);
 		return -1;
 	} else if (r < 1L) {
 		cl_log(LOG_INFO, "Cancelling IO request due to timeout (rw=%d)", rw);
 		r = io_cancel(st->ioctx, ios[0], &event);
 		if (r) {
 			DBGLOG(LOG_INFO, "Could not cancel IO request (rw=%d)", rw);
 			/* Doesn't really matter, debugging information.
 			 */
 		}
 		return -1;
 	} else if (r > 1L) {
 		cl_log(LOG_ERR, "More than one IO was returned (r=%ld)", r);
 		return -1;
 	}
 
 	
 	/* IO is happy */
 	if (event.res == sector_size) {
 		return 0;
 	} else {
 		cl_log(LOG_ERR, "Short IO (rw=%d, res=%lu, sector_size=%d)",
 				rw, event.res, sector_size);
 		return -1;
 	}
 }
 
 static int
 sector_write(struct sbd_context *st, int sector, void *data)
 {
 	return sector_io(st, sector, data, 1);
 }
 
 static int
 sector_read(struct sbd_context *st, int sector, void *data)
 {
 	return sector_io(st, sector, data, 0);
 }
 
 static int
 slot_read(struct sbd_context *st, int slot, struct sector_node_s *s_node)
 {
 	return sector_read(st, SLOT_TO_SECTOR(slot), s_node);
 }
 
 static int
 slot_write(struct sbd_context *st, int slot, struct sector_node_s *s_node)
 {
 	return sector_write(st, SLOT_TO_SECTOR(slot), s_node);
 }
 
 static int
 mbox_write(struct sbd_context *st, int mbox, struct sector_mbox_s *s_mbox)
 {
 	return sector_write(st, MBOX_TO_SECTOR(mbox), s_mbox);
 }
 
 static int
 mbox_read(struct sbd_context *st, int mbox, struct sector_mbox_s *s_mbox)
 {
 	return sector_read(st, MBOX_TO_SECTOR(mbox), s_mbox);
 }
 
 static int
 mbox_write_verify(struct sbd_context *st, int mbox, struct sector_mbox_s *s_mbox)
 {
 	void *data;
 	int rc = 0;
 
 	if (sector_write(st, MBOX_TO_SECTOR(mbox), s_mbox) < 0)
 		return -1;
 
 	data = sector_alloc();
 	if (sector_read(st, MBOX_TO_SECTOR(mbox), data) < 0) {
 		rc = -1;
 		goto out;
 	}
 
 
 	if (memcmp(s_mbox, data, sector_size) != 0) {
 		cl_log(LOG_ERR, "Write verification failed!");
 		rc = -1;
 		goto out;
 	}
 	rc = 0;
 out:
 	free(data);
 	return rc;
 }
 
 static int header_write(struct sbd_context *st, struct sector_header_s *s_header)
 {
 	s_header->sector_size = htonl(s_header->sector_size);
 	s_header->timeout_watchdog = htonl(s_header->timeout_watchdog);
 	s_header->timeout_allocate = htonl(s_header->timeout_allocate);
 	s_header->timeout_loop = htonl(s_header->timeout_loop);
 	s_header->timeout_msgwait = htonl(s_header->timeout_msgwait);
 	return sector_write(st, 0, s_header);
 }
 
 static int
 header_read(struct sbd_context *st, struct sector_header_s *s_header)
 {
 	if (sector_read(st, 0, s_header) < 0)
 		return -1;
 
 	s_header->sector_size = ntohl(s_header->sector_size);
 	s_header->timeout_watchdog = ntohl(s_header->timeout_watchdog);
 	s_header->timeout_allocate = ntohl(s_header->timeout_allocate);
 	s_header->timeout_loop = ntohl(s_header->timeout_loop);
 	s_header->timeout_msgwait = ntohl(s_header->timeout_msgwait);
 	/* This sets the global defaults: */
 	timeout_watchdog = s_header->timeout_watchdog;
 	timeout_allocate = s_header->timeout_allocate;
 	timeout_loop     = s_header->timeout_loop;
 	timeout_msgwait  = s_header->timeout_msgwait;
 
 	return 0;
 }
 
 static int
 valid_header(const struct sector_header_s *s_header)
 {
 	if (memcmp(s_header->magic, sbd_magic, sizeof(s_header->magic)) != 0) {
 		cl_log(LOG_ERR, "Header magic does not match.");
 		return -1;
 	}
 	if (s_header->version != sbd_version) {
 		cl_log(LOG_ERR, "Header version does not match.");
 		return -1;
 	}
 	if (s_header->sector_size != sector_size) {
 		cl_log(LOG_ERR, "Header sector size does not match.");
 		return -1;
 	}
 	return 0;
 }
 
 static struct sector_header_s *
 header_get(struct sbd_context *st)
 {
 	struct sector_header_s *s_header;
 	s_header = sector_alloc();
 
 	if (header_read(st, s_header) < 0) {
 		cl_log(LOG_ERR, "Unable to read header from device %d", st->devfd);
 		return NULL;
 	}
 
 	if (valid_header(s_header) < 0) {
 		cl_log(LOG_ERR, "header on device %d is not valid.", st->devfd);
 		return NULL;
 	}
 
 	/* cl_log(LOG_INFO, "Found version %d header with %d slots",
 			s_header->version, s_header->slots); */
 
 	return s_header;
 }
 
 static int
 header_dump(struct sbd_context *st)
 {
 	struct sector_header_s *s_header;
 	char uuid[37];
 
 	s_header = header_get(st);
 	if (s_header == NULL)
 		return -1;
 
 	printf("Header version     : %u.%u\n", s_header->version,
 			s_header->minor_version);
 	if (s_header->minor_version > 0) {
 		uuid_unparse_lower(s_header->uuid, uuid);
 		printf("UUID               : %s\n", uuid);
 	}
 
 	printf("Number of slots    : %u\n", s_header->slots);
 	printf("Sector size        : %lu\n",
 			(unsigned long)s_header->sector_size);
 	printf("Timeout (watchdog) : %lu\n",
 			(unsigned long)s_header->timeout_watchdog);
 	printf("Timeout (allocate) : %lu\n",
 			(unsigned long)s_header->timeout_allocate);
 	printf("Timeout (loop)     : %lu\n",
 			(unsigned long)s_header->timeout_loop);
 	printf("Timeout (msgwait)  : %lu\n",
 			(unsigned long)s_header->timeout_msgwait);
 	return 0;
 }
 
 static int
 init_device(struct sbd_context *st)
 {
 	struct sector_header_s	*s_header;
 	struct sector_node_s	*s_node;
 	struct sector_mbox_s	*s_mbox;
 	struct stat 		s;
 	char			uuid[37];
 	int			i;
 	int			rc = 0;
 
 	s_header = sector_alloc();
 	s_node = sector_alloc();
 	s_mbox = sector_alloc();
 	memcpy(s_header->magic, sbd_magic, sizeof(s_header->magic));
 	s_header->version = sbd_version;
 	s_header->slots = 255;
 	s_header->sector_size = sector_size;
 	s_header->timeout_watchdog = timeout_watchdog;
 	s_header->timeout_allocate = timeout_allocate;
 	s_header->timeout_loop = timeout_loop;
 	s_header->timeout_msgwait = timeout_msgwait;
 
 	s_header->minor_version = 1;
 	uuid_generate(s_header->uuid);
 	uuid_unparse_lower(s_header->uuid, uuid);
 
 	fstat(st->devfd, &s);
 	/* printf("st_size = %ld, st_blksize = %ld, st_blocks = %ld\n",
 			s.st_size, s.st_blksize, s.st_blocks); */
 
 	cl_log(LOG_INFO, "Creating version %d.%d header on device %d (uuid: %s)",
 			s_header->version, s_header->minor_version,
 			st->devfd, uuid);
 	fprintf(stdout, "Creating version %d.%d header on device %d (uuid: %s)\n",
 			s_header->version, s_header->minor_version,
 			st->devfd, uuid);
 	if (header_write(st, s_header) < 0) {
 		rc = -1; goto out;
 	}
 	cl_log(LOG_INFO, "Initializing %d slots on device %d",
 			s_header->slots,
 			st->devfd);
 	fprintf(stdout, "Initializing %d slots on device %d\n",
 			s_header->slots,
 			st->devfd);
 	for (i=0;i < s_header->slots;i++) {
 		if (slot_write(st, i, s_node) < 0) {
 			rc = -1; goto out;
 		}
 		if (mbox_write(st, i, s_mbox) < 0) {
 			rc = -1; goto out;
 		}
 	}
 
 out:	free(s_node);
 	free(s_header);
 	free(s_mbox);
 	return(rc);
 }
 
 /* Check if there already is a slot allocated to said name; returns the
  * slot number. If not found, returns -1.
  * This is necessary because slots might not be continuous. */
 static int
 slot_lookup(struct sbd_context *st, const struct sector_header_s *s_header, const char *name)
 {
 	struct sector_node_s	*s_node = NULL;
 	int 			i;
 	int			rc = -1;
 
 	if (!name) {
 		cl_log(LOG_ERR, "slot_lookup(): No name specified.\n");
 		goto out;
 	}
 
 	s_node = sector_alloc();
 
 	for (i=0; i < s_header->slots; i++) {
 		if (slot_read(st, i, s_node) < 0) {
 			rc = -2; goto out;
 		}
 		if (s_node->in_use != 0) {
 			if (strncasecmp(s_node->name, name,
 						SECTOR_NAME_MAX) == 0) {
 				DBGLOG(LOG_INFO, "%s owns slot %d", name, i);
 				rc = i; goto out;
 			}
 		}
 	}
 
 out:	free(s_node);
 	return rc;
 }
 
 static int
 slot_unused(struct sbd_context *st, const struct sector_header_s *s_header)
 {
 	struct sector_node_s	*s_node;
 	int 			i;
 	int			rc = -1;
 
 	s_node = sector_alloc();
 
 	for (i=0; i < s_header->slots; i++) {
 		if (slot_read(st, i, s_node) < 0) {
 			rc = -1; goto out;
 		}
 		if (s_node->in_use == 0) {
 			rc = i; goto out;
 		}
 	}
 
 out:	free(s_node);
 	return rc;
 }
 
 
 static int
 slot_allocate(struct sbd_context *st, const char *name)
 {
 	struct sector_header_s	*s_header = NULL;
 	struct sector_node_s	*s_node = NULL;
 	struct sector_mbox_s	*s_mbox = NULL;
 	int			i;
 	int			rc = 0;
 
 	if (!name) {
 		cl_log(LOG_ERR, "slot_allocate(): No name specified.\n");
 		fprintf(stderr, "slot_allocate(): No name specified.\n");
 		rc = -1; goto out;
 	}
 
 	s_header = header_get(st);
 	if (!s_header) {
 		rc = -1; goto out;
 	}
 
 	s_node = sector_alloc();
 	s_mbox = sector_alloc();
 
 	while (1) {
 		i = slot_lookup(st, s_header, name);
 		if ((i >= 0) || (i == -2)) {
 			/* -1 is "no slot found", in which case we
 			 * proceed to allocate a new one.
 			 * -2 is "read error during lookup", in which
 			 * case we error out too
 			 * >= 0 is "slot already allocated" */
 			rc = i; goto out;
 		}
 
 		i = slot_unused(st, s_header);
 		if (i >= 0) {
 			cl_log(LOG_INFO, "slot %d is unused - trying to own", i);
 			fprintf(stdout, "slot %d is unused - trying to own\n", i);
 			memset(s_node, 0, sizeof(*s_node));
 			s_node->in_use = 1;
 			strncpy(s_node->name, name, SECTOR_NAME_MAX);
 			if (slot_write(st, i, s_node) < 0) {
 				rc = -1; goto out;
 			}
 			sleep(timeout_allocate);
 		} else {
 			cl_log(LOG_ERR, "No more free slots.");
 			fprintf(stderr, "No more free slots.\n");
 			rc = -1; goto out;
 		}
 	}
 
 out:	free(s_node);
 	free(s_header);
 	free(s_mbox);
 	return(rc);
 }
 
 static int
 slot_list(struct sbd_context *st)
 {
 	struct sector_header_s	*s_header = NULL;
 	struct sector_node_s	*s_node = NULL;
 	struct sector_mbox_s	*s_mbox = NULL;
 	int 			i;
 	int			rc = 0;
 
 	s_header = header_get(st);
 	if (!s_header) {
 		rc = -1; goto out;
 	}
 
 	s_node = sector_alloc();
 	s_mbox = sector_alloc();
 
 	for (i=0; i < s_header->slots; i++) {
 		if (slot_read(st, i, s_node) < 0) {
 			rc = -1; goto out;
 		}
 		if (s_node->in_use > 0) {
 			if (mbox_read(st, i, s_mbox) < 0) {
 				rc = -1; goto out;
 			}
 			printf("%d\t%s\t%s\t%s\n",
 				i, s_node->name, char2cmd(s_mbox->cmd),
 				s_mbox->from);
 		}
 	}
 
 out:	free(s_node);
 	free(s_header);
 	free(s_mbox);
 	return rc;
 }
 
 static int
 slot_msg(struct sbd_context *st, const char *name, const char *cmd)
 {
 	struct sector_header_s	*s_header = NULL;
 	struct sector_mbox_s	*s_mbox = NULL;
 	int			mbox;
 	int			rc = 0;
 	char			uuid[37];
 
 	if (!name || !cmd) {
 		cl_log(LOG_ERR, "slot_msg(): No recipient / cmd specified.\n");
 		rc = -1; goto out;
 	}
 
 	s_header = header_get(st);
 	if (!s_header) {
 		rc = -1; goto out;
 	}
 
 	if (strcmp(name, "LOCAL") == 0) {
 		name = local_uname;
 	}
 	
 	if (s_header->minor_version > 0) {
 		uuid_unparse_lower(s_header->uuid, uuid);
 		cl_log(LOG_INFO, "Device UUID: %s", uuid);
 	}
 
 	mbox = slot_lookup(st, s_header, name);
 	if (mbox < 0) {
 		cl_log(LOG_ERR, "slot_msg(): No slot found for %s.", name);
 		rc = -1; goto out;
 	}
 
 	s_mbox = sector_alloc();
 
 	s_mbox->cmd = cmd2char(cmd);
 	if (s_mbox->cmd < 0) {
 		cl_log(LOG_ERR, "slot_msg(): Invalid command %s.", cmd);
 		rc = -1; goto out;
 	}
 
 	strncpy(s_mbox->from, local_uname, SECTOR_NAME_MAX);
 
 	cl_log(LOG_INFO, "Writing %s to node slot %s",
 			cmd, name);
 	if (mbox_write_verify(st, mbox, s_mbox) < -1) {
 		rc = -1; goto out;
 	}
 	if (strcasecmp(cmd, "exit") != 0) {
 		cl_log(LOG_INFO, "Messaging delay: %d",
 				(int)timeout_msgwait);
 		sleep(timeout_msgwait);
 	}
 	cl_log(LOG_INFO, "%s successfully delivered to %s",
 			cmd, name);
 
 out:	free(s_mbox);
 	free(s_header);
 	return rc;
 }
 
 static int
 slot_ping(struct sbd_context *st, const char *name)
 {
 	struct sector_header_s	*s_header = NULL;
 	struct sector_mbox_s	*s_mbox = NULL;
 	int			mbox;
 	int			waited = 0;
 	int			rc = 0;
 
 	if (!name) {
 		cl_log(LOG_ERR, "slot_ping(): No recipient specified.\n");
 		rc = -1; goto out;
 	}
 
 	s_header = header_get(st);
 	if (!s_header) {
 		rc = -1; goto out;
 	}
 
 	if (strcmp(name, "LOCAL") == 0) {
 		name = local_uname;
 	}
 
 	mbox = slot_lookup(st, s_header, name);
 	if (mbox < 0) {
 		cl_log(LOG_ERR, "slot_msg(): No slot found for %s.", name);
 		rc = -1; goto out;
 	}
 
 	s_mbox = sector_alloc();
 	s_mbox->cmd = SBD_MSG_TEST;
 
 	strncpy(s_mbox->from, local_uname, SECTOR_NAME_MAX);
 
 	DBGLOG(LOG_DEBUG, "Pinging node %s", name);
 	if (mbox_write(st, mbox, s_mbox) < -1) {
 		rc = -1; goto out;
 	}
 
 	rc = -1;
 	while (waited <= timeout_msgwait) {
 		if (mbox_read(st, mbox, s_mbox) < 0)
 			break;
 		if (s_mbox->cmd != SBD_MSG_TEST) {
 			rc = 0;
 			break;
 		}
 		sleep(1);
 		waited++;
 	}
 
 	if (rc == 0) {
 		cl_log(LOG_DEBUG, "%s successfully pinged.", name);
 	} else {
 		cl_log(LOG_ERR, "%s failed to ping.", name);
 	}
 
 out:	free(s_mbox);
 	free(s_header);
 	return rc;
 }
 
 int init_devices(struct servants_list_item *servants)
 {
 	int rc = 0;
 	struct sbd_context *st;
 	struct servants_list_item *s;
 
 	for (s = servants; s; s = s->next) {
 		fprintf(stdout, "Initializing device %s\n",
 				s->devname);
 		st = open_device(s->devname, LOG_ERR);
 		if (!st) {
 			return -1;
 		}
 		rc = init_device(st);
 		close_device(st);
 		if (rc == -1) {
 			fprintf(stderr, "Failed to init device %s\n", s->devname);
 			return rc;
 		}
 		fprintf(stdout, "Device %s is initialized.\n", s->devname);
 	}
 	return 0;
 }
 
 static int slot_msg_wrapper(const char* devname, int mode, const void* argp)
 {
 	int rc = 0;
 	struct sbd_context *st;
 	const struct slot_msg_arg_t* arg = (const struct slot_msg_arg_t*)argp;
 
         st = open_device(devname, LOG_WARNING);
         if (!st) 
 		return -1;
 	cl_log(LOG_INFO, "Delivery process handling %s",
 			devname);
 	rc = slot_msg(st, arg->name, arg->msg);
 	close_device(st);
 	return rc;
 }
 
 static int slot_ping_wrapper(const char* devname, int mode, const void* argp)
 {
 	int rc = 0;
 	const char* name = (const char*)argp;
 	struct sbd_context *st;
 
 	st = open_device(devname, LOG_WARNING);
 	if (!st)
 		return -1;
 	rc = slot_ping(st, name);
 	close_device(st);
 	return rc;
 }
 
 int allocate_slots(const char *name, struct servants_list_item *servants)
 {
 	int rc = 0;
 	struct sbd_context *st;
 	struct servants_list_item *s;
 
 	for (s = servants; s; s = s->next) {
 		fprintf(stdout, "Trying to allocate slot for %s on device %s.\n", 
 				name,
 				s->devname);
 		st = open_device(s->devname, LOG_WARNING);
 		if (!st) {
 			return -1;
 		}
 		rc = slot_allocate(st, name);
 		close_device(st);
 		if (rc < 0)
 			return rc;
 		fprintf(stdout, "Slot for %s has been allocated on %s.\n",
 				name,
 				s->devname);
 	}
 	return 0;
 }
 
 int list_slots(struct servants_list_item *servants)
 {
 	int rc = 0;
 	struct servants_list_item *s;
 	struct sbd_context *st;
 
 	for (s = servants; s; s = s->next) {
 		int rv  = 0;
 
 		st = open_device(s->devname, LOG_WARNING);
 		if (!st) {
 			rc = -1;
 			fprintf(stdout, "== disk %s unreadable!\n", s->devname);
 			continue;
 		}
 		rv = slot_list(st);
 		close_device(st);
 		if (rv == -1) {
 			rc = -1;
 			fprintf(stdout, "== Slots on disk %s NOT dumped\n", s->devname);
 		}
 	}
 	return rc;
 }
 
 int ping_via_slots(const char *name, struct servants_list_item *servants)
 {
 	int sig = 0;
 	pid_t pid = 0;
 	int status = 0;
 	int servants_finished = 0;
 	sigset_t procmask;
 	siginfo_t sinfo;
 	struct servants_list_item *s;
 
 	sigemptyset(&procmask);
 	sigaddset(&procmask, SIGCHLD);
 	sigprocmask(SIG_BLOCK, &procmask, NULL);
 
 	for (s = servants; s; s = s->next) {
             if(sbd_is_disk(s)) {
                 s->pid = assign_servant(s->devname, &slot_ping_wrapper, 0, (const void*)name);
             }
         }
 
 	while (servants_finished < disk_count) {
 		sig = sigwaitinfo(&procmask, &sinfo);
 		if (sig == SIGCHLD) {
 			while ((pid = wait(&status))) {
 				if (pid == -1 && errno == ECHILD) {
 					break;
 				} else {
 					s = lookup_servant_by_pid(pid);
 					if (sbd_is_disk(s)) {
 						servants_finished++;
 					}
 				}
 			}
 		}
 	}
 	return 0;
 }
 
 int quorum_write(int good_servants)
 {
 	return (good_servants > disk_count/2);
 }
 
 int messenger(const char *name, const char *msg, struct servants_list_item *servants)
 {
 	int sig = 0;
 	pid_t pid = 0;
 	int status = 0;
 	int servants_finished = 0;
 	int successful_delivery = 0;
 	sigset_t procmask;
 	siginfo_t sinfo;
 	struct servants_list_item *s;
 	struct slot_msg_arg_t slot_msg_arg = {name, msg};
 
 	sigemptyset(&procmask);
 	sigaddset(&procmask, SIGCHLD);
 	sigprocmask(SIG_BLOCK, &procmask, NULL);
 
 	for (s = servants; s; s = s->next) {
             s->pid = assign_servant(s->devname, &slot_msg_wrapper, 0, &slot_msg_arg);
 	}
 	
 	while (!(quorum_write(successful_delivery) || 
 		(servants_finished == disk_count))) {
 		sig = sigwaitinfo(&procmask, &sinfo);
 		if (sig == SIGCHLD) {
 			while ((pid = waitpid(-1, &status, WNOHANG))) {
 				if (pid == -1 && errno == ECHILD) {
 					break;
 				} else {
 					servants_finished++;
 					if (WIFEXITED(status)
 						&& WEXITSTATUS(status) == 0) {
 						DBGLOG(LOG_INFO, "Process %d succeeded.",
 								(int)pid);
 						successful_delivery++;
 					} else {
 						cl_log(LOG_WARNING, "Process %d failed to deliver!",
 								(int)pid);
 					}
 				}
 			}
 		}
 	}
 	if (quorum_write(successful_delivery)) {
 		cl_log(LOG_INFO, "Message successfully delivered.");
 		return 0;
 	} else {
 		cl_log(LOG_ERR, "Message is not delivered via more then a half of devices");
 		return -1;
 	}
 }
 
 unsigned long
 get_first_msgwait(struct servants_list_item *servants) 
 {
     unsigned long msgwait = 0;
     struct servants_list_item *s = servants;
 
     for (s = servants; s; s = s->next) {
         struct sbd_context *st;
         struct sector_header_s *s_header;
         st = open_device(s->devname, LOG_WARNING);
         if (!st) {
             continue;
         }
 
         s_header = header_get(st);
         if (s_header != NULL) {
             msgwait = (unsigned long)s_header->timeout_msgwait;
             close_device(st);
             return msgwait;
         }
 
         close_device(st);
     }
     return msgwait;
 }
 
 int dump_headers(struct servants_list_item *servants)
 {
 	int rc = 0;
 	struct servants_list_item *s = servants;
 	struct sbd_context *st;
 
 	for (s = servants; s; s = s->next) {
 		int rv;
 
 		fprintf(stdout, "==Dumping header on disk %s\n", s->devname);
 		st = open_device(s->devname, LOG_WARNING);
 		if (st) {
 			rv = header_dump(st);
 			close_device(st);
 		} else {
 			fprintf(stdout, "== disk %s unreadable!\n", s->devname);
 			rv = -1;
 		}
 
 		if (rv == -1) {
 			rc = -1;
 			fprintf(stdout, "==Header on disk %s NOT dumped\n", s->devname);
 		} else {
 			fprintf(stdout, "==Header on disk %s is dumped\n", s->devname);
 		}
 	}
 	return rc;
 }
 
 void open_any_device(struct servants_list_item *servants)
 {
 	struct sector_header_s *hdr_cur = NULL;
 	struct timespec t_0;
 	int t_wait = 0;
 
 	clock_gettime(CLOCK_MONOTONIC, &t_0);
 
 	while (!hdr_cur && t_wait < timeout_startup) {
 		struct timespec t_now;
 		struct servants_list_item* s;
 
 		for (s = servants; s; s = s->next) {
 			struct sbd_context *st = open_device(s->devname, LOG_DEBUG);
 			if (!st)
 				continue;
 			hdr_cur = header_get(st);
 			close_device(st);
 			if (hdr_cur)
 				break;
 		}
 		clock_gettime(CLOCK_MONOTONIC, &t_now);
 		t_wait = t_now.tv_sec - t_0.tv_sec;
 		if (!hdr_cur) {
 			sleep(timeout_loop);
 		}
 	}
 
 	if (hdr_cur) {
 		timeout_watchdog = hdr_cur->timeout_watchdog;
 		timeout_allocate = hdr_cur->timeout_allocate;
 		timeout_loop = hdr_cur->timeout_loop;
 		timeout_msgwait = hdr_cur->timeout_msgwait;
 	} else { 
 		cl_log(LOG_ERR, "No devices were available at start-up within %i seconds.",
 				timeout_startup);
 		exit(1);
 	}
 
 	free(hdr_cur);
 	return;
 }
 
 /*
  ::-::-::-::-::-::-::-::-::-::-::-::-::
    Begin disk based servant code
  ::-::-::-::-::-::-::-::-::-::-::-::-::
 */
 
 static int servant_check_timeout_inconsistent(struct sector_header_s *hdr)
 {
 	if (timeout_watchdog != hdr->timeout_watchdog) {
 		cl_log(LOG_WARNING, "watchdog timeout: %d versus %d on this device",
 				(int)timeout_watchdog, (int)hdr->timeout_watchdog);
 		return -1;
 	}
 	if (timeout_allocate != hdr->timeout_allocate) {
 		cl_log(LOG_WARNING, "allocate timeout: %d versus %d on this device",
 				(int)timeout_allocate, (int)hdr->timeout_allocate);
 		return -1;
 	}
 	if (timeout_loop != hdr->timeout_loop) {
 		cl_log(LOG_WARNING, "loop timeout: %d versus %d on this device",
 				(int)timeout_loop, (int)hdr->timeout_loop);
 		return -1;
 	}
 	if (timeout_msgwait != hdr->timeout_msgwait) {
 		cl_log(LOG_WARNING, "msgwait timeout: %d versus %d on this device",
 				(int)timeout_msgwait, (int)hdr->timeout_msgwait);
 		return -1;
 	}
 	return 0;
 }
 
 int servant(const char *diskname, int mode, const void* argp)
 {
 	struct sector_mbox_s *s_mbox = NULL;
 	struct sector_node_s *s_node = NULL;
 	struct sector_header_s	*s_header = NULL;
 	int mbox;
 	int rc = 0;
 	time_t t0, t1, latency;
 	union sigval signal_value;
 	sigset_t servant_masks;
 	struct sbd_context *st;
 	pid_t ppid;
 	char uuid[37];
 	const struct servants_list_item *s = argp;
 
 	if (!diskname) {
 		cl_log(LOG_ERR, "Empty disk name %s.", diskname);
 		return -1;
 	}
 
 	cl_log(LOG_INFO, "Servant starting for device %s", diskname);
 
 	/* Block most of the signals */
 	sigfillset(&servant_masks);
 	sigdelset(&servant_masks, SIGKILL);
 	sigdelset(&servant_masks, SIGFPE);
 	sigdelset(&servant_masks, SIGILL);
 	sigdelset(&servant_masks, SIGSEGV);
 	sigdelset(&servant_masks, SIGBUS);
 	sigdelset(&servant_masks, SIGALRM);
 	/* FIXME: check error */
 	sigprocmask(SIG_SETMASK, &servant_masks, NULL);
 
 	st = open_device(diskname, LOG_WARNING);
 	if (!st) {
 		exit(EXIT_MD_IO_FAIL);
 	}
 
 	s_header = header_get(st);
 	if (!s_header) {
 		cl_log(LOG_ERR, "Not a valid header on %s", diskname);
 		exit(EXIT_MD_IO_FAIL);
 	}
 
 	if (servant_check_timeout_inconsistent(s_header) < 0) {
 		cl_log(LOG_ERR, "Timeouts on %s do not match first device",
 				diskname);
 		exit(EXIT_MD_IO_FAIL);
 	}
 
 	if (s_header->minor_version > 0) {
 		uuid_unparse_lower(s_header->uuid, uuid);
 		cl_log(LOG_INFO, "Device %s uuid: %s", diskname, uuid);
 	}
 
 	mbox = slot_allocate(st, local_uname);
 	if (mbox < 0) {
 		cl_log(LOG_ERR,
 		       "No slot allocated, and automatic allocation failed for disk %s.",
 		       diskname);
 		rc = EXIT_MD_IO_FAIL;
 		goto out;
 	}
 	s_node = sector_alloc();
 	if (slot_read(st, mbox, s_node) < 0) {
 		cl_log(LOG_ERR, "Unable to read node entry on %s",
 				diskname);
 		exit(EXIT_MD_IO_FAIL);
 	}
 
 	cl_log(LOG_NOTICE, "Monitoring slot %d on disk %s", mbox, diskname);
 	if (s_header->minor_version == 0) {
 		set_proc_title("sbd: watcher: %s - slot: %d", diskname, mbox);
 	} else {
 		set_proc_title("sbd: watcher: %s - slot: %d - uuid: %s",
 				diskname, mbox, uuid);
 	}
 
 	s_mbox = sector_alloc();
 	if (s->first_start) {
 		if (mode > 0) {
 			if (mbox_read(st, mbox, s_mbox) < 0) {
 				cl_log(LOG_ERR, "mbox read failed during start-up in servant.");
 				rc = EXIT_MD_IO_FAIL;
 				goto out;
 			}
 			if (s_mbox->cmd != SBD_MSG_EXIT &&
 					s_mbox->cmd != SBD_MSG_EMPTY) {
 				/* Not a clean stop. Abort start-up */
 				cl_log(LOG_WARNING, "Found fencing message - aborting start-up. Manual intervention required!");
 				ppid = getppid();
 				sigqueue(ppid, SIG_EXITREQ, signal_value);
 				rc = 0;
 				goto out;
 			}
 		}
 		DBGLOG(LOG_INFO, "First servant start - zeroing inbox");
 		memset(s_mbox, 0, sizeof(*s_mbox));
 		if (mbox_write(st, mbox, s_mbox) < 0) {
 			rc = EXIT_MD_IO_FAIL;
 			goto out;
 		}
 	}
 
 	memset(&signal_value, 0, sizeof(signal_value));
 
 	while (1) {
 		struct sector_header_s	*s_header_retry = NULL;
 		struct sector_node_s	*s_node_retry = NULL;
 
 		t0 = time(NULL);
 		sleep(timeout_loop);
 
 		ppid = getppid();
 
 		if (ppid == 1) {
 			/* Our parent died unexpectedly. Triggering
 			 * self-fence. */
-			do_reset();
+			do_timeout_action();
 		}
 
 		/* These attempts are, by definition, somewhat racy. If
 		 * the device is wiped out or corrupted between here and
 		 * us reading our mbox, there is nothing we can do about
 		 * that. But at least we tried. */
 		s_header_retry = header_get(st);
 		if (!s_header_retry) {
 			cl_log(LOG_ERR, "No longer found a valid header on %s", diskname);
 			exit(EXIT_MD_IO_FAIL);
 		}
 		if (memcmp(s_header, s_header_retry, sizeof(*s_header)) != 0) {
 			cl_log(LOG_ERR, "Header on %s changed since start-up!", diskname);
 			exit(EXIT_MD_IO_FAIL);
 		}
 		free(s_header_retry);
 
 		s_node_retry = sector_alloc();
 		if (slot_read(st, mbox, s_node_retry) < 0) {
 			cl_log(LOG_ERR, "slot read failed in servant.");
 			exit(EXIT_MD_IO_FAIL);
 		}
 		if (memcmp(s_node, s_node_retry, sizeof(*s_node)) != 0) {
 			cl_log(LOG_ERR, "Node entry on %s changed since start-up!", diskname);
 			exit(EXIT_MD_IO_FAIL);
 		}
 		free(s_node_retry);
 
 		if (mbox_read(st, mbox, s_mbox) < 0) {
 			cl_log(LOG_ERR, "mbox read failed in servant.");
 			exit(EXIT_MD_IO_FAIL);
 		}
 
 		if (s_mbox->cmd > 0) {
 			cl_log(LOG_NOTICE,
 			       "Received command %s from %s on disk %s",
 			       char2cmd(s_mbox->cmd), s_mbox->from, diskname);
 
 			switch (s_mbox->cmd) {
 			case SBD_MSG_TEST:
 				memset(s_mbox, 0, sizeof(*s_mbox));
 				mbox_write(st, mbox, s_mbox);
 				sigqueue(ppid, SIG_TEST, signal_value);
 				break;
 			case SBD_MSG_RESET:
 				exit(EXIT_MD_REQUEST_RESET);
 			case SBD_MSG_OFF:
 				exit(EXIT_MD_REQUEST_SHUTOFF);
 			case SBD_MSG_EXIT:
 				sigqueue(ppid, SIG_EXITREQ, signal_value);
 				break;
 			case SBD_MSG_CRASHDUMP:
 				exit(EXIT_MD_REQUEST_CRASHDUMP);
 			default:
 				/* FIXME:
 				   An "unknown" message might result
 				   from a partial write.
 				   log it and clear the slot.
 				 */
 				cl_log(LOG_ERR, "Unknown message on disk %s",
 				       diskname);
 				memset(s_mbox, 0, sizeof(*s_mbox));
 				mbox_write(st, mbox, s_mbox);
 				break;
 			}
 		}
 		sigqueue(ppid, SIG_LIVENESS, signal_value);
 
 		t1 = time(NULL);
 		latency = t1 - t0;
 		if (timeout_watchdog_warn && (latency > timeout_watchdog_warn)) {
 			cl_log(LOG_WARNING,
 			       "Latency: %d exceeded threshold %d on disk %s",
 			       (int)latency, (int)timeout_watchdog_warn,
 			       diskname);
 		} else if (debug) {
 			DBGLOG(LOG_DEBUG, "Latency: %d on disk %s", (int)latency,
 			       diskname);
 		}
 	}
  out:
 	free(s_mbox);
 	close_device(st);
 	exit(rc);
 }
 
 
diff --git a/src/sbd.h b/src/sbd.h
index 0f8847a..386c85c 100644
--- a/src/sbd.h
+++ b/src/sbd.h
@@ -1,203 +1,206 @@
 /*
  * Copyright (C) 2013 Lars Marowsky-Bree <lmb@suse.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 software 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 <arpa/inet.h>
 #include <asm/unistd.h>
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <libaio.h>
 #include <linux/fs.h>
 #include <linux/types.h>
 #include <linux/watchdog.h>
 #include <malloc.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdbool.h>
 #include <string.h>
 #include <sys/ioctl.h>
 #include <sys/ptrace.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/utsname.h>
 #include <sys/wait.h>
 #include <syslog.h>
 #include <time.h>
 #include <unistd.h>
 #include <uuid/uuid.h>
 #include <qb/qblog.h>
 #include <pacemaker/crm_config.h>
 #include <config.h>
 
 /* signals reserved for multi-disk sbd */
 #define SIG_LIVENESS (SIGRTMIN + 1)	/* report liveness of the disk */
 #define SIG_EXITREQ  (SIGRTMIN + 2)	/* exit request to inquisitor */
 #define SIG_TEST     (SIGRTMIN + 3)	/* trigger self test */
 #define SIG_RESTART  (SIGRTMIN + 4)	/* trigger restart of all failed disk */
 #define SIG_PCMK_UNHEALTHY  (SIGRTMIN + 5)
 /* FIXME: should add dynamic check of SIG_XX >= SIGRTMAX */
 
 /* exit status for disk-servant */
 #define EXIT_MD_IO_FAIL             20
 #define EXIT_MD_REQUEST_RESET       21
 #define EXIT_MD_REQUEST_SHUTOFF     22
 #define EXIT_MD_REQUEST_CRASHDUMP   23
 
 #define HOG_CHAR	0xff
 #define SECTOR_NAME_MAX 63
 
 /* Sector data types */
 struct sector_header_s {
 	char	magic[8];
 	unsigned char	version;
 	unsigned char	slots;
 	/* Caveat: stored in network byte-order */
 	uint32_t	sector_size;
 	uint32_t	timeout_watchdog;
 	uint32_t	timeout_allocate;
 	uint32_t	timeout_loop;
 	uint32_t	timeout_msgwait;
 	/* Minor version for extensions to the core data set:
 	 * compatible and optional values. */
 	unsigned char	minor_version;
 	uuid_t		uuid; /* 16 bytes */
 };
 
 struct sector_mbox_s {
 	signed char	cmd;
 	char		from[SECTOR_NAME_MAX+1];
 };
 
 struct sector_node_s {
 	/* slots will be created with in_use == 0 */
 	char	in_use;
 	char 	name[SECTOR_NAME_MAX+1];
 };
 
 struct servants_list_item {
 	const char* devname;
 	pid_t pid;
 	int restarts;
 	int restart_blocked;
 	int outdated;
 	int first_start;
 	struct timespec t_last, t_started;
 	struct servants_list_item *next;
 };
 
 struct sbd_context {
 	int	devfd;
 	io_context_t	ioctx;
 	struct iocb	io;
 };
 
 enum pcmk_health 
 {
     pcmk_health_unknown,
     pcmk_health_pending,
     pcmk_health_transient,
     pcmk_health_unclean,
     pcmk_health_shutdown,
     pcmk_health_online,
     pcmk_health_noquorum,
 };
 
 void usage(void);
 int watchdog_init_interval(void);
 int watchdog_tickle(void);
 int watchdog_init(void);
 void sysrq_init(void);
 void watchdog_close(bool disarm);
 int watchdog_info(void);
 int watchdog_test(void);
 void sysrq_trigger(char t);
 void do_crashdump(void);
 void do_reset(void);
 void do_off(void);
+void do_timeout_action(void);
 pid_t make_daemon(void);
 void maximize_priority(void);
 void sbd_get_uname(void);
 void sbd_set_format_string(int method, const char *daemon);
 void notify_parent(void);
 
 /* Tunable defaults: */
 extern unsigned long    timeout_watchdog;
 extern unsigned long    timeout_watchdog_warn;
 extern unsigned long    timeout_watchdog_crashdump;
 extern int      timeout_allocate;
 extern int      timeout_loop;
 extern int      timeout_msgwait;
 extern int      timeout_io;
 extern int      timeout_startup;
 extern int  watchdog_use;
 extern int  watchdog_set_timeout;
 extern int  skip_rt;
 extern int  debug;
 extern int  debug_mode;
 extern char *watchdogdev;
 extern bool watchdogdev_is_default;
 extern char*  local_uname;
+extern bool do_flush;
+extern char timeout_sysrq_char;
 
 /* Global, non-tunable variables: */
 extern int  sector_size;
 extern int  watchdogfd;
 extern const char* cmdname;
 
 typedef int (*functionp_t)(const char* devname, int mode, const void* argp);
 
 int assign_servant(const char* devname, functionp_t functionp, int mode, const void* argp);
 
 #if SUPPORT_SHARED_DISK
 void open_any_device(struct servants_list_item *servants);
 int init_devices(struct servants_list_item *servants);
 int allocate_slots(const char *name, struct servants_list_item *servants);
 int list_slots(struct servants_list_item *servants);
 int ping_via_slots(const char *name, struct servants_list_item *servants);
 int dump_headers(struct servants_list_item *servants);
 unsigned long get_first_msgwait(struct servants_list_item *servants);
 int messenger(const char *name, const char *msg, struct servants_list_item *servants);
 int servant(const char *diskname, int mode, const void* argp);
 #endif
 
 int servant_pcmk(const char *diskname, int mode, const void* argp);
 int servant_cluster(const char *diskname, int mode, const void* argp);
 
 struct servants_list_item *lookup_servant_by_dev(const char *devname);
 struct servants_list_item *lookup_servant_by_pid(pid_t pid);
 
 int init_set_proc_title(int argc, char *argv[], char *envp[]);
 void set_proc_title(const char *fmt,...);
 
 #define cl_log(level, fmt, args...) qb_log_from_external_source( __func__, __FILE__, fmt, level, __LINE__, 0, ##args)
 
 #  define cl_perror(fmt, args...) do {                                  \
 	const char *err = strerror(errno);				\
 	cl_log(LOG_ERR, fmt ": %s (%d)", ##args, err, errno);		\
     } while(0)
 
 #define DBGLOG(lvl, fmt, args...) do {           \
 	if (debug > 0) cl_log(lvl, fmt, ##args); \
 	} while(0)
 
 extern int servant_health;
 void set_servant_health(enum pcmk_health state, int level, char const *format, ...) __attribute__ ((__format__ (__printf__, 3, 4)));
 
 bool sbd_is_disk(struct servants_list_item *servant);
 bool sbd_is_pcmk(struct servants_list_item *servant);
 bool sbd_is_cluster(struct servants_list_item *servant);
diff --git a/src/sbd.sysconfig b/src/sbd.sysconfig
index c6d7c07..8f38426 100644
--- a/src/sbd.sysconfig
+++ b/src/sbd.sysconfig
@@ -1,78 +1,96 @@
 ## Type: string
 ## Default: ""
 #
 # SBD_DEVICE specifies the devices to use for exchanging sbd messages
 # and to monitor. If specifying more than one path, use ";" as
 # separator.
 #
 #SBD_DEVICE=""
 
 ## Type: yesno
 ## Default: yes
 #
 # Whether to enable the pacemaker integration.
 #
 SBD_PACEMAKER=yes
 
 ## Type: list(always,clean)
 ## Default: always
 #
 # Specify the start mode for sbd. Setting this to "clean" will only
 # allow sbd to start if it was not previously fenced. See the -S option
 # in the man page.
 #
 SBD_STARTMODE=always
 
 ## Type: yesno / integer
 ## Default: no
 #
 # Whether to delay after starting sbd on boot for "msgwait" seconds.
 # This may be necessary if your cluster nodes reboot so fast that the
 # other nodes are still waiting in the fence acknowledgement phase.
 # This is an occasional issue with virtual machines.
 #
 # This can also be enabled by being set to a specific delay value, in
 # seconds. Sometimes a longer delay than the default, "msgwait", is
 # needed, for example in the cases where it's considered to be safer to
 # wait longer than:
 # corosync token timeout + consensus timeout + pcmk_delay_max + msgwait
 #
 # Be aware that the special value "1" means "yes" rather than "1s".
 #
 # Consider that you might have to adapt the startup-timeout accordingly
 # if the default isn't sufficient. (TimeoutStartSec for systemd)
 #
 # This option may be ignored at a later point, once pacemaker handles
 # this case better.
 #
 SBD_DELAY_START=no
 
 ## Type: string
 ## Default: /dev/watchdog
 #
 # Watchdog device to use. If set to /dev/null, no watchdog device will
 # be used.
 #
 SBD_WATCHDOG_DEV=/dev/watchdog
 
 ## Type: integer
 ## Default: 5
 #
 # How long, in seconds, the watchdog will wait before panicing the
 # node if no-one tickles it.
 #
 # This depends mostly on your storage latency; the majority of devices
 # must be successfully read within this time, or else the node will
 # self-fence.
 #
 # If your sbd device(s) reside on a multipath setup or iSCSI, this
 # should be the time required to detect a path failure.
 #
 SBD_WATCHDOG_TIMEOUT=5
 
+## Type: string
+## Default: "flush,reboot"
+#
+# Actions to be executed when the watchers don't timely report to the sbd
+# master process or one of the watchers detects that the master process
+# has died.
+#
+# Set timeout-action to comma-separated combination of
+# noflush|flush plus reboot|crashdump|off.
+# If just one of both is given the other stays at the default.
+#
+# This doesn't affect actions like off, crashdump, reboot explicitly
+# triggered via message slots.
+# And it does as well not configure the action a watchdog would
+# trigger should it run off (there is no generic interface).
+#
+SBD_TIMEOUT_ACTION=flush,reboot
+
 ## Type: string
 ## Default: ""
 #
 # Additional options for starting sbd
 #
 SBD_OPTS=