diff --git a/.gitignore b/.gitignore
index a831bc1994f92ea3f2708f8cd68300f18bf1fd23..42a8d1a90b596d48bdb9832c9c6bcbfbe7a6ca06 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,6 +31,7 @@
 /ganeti-[0-9]*.[0-9]*.[0-9]*
 
 # daemons
+/daemons/daemon-util
 /daemons/ganeti-cleaner
 
 # devel
diff --git a/Makefile.am b/Makefile.am
index 20c7504129681adeaf4e2e28a5f6c703b30b3bf2..8f41f80e7becab96fdae8d7403a1304f11aac956 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -53,6 +53,7 @@ maintainer-clean-local:
 
 CLEANFILES = \
 	autotools/replace_vars.sed \
+	daemons/daemon-util \
 	daemons/ganeti-cleaner \
 	devel/upload \
 	doc/examples/bash_completion \
@@ -220,6 +221,9 @@ dist_tools_SCRIPTS = \
 	tools/cfgupgrade \
 	tools/lvmstrap
 
+pkglib_SCRIPTS = \
+	daemons/daemon-util
+
 EXTRA_DIST = \
 	NEWS \
 	pylintrc \
@@ -227,6 +231,7 @@ EXTRA_DIST = \
 	autotools/check-python-code \
 	autotools/docbook-wrapper \
 	$(RUN_IN_TEMPDIR) \
+	daemons/daemon-util.in \
 	daemons/ganeti-cleaner.in \
 	devel/upload.in \
 	$(docdot) \
@@ -342,7 +347,7 @@ devel/upload: devel/upload.in $(REPLACE_VARS_SED)
 	sed -f $(REPLACE_VARS_SED) < $< > $@
 	chmod u+x $@
 
-daemons/ganeti-cleaner: daemons/ganeti-cleaner.in \
+daemons/%: daemons/%.in \
 		$(REPLACE_VARS_SED)
 	sed -f $(REPLACE_VARS_SED) < $< > $@
 	chmod +x $@
@@ -418,6 +423,7 @@ lib/_autoconf.py: Makefile stamp-directories
 	  echo "LVM_STRIPECOUNT = $(LVM_STRIPECOUNT)"; \
 	  echo "TOOLSDIR = '$(toolsdir)'"; \
 	  echo "GNT_SCRIPTS = [$(foreach i,$(notdir $(gnt_scripts)),'$(i)',)]"; \
+	  echo "PKGLIBDIR = '$(pkglibdir)'"; \
 	} > $@
 
 $(REPLACE_VARS_SED): Makefile
diff --git a/NEWS b/NEWS
index 355c491992b08456def0d87edc214d7b18a15b00..3ab1d2f5f461036559b01ceb326c73dfa5cce1d9 100644
--- a/NEWS
+++ b/NEWS
@@ -72,6 +72,10 @@ Details
   (``rapi_users``)
 - Added option to specify maximum timeout on instance shutdown
 - Added ``--no-ssh-init`` option to ``gnt-cluster init``
+- Added new helper script to start and stop Ganeti daemons
+  (``daemon-util``), with the intent to reduce the work necessary to
+  adjust Ganeti for non-Debian distributions and to start/stop daemons
+  from one place
 - Added more unittests
 - Fixed critical bug in ganeti-masterd startup
 - Pass ``INSTANCE_REINSTALL`` variable to OS installation script when
diff --git a/daemons/daemon-util.in b/daemons/daemon-util.in
new file mode 100755
index 0000000000000000000000000000000000000000..12b4afbf8c7ca8f56f095207abb3a33535ed3640
--- /dev/null
+++ b/daemons/daemon-util.in
@@ -0,0 +1,124 @@
+#!/bin/bash
+#
+
+# Copyright (C) 2009 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+set -e
+
+defaults_file=@SYSCONFDIR@/default/ganeti
+
+NODED_ARGS=
+MASTERD_ARGS=
+CONFD_ARGS=
+RAPI_ARGS=
+
+# Read defaults file if it exists
+if [[ -s $defaults_file ]]; then
+  . $defaults_file
+fi
+
+_daemon_pidfile() {
+  echo "@LOCALSTATEDIR@/run/ganeti/$1.pid"
+}
+
+# Checks whether daemon is running
+check() {
+  if [[ "$#" -lt 1 ]]; then
+    echo 'Missing daemon name.' >&2
+    exit 1
+  fi
+
+  local name="$1"; shift
+
+  start-stop-daemon --stop --signal 0 --quiet \
+    --pidfile $(_daemon_pidfile $name)
+}
+
+# Starts a daemon
+start() {
+  if [[ "$#" -lt 1 ]]; then
+    echo 'Missing daemon name.' >&2
+    exit 1
+  fi
+
+  local name="$1"; shift
+
+  # Convert daemon name to uppercase after removing "ganeti-" prefix
+  local ucname=$(tr a-z A-Z <<< ${name#ganeti-})
+
+  # Read $<daemon>_ARGS and $EXTRA_<daemon>_ARGS
+  eval local args="\$${ucname}_ARGS \$EXTRA_${ucname}_ARGS"
+
+  start-stop-daemon --start --quiet --oknodo \
+    --pidfile $(_daemon_pidfile $name) \
+    --startas "@PREFIX@/sbin/$name" \
+    -- $args "$@"
+}
+
+# Stops a daemon
+stop() {
+  if [[ "$#" -lt 1 ]]; then
+    echo 'Missing daemon name.' >&2
+    exit 1
+  fi
+
+  local name="$1"; shift
+
+  start-stop-daemon --stop --quiet --oknodo --retry 30 \
+    --pidfile $(_daemon_pidfile $name)
+}
+
+# Starts a daemon if it's not yet running
+check_and_start() {
+  local name="$1"
+
+  if ! check $name; then
+    start $name
+  fi
+}
+
+# Starts the master role
+start_master() {
+  start ganeti-masterd
+  start ganeti-rapi
+}
+
+# Stops the master role
+stop_master() {
+  stop ganeti-rapi
+  stop ganeti-masterd
+}
+
+if [[ "$#" -lt 1 ]]; then
+  echo "Usage: $0 <action>" >&2
+  exit 1
+fi
+
+orig_action=$1; shift
+
+# Replace all dashes (-) with underlines (_)
+action=${orig_action//-/_}
+
+# Is it a known function?
+if ! declare -F "$action" >/dev/null 2>&1; then
+  echo "Unknown command: $orig_action" >&2
+  exit 1
+fi
+
+# Call handler function
+$action "$@"
diff --git a/daemons/ganeti-watcher b/daemons/ganeti-watcher
index e77db0c626251fda31ea6de58e18946b64185d65..c09e7a475f4031a915873dfd60221a35fd8c9456 100755
--- a/daemons/ganeti-watcher
+++ b/daemons/ganeti-watcher
@@ -78,28 +78,17 @@ def ShouldPause():
   return bool(utils.ReadWatcherPauseFile(constants.WATCHER_PAUSEFILE))
 
 
-def StartMaster():
-  """Try to start the master daemon.
+def EnsureDaemon(name):
+  """Check for and start daemon if not alive.
 
   """
-  result = utils.RunCmd(['ganeti-masterd'])
+  result = utils.RunCmd([constants.DAEMON_UTIL, "check-and-start", name])
   if result.failed:
-    logging.error("Can't start the master daemon: output '%s'", result.output)
-  return not result.failed
-
+    logging.error("Can't start daemon '%s', failure %s, output: %s",
+                  name, result.fail_reason, result.output)
+    return False
 
-def EnsureDaemon(daemon):
-  """Check for and start daemon if not alive.
-
-  """
-  pidfile = utils.DaemonPidFileName(daemon)
-  pid = utils.ReadPidFile(pidfile)
-  if pid == 0 or not utils.IsProcessAlive(pid): # no file or dead pid
-    logging.debug("Daemon '%s' not alive, trying to restart", daemon)
-    result = utils.RunCmd([daemon])
-    if not result:
-      logging.error("Can't start daemon '%s', failure %s, output: %s",
-                    daemon, result.fail_reason, result.output)
+  return True
 
 
 class WatcherState(object):
@@ -503,7 +492,7 @@ def main():
       except luxi.NoMasterError, err:
         logging.warning("Master seems to be down (%s), trying to restart",
                         str(err))
-        if not StartMaster():
+        if not EnsureDaemon(constants.MASTERD):
           logging.critical("Can't start the master, exiting")
           sys.exit(constants.EXIT_FAILURE)
         # else retry the connection
diff --git a/doc/examples/ganeti.initd.in b/doc/examples/ganeti.initd.in
index 75e41ce5dcc2eaa466808bae7e784570840f6ece..9897820602815b32b69618ac56355163d27baefb 100644
--- a/doc/examples/ganeti.initd.in
+++ b/doc/examples/ganeti.initd.in
@@ -14,21 +14,12 @@
 PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
 DESC="Ganeti cluster"
 
-GANETIRUNDIR="@LOCALSTATEDIR@/run/ganeti"
-
-GANETI_DEFAULTS_FILE="@SYSCONFDIR@/default/ganeti"
-
 NODED="ganeti-noded"
-NODED_ARGS=""
-
 MASTERD="ganeti-masterd"
-MASTERD_ARGS=""
-
 CONFD="ganeti-confd"
-CONFD_ARGS=""
-
 RAPI="ganeti-rapi"
-RAPI_ARGS=""
+
+DAEMON_UTIL=@PKGLIBDIR@/daemon-util
 
 SCRIPTNAME="@SYSCONFDIR@/init.d/ganeti"
 
@@ -36,10 +27,6 @@ test -f "@PREFIX@/sbin/$NODED" || exit 0
 
 . /lib/lsb/init-functions
 
-if [ -s $GANETI_DEFAULTS_FILE ]; then
-    . $GANETI_DEFAULTS_FILE
-fi
-
 check_config() {
     for fname in \
         "@LOCALSTATEDIR@/lib/ganeti/server.pem"
@@ -69,13 +56,9 @@ check_exitcode() {
 
 start_action() {
     # called as start_action daemon-name
-    local daemon="$1"; shift
+    local daemon="$1"
     log_action_begin_msg "$daemon"
-    start-stop-daemon --start --quiet \
-        --pidfile "${GANETIRUNDIR}/${daemon}.pid" \
-        --startas "@PREFIX@/sbin/$daemon" \
-        --oknodo \
-        -- "$@"
+    $DAEMON_UTIL start "$@"
     check_exitcode $?
 }
 
@@ -83,8 +66,7 @@ stop_action() {
     # called as stop_action daemon-name
     local daemon="$1"
     log_action_begin_msg "$daemon"
-    start-stop-daemon --stop --quiet --oknodo \
-        --retry 30 --pidfile "${GANETIRUNDIR}/${daemon}.pid"
+    $DAEMON_UTIL stop "$@"
     check_exitcode $?
 }
 
@@ -97,6 +79,19 @@ maybe_do() {
     fi
 }
 
+start_all() {
+    check_config
+    for i in $NODED $MASTERD $CONFD $RAPI; do \
+        maybe_do "$1" stop_action $i
+    done
+}
+
+stop_all() {
+    for i in $RAPI $CONFD $MASTERD $NODED; do \
+        maybe_do "$1" stop_action $i
+    done
+}
+
 if [ -n "$2" -a \
     "$2" != "$NODED" -a \
     "$2" != "$CONFD" -a \
@@ -109,29 +104,15 @@ fi
 case "$1" in
     start)
         log_daemon_msg "Starting $DESC" "$2"
-        check_config
-        maybe_do "$2" start_action $NODED $NODED_ARGS
-        maybe_do "$2" start_action $MASTERD $MASTERD_ARGS
-        maybe_do "$2" start_action $CONFD $CONFD_ARGS
-        maybe_do "$2" start_action $RAPI $RAPI_ARGS
+        start_all "$2"
         ;;
     stop)
         log_daemon_msg "Stopping $DESC" "$2"
-        maybe_do "$2" stop_action $RAPI
-        maybe_do "$2" stop_action $CONFD
-        maybe_do "$2" stop_action $MASTERD
-        maybe_do "$2" stop_action $NODED
+        stop_all "$2"
         ;;
     restart|force-reload)
-        maybe_do "$2" stop_action $RAPI
-        maybe_do "$2" stop_action $CONFD
-        maybe_do "$2" stop_action $MASTERD
-        maybe_do "$2" stop_action $NODED
-        check_config
-        maybe_do "$2" start_action $NODED $NODED_ARGS
-        maybe_do "$2" start_action $MASTERD $MASTERD_ARGS
-        maybe_do "$2" start_action $CONFD $CONFD_ARGS
-        maybe_do "$2" start_action $RAPI $RAPI_ARGS
+        stop_all "$2"
+        start_all "$2"
         ;;
     *)
         log_success_msg "Usage: $SCRIPTNAME {start|stop|force-reload|restart}"
diff --git a/lib/backend.py b/lib/backend.py
index 7562e5cf7cbfbaa1024d76f5d7c46997aa9d5392..3aac2500de60e0783c67136e78f65501f3047c3b 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -255,21 +255,20 @@ def StartMaster(start_daemons, no_voting):
 
   # and now start the master and rapi daemons
   if start_daemons:
-    daemons_params = {
-        'ganeti-masterd': [],
-        'ganeti-rapi': [],
-        }
     if no_voting:
-      daemons_params['ganeti-masterd'].append('--no-voting')
-      daemons_params['ganeti-masterd'].append('--yes-do-it')
-    for daemon in daemons_params:
-      cmd = [daemon]
-      cmd.extend(daemons_params[daemon])
-      result = utils.RunCmd(cmd)
-      if result.failed:
-        msg = "Can't start daemon %s: %s" % (daemon, result.output)
-        logging.error(msg)
-        err_msgs.append(msg)
+      masterd_args = "--no-voting --yes-do-it"
+    else:
+      masterd_args = ""
+
+    env = {
+      "EXTRA_MASTERD_ARGS": masterd_args,
+      }
+
+    result = utils.RunCmd([constants.DAEMON_UTIL, "start-master"], env=env)
+    if result.failed:
+      msg = "Can't start Ganeti master: %s" % result.output
+      logging.error(msg)
+      err_msgs.append(msg)
 
   if err_msgs:
     _Fail("; ".join(err_msgs))
@@ -301,9 +300,11 @@ def StopMaster(stop_daemons):
     # but otherwise ignore the failure
 
   if stop_daemons:
-    # stop/kill the rapi and the master daemon
-    for daemon in constants.RAPI, constants.MASTERD:
-      utils.KillProcess(utils.ReadPidFile(utils.DaemonPidFileName(daemon)))
+    result = utils.RunCmd([constants.DAEMON_UTIL, "stop-master"])
+    if result.failed:
+      logging.error("Could not stop Ganeti master, command %s had exitcode %s"
+                    " and error %s",
+                    result.cmd, result.exit_code, result.output)
 
 
 def AddNode(dsa, dsapub, rsa, rsapub, sshkey, sshpub):
@@ -385,10 +386,10 @@ def LeaveCluster(modify_ssh_setup):
   except:
     logging.exception("Error while removing cluster secrets")
 
-  confd_pid = utils.ReadPidFile(utils.DaemonPidFileName(constants.CONFD))
-
-  if confd_pid:
-    utils.KillProcess(confd_pid, timeout=2)
+  result = utils.RunCmd([constants.DAEMON_UTIL, "stop", constants.CONFD])
+  if result.failed:
+    logging.error("Command %s failed with exitcode %s and error %s",
+                  result.cmd, result.exit_code, result.output)
 
   # Raise a custom exception (handled in ganeti-noded)
   raise errors.QuitGanetiException(True, 'Shutdown scheduled')
@@ -2435,15 +2436,18 @@ def DemoteFromMC():
   master, myself = ssconf.GetMasterAndMyself()
   if master == myself:
     _Fail("ssconf status shows I'm the master node, will not demote")
-  pid_file = utils.DaemonPidFileName(constants.MASTERD)
-  if utils.IsProcessAlive(utils.ReadPidFile(pid_file)):
+
+  result = utils.RunCmd([constants.DAEMON_UTIL, "check", constants.MASTERD])
+  if not result.failed:
     _Fail("The master daemon is running, will not demote")
+
   try:
     if os.path.isfile(constants.CLUSTER_CONF_FILE):
       utils.CreateBackup(constants.CLUSTER_CONF_FILE)
   except EnvironmentError, err:
     if err.errno != errno.ENOENT:
       _Fail("Error while backing up cluster file: %s", err, exc=True)
+
   utils.RemoveFile(constants.CLUSTER_CONF_FILE)
 
 
diff --git a/lib/bootstrap.py b/lib/bootstrap.py
index 6d53c2ee5fa5b43a65d57c987600415b441024ef..5b277e9d8b2c20a618e9c2b630be9d0606a4779f 100644
--- a/lib/bootstrap.py
+++ b/lib/bootstrap.py
@@ -126,8 +126,7 @@ def _InitGanetiServerSetup(master_name):
   if not os.path.exists(constants.HMAC_CLUSTER_KEY):
     GenerateHmacKey(constants.HMAC_CLUSTER_KEY)
 
-  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
-
+  result = utils.RunCmd([constants.DAEMON_UTIL, "start", constants.NODED])
   if result.failed:
     raise errors.OpExecError("Could not start the node daemon, command %s"
                              " had exitcode %s and error %s" %
@@ -241,12 +240,6 @@ def InitCluster(cluster_name, mac_prefix,
                                (master_netdev,
                                 result.output.strip()), errors.ECODE_INVAL)
 
-  if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
-          os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
-    raise errors.OpPrereqError("Init.d script '%s' missing or not"
-                               " executable." % constants.NODE_INITD_SCRIPT,
-                               errors.ECODE_ENVIRON)
-
   dirs = [(constants.RUN_GANETI_DIR, constants.RUN_DIRS_MODE)]
   utils.EnsureDirs(dirs)
 
@@ -416,13 +409,13 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
                "cat > '%s' << '!EOF.' && \n"
                "%s!EOF.\n"
                "chmod 0400 %s %s %s && "
-               "%s restart" %
+               "%s start %s" %
                (constants.SSL_CERT_FILE, noded_cert,
                 constants.RAPI_CERT_FILE, rapi_cert,
                 constants.HMAC_CLUSTER_KEY, hmac_key,
                 constants.SSL_CERT_FILE, constants.RAPI_CERT_FILE,
                 constants.HMAC_CLUSTER_KEY,
-                constants.NODE_INITD_SCRIPT))
+                constants.DAEMON_UTIL, constants.NODED))
 
   result = sshrunner.Run(node, 'root', mycommand, batch=False,
                          ask_key=ssh_key_check,
diff --git a/lib/constants.py b/lib/constants.py
index 14186acf62c92d0c7952bda25b66c546a82f019a..bab6d8d11a59d4ab7628befa58f07ba398d393f4 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -104,6 +104,7 @@ INSTANCE_UPFILE = RUN_GANETI_DIR + "/instance-status"
 SSH_KNOWN_HOSTS_FILE = DATA_DIR + "/known_hosts"
 RAPI_USERS_FILE = DATA_DIR + "/rapi_users"
 QUEUE_DIR = DATA_DIR + "/queue"
+DAEMON_UTIL = _autoconf.PKGLIBDIR + "/daemon-util"
 ETC_HOSTS = "/etc/hosts"
 DEFAULT_FILE_STORAGE_DIR = _autoconf.FILE_STORAGE_DIR
 SYSCONFDIR = _autoconf.SYSCONFDIR
@@ -112,8 +113,6 @@ CONF_DIR = SYSCONFDIR + "/ganeti"
 
 MASTER_SOCKET = SOCKET_DIR + "/ganeti-master"
 
-NODE_INITD_SCRIPT = SYSCONFDIR + "/init.d/ganeti"
-
 NODED = "ganeti-noded"
 CONFD = "ganeti-confd"
 RAPI = "ganeti-rapi"