diff --git a/include/crm/common/internal.h b/include/crm/common/internal.h
index 076b9f60b8..064e983b6b 100644
--- a/include/crm/common/internal.h
+++ b/include/crm/common/internal.h
@@ -1,135 +1,131 @@
 /*
- * Copyright (C) 2015
- *     Andrew Beekhof <andrew@beekhof.net>
+ * Copyright 2015-2018 Andrew Beekhof <andrew@beekhof.net>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef CRM_COMMON_INTERNAL__H
 #define CRM_COMMON_INTERNAL__H
 
 #include <glib.h>       /* for gboolean */
 #include <dirent.h>     /* for struct dirent */
 #include <unistd.h>     /* for getpid() */
 #include <sys/types.h>  /* for uid_t and gid_t */
 
 #include <crm/common/logging.h>
 
 /* internal I/O utilities (from io.c) */
 
 char *generate_series_filename(const char *directory, const char *series, int sequence,
                                gboolean bzip);
 int get_last_sequence(const char *directory, const char *series);
 void write_last_sequence(const char *directory, const char *series, int sequence, int max);
 int crm_chown_last_sequence(const char *directory, const char *series, uid_t uid, gid_t gid);
 
 gboolean crm_is_writable(const char *dir, const char *file, const char *user, const char *group,
                          gboolean need_both);
 
 void crm_sync_directory(const char *name);
 
 char *crm_read_contents(const char *filename);
 int crm_write_sync(int fd, const char *contents);
 int crm_set_nonblocking(int fd);
 const char *crm_get_tmpdir(void);
 
 
 /* internal procfs utilities (from procfs.c) */
 
 int crm_procfs_process_info(struct dirent *entry, char *name, int *pid);
 int crm_procfs_pid_of(const char *name);
 unsigned int crm_procfs_num_cores(void);
 
 
 /* internal XML schema functions (from xml.c) */
 
 void crm_schema_init(void);
 void crm_schema_cleanup(void);
 
 
+/* internal functions related to process IDs (from pid.c) */
+
+int crm_pid_active(long pid, const char *daemon);
+long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon);
+long crm_read_pidfile(const char *filename);
+int crm_lock_pidfile(const char *filename, const char *name);
+
+
 /* internal generic string functions (from strings.c) */
 
 long long crm_int_helper(const char *text, char **end_text);
 guint crm_parse_ms(const char *text);
 bool crm_starts_with(const char *str, const char *prefix);
 gboolean crm_ends_with(const char *s, const char *match);
 gboolean crm_ends_with_ext(const char *s, const char *match);
 char *add_list_element(char *list, const char *value);
 bool crm_compress_string(const char *data, int length, int max, char **result,
                          unsigned int *result_len);
 gint crm_alpha_sort(gconstpointer a, gconstpointer b);
 
 static inline char *
 crm_concat(const char *prefix, const char *suffix, char join)
 {
     CRM_ASSERT(prefix && suffix);
     return crm_strdup_printf("%s%c%s", prefix, join, suffix);
 }
 
 static inline int
 crm_strlen_zero(const char *s)
 {
     return !s || *s == '\0';
 }
 
 static inline char *
 crm_getpid_s()
 {
     return crm_strdup_printf("%lu", (unsigned long) getpid());
 }
 
 /* convenience functions for failure-related node attributes */
 
 #define CRM_FAIL_COUNT_PREFIX   "fail-count"
 #define CRM_LAST_FAILURE_PREFIX "last-failure"
 
 /*!
  * \internal
  * \brief Generate a failure-related node attribute name for a resource
  *
  * \param[in] prefix       Start of attribute name
  * \param[in] rsc_id       Resource name
  * \param[in] op           Operation name
  * \param[in] interval_ms  Operation interval
  *
  * \return Newly allocated string with attribute name
  *
  * \note Failure attributes are named like PREFIX-RSC#OP_INTERVAL (for example,
  *       "fail-count-myrsc#monitor_30000"). The '#' is used because it is not
  *       a valid character in a resource ID, to reliably distinguish where the
  *       operation name begins. The '_' is used simply to be more comparable to
  *       action labels like "myrsc_monitor_30000".
  */
 static inline char *
 crm_fail_attr_name(const char *prefix, const char *rsc_id, const char *op,
                    guint interval_ms)
 {
     CRM_CHECK(prefix && rsc_id && op, return NULL);
     return crm_strdup_printf("%s-%s#%s_%u", prefix, rsc_id, op, interval_ms);
 }
 
 static inline char *
 crm_failcount_name(const char *rsc_id, const char *op, guint interval_ms)
 {
     return crm_fail_attr_name(CRM_FAIL_COUNT_PREFIX, rsc_id, op, interval_ms);
 }
 
 static inline char *
 crm_lastfailure_name(const char *rsc_id, const char *op, guint interval_ms)
 {
     return crm_fail_attr_name(CRM_LAST_FAILURE_PREFIX, rsc_id, op, interval_ms);
 }
 
 #endif /* CRM_COMMON_INTERNAL__H */
diff --git a/include/crm_internal.h b/include/crm_internal.h
index 4c6fb0d4a1..78d9479058 100644
--- a/include/crm_internal.h
+++ b/include/crm_internal.h
@@ -1,382 +1,365 @@
-/* crm_internal.h */
-
 /*
- * Copyright (C) 2006 - 2008
- *     Andrew Beekhof <andrew@beekhof.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
+ * Copyright 2006-2018 Andrew Beekhof <andrew@beekhof.net>
  *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef CRM_INTERNAL__H
 #  define CRM_INTERNAL__H
 
 #  include <config.h>
 #  include <portability.h>
 
 #  include <glib.h>
 #  include <stdbool.h>
 #  include <libxml/tree.h>
 
 #  include <crm/lrmd.h>
 #  include <crm/common/logging.h>
 #  include <crm/common/ipcs.h>
 #  include <crm/common/internal.h>
 
 /* Dynamic loading of libraries */
 void *find_library_function(void **handle, const char *lib, const char *fn, int fatal);
 void *convert_const_pointer(const void *ptr);
 
 /* For ACLs */
 char *uid2username(uid_t uid);
 void determine_request_user(const char *user, xmlNode * request, const char *field);
 const char *crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user);
 
 #  if ENABLE_ACL
 #    include <string.h>
 static inline gboolean
 is_privileged(const char *user)
 {
     if (user == NULL) {
         return FALSE;
     } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
         return TRUE;
     } else if (strcmp(user, "root") == 0) {
         return TRUE;
     }
     return FALSE;
 }
 #  endif
 
 /* CLI option processing*/
 #  ifdef HAVE_GETOPT_H
 #    include <getopt.h>
 #  else
 #    define no_argument 0
 #    define required_argument 1
 #  endif
 
 #  define pcmk_option_default	0x00000
 #  define pcmk_option_hidden	0x00001
 #  define pcmk_option_paragraph	0x00002
 #  define pcmk_option_example	0x00004
 
 struct crm_option {
     /* Fields from 'struct option' in getopt.h */
     /* name of long option */
     const char *name;
     /*
      * one of no_argument, required_argument, and optional_argument:
      * whether option takes an argument
      */
     int has_arg;
     /* if not NULL, set *flag to val when option found */
     int *flag;
     /* if flag not NULL, value to set *flag to; else return value */
     int val;
 
     /* Custom fields */
     const char *desc;
     long flags;
 };
 
 void crm_set_options(const char *short_options, const char *usage, struct crm_option *long_options,
                      const char *app_desc);
 int crm_get_option(int argc, char **argv, int *index);
 int crm_get_option_long(int argc, char **argv, int *index, const char **longname);
 crm_exit_t crm_help(char cmd, crm_exit_t exit_code);
 
 /* Cluster Option Processing */
 typedef struct pe_cluster_option_s {
     const char *name;
     const char *alt_name;
     const char *type;
     const char *values;
     const char *default_value;
 
      gboolean(*is_valid) (const char *);
 
     const char *description_short;
     const char *description_long;
 
 } pe_cluster_option;
 
 const char *cluster_option(GHashTable * options, gboolean(*validate) (const char *),
                            const char *name, const char *old_name, const char *def_value);
 
 const char *get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len,
                              const char *name);
 
 void config_metadata(const char *name, const char *version, const char *desc_short,
                      const char *desc_long, pe_cluster_option * option_list, int len);
 
 void verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len);
 gboolean check_time(const char *value);
 gboolean check_timer(const char *value);
 gboolean check_boolean(const char *value);
 gboolean check_number(const char *value);
 gboolean check_positive_number(const char *value);
 gboolean check_quorum(const char *value);
 gboolean check_script(const char *value);
 gboolean check_utilization(const char *value);
 long crm_get_sbd_timeout(void);
 long crm_auto_watchdog_timeout(void);
 gboolean check_sbd_timeout(const char *value);
 void crm_args_fini(void);
 
 /* char2score */
 extern int node_score_red;
 extern int node_score_green;
 extern int node_score_yellow;
 
 /* Assorted convenience functions */
-int crm_pid_active(long pid, const char *daemon);
 void crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile);
 
 /* from operations.c */
 char *generate_op_key(const char *rsc_id, const char *op_type, guint interval_ms);
 char *generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type);
 char *generate_transition_magic(const char *transition_key, int op_status, int op_rc);
 char *generate_transition_key(int action, int transition_id, int target_rc, const char *node);
 void filter_action_parameters(xmlNode *param_set, const char *version);
 xmlNode *create_operation_update(xmlNode *parent, lrmd_event_data_t *event,
                                  const char *caller_version, int target_rc,
                                  const char *node, const char *origin,
                                  int level);
 
 // printf-style format to create operation ID from resource, action, interval
 #define CRM_OP_FMT "%s_%s_%u"
 
 static inline long long
 crm_clear_bit(const char *function, int line, const char *target, long long word, long long bit)
 {
     long long rc = (word & ~bit);
 
     if (rc == word) {
         /* Unchanged */
     } else if (target) {
         crm_trace("Bit 0x%.8llx for %s cleared by %s:%d", bit, target, function, line);
     } else {
         crm_trace("Bit 0x%.8llx cleared by %s:%d", bit, function, line);
     }
 
     return rc;
 }
 
 static inline long long
 crm_set_bit(const char *function, int line, const char *target, long long word, long long bit)
 {
     long long rc = (word | bit);
 
     if (rc == word) {
         /* Unchanged */
     } else if (target) {
         crm_trace("Bit 0x%.8llx for %s set by %s:%d", bit, target, function, line);
     } else {
         crm_trace("Bit 0x%.8llx set by %s:%d", bit, function, line);
     }
 
     return rc;
 }
 
 #  define set_bit(word, bit) word = crm_set_bit(__FUNCTION__, __LINE__, NULL, word, bit)
 #  define clear_bit(word, bit) word = crm_clear_bit(__FUNCTION__, __LINE__, NULL, word, bit)
 
 char *generate_hash_key(const char *crm_msg_reference, const char *sys);
 
 /*! remote tcp/tls helper functions */
 typedef struct crm_remote_s crm_remote_t;
 
 int crm_remote_send(crm_remote_t * remote, xmlNode * msg);
 int crm_remote_ready(crm_remote_t * remote, int total_timeout /*ms */ );
 gboolean crm_remote_recv(crm_remote_t * remote, int total_timeout /*ms */ , int *disconnected);
 xmlNode *crm_remote_parse_buffer(crm_remote_t * remote);
 int crm_remote_tcp_connect(const char *host, int port);
 int crm_remote_tcp_connect_async(const char *host, int port, int timeout,       /*ms */
                                  int *timer_id, void *userdata, void (*callback) (void *userdata, int sock));
 int crm_remote_accept(int ssock);
 void crm_sockaddr2str(void *sa, char *s);
 
 #  ifdef HAVE_GNUTLS_GNUTLS_H
 /*!
  * \internal
  * \brief Initiate the client handshake after establishing the tcp socket.
  * \note This is a blocking function, it will block until the entire handshake
  *       is complete or until the timeout period is reached.
  * \retval 0 success
  * \retval negative, failure
  */
 int crm_initiate_client_tls_handshake(crm_remote_t * remote, int timeout_ms);
 
 /*!
  * \internal
  * \brief Create client or server session for anon DH encryption credentials
  * \param sock, the socket the session will use for transport
  * \param type, GNUTLS_SERVER or GNUTLS_CLIENT
  * \param credentials, gnutls_anon_server_credentials_t or gnutls_anon_client_credentials_t
  *
  * \retval gnutls_session_t * on success
  * \retval NULL on failure
  */
 void *crm_create_anon_tls_session(int sock, int type, void *credentials);
 
 /*!
  * \internal
  * \brief Create client or server session for PSK credentials
  * \param sock, the socket the session will use for transport
  * \param type, GNUTLS_SERVER or GNUTLS_CLIENT
  * \param credentials, gnutls_psk_server_credentials_t or gnutls_osk_client_credentials_t
  *
  * \retval gnutls_session_t * on success
  * \retval NULL on failure
  */
 void *create_psk_tls_session(int csock, int type, void *credentials);
 #  endif
 
 const char *daemon_option(const char *option);
 void set_daemon_option(const char *option, const char *value);
 gboolean daemon_option_enabled(const char *daemon, const char *option);
 void strip_text_nodes(xmlNode * xml);
 void pcmk_panic(const char *origin);
 void sysrq_init(void);
 pid_t pcmk_locate_sbd(void);
-long crm_pidfile_inuse(const char *filename, long mypid, const char *daemon);
-long crm_read_pidfile(const char *filename);
 
 #  define crm_config_err(fmt...) { crm_config_error = TRUE; crm_err(fmt); }
 #  define crm_config_warn(fmt...) { crm_config_warning = TRUE; crm_warn(fmt); }
 
 #  define F_ATTRD_KEY		"attr_key"
 #  define F_ATTRD_ATTRIBUTE	"attr_name"
 #  define F_ATTRD_REGEX 	"attr_regex"
 #  define F_ATTRD_TASK		"task"
 #  define F_ATTRD_VALUE		"attr_value"
 #  define F_ATTRD_SET		"attr_set"
 #  define F_ATTRD_IS_REMOTE	"attr_is_remote"
 #  define F_ATTRD_IS_PRIVATE     "attr_is_private"
 #  define F_ATTRD_SECTION	"attr_section"
 #  define F_ATTRD_DAMPEN	"attr_dampening"
 #  define F_ATTRD_HOST		"attr_host"
 #  define F_ATTRD_HOST_ID	"attr_host_id"
 #  define F_ATTRD_USER		"attr_user"
 #  define F_ATTRD_WRITER	"attr_writer"
 #  define F_ATTRD_VERSION	"attr_version"
 #  define F_ATTRD_RESOURCE          "attr_resource"
 #  define F_ATTRD_OPERATION         "attr_clear_operation"
 #  define F_ATTRD_INTERVAL          "attr_clear_interval"
 
 /* attrd operations */
 #  define ATTRD_OP_PEER_REMOVE   "peer-remove"
 #  define ATTRD_OP_UPDATE        "update"
 #  define ATTRD_OP_UPDATE_BOTH   "update-both"
 #  define ATTRD_OP_UPDATE_DELAY  "update-delay"
 #  define ATTRD_OP_QUERY         "query"
 #  define ATTRD_OP_REFRESH       "refresh"
 #  define ATTRD_OP_FLUSH         "flush"
 #  define ATTRD_OP_SYNC          "sync"
 #  define ATTRD_OP_SYNC_RESPONSE "sync-response"
 #  define ATTRD_OP_CLEAR_FAILURE "clear-failure"
 
 #  define PCMK_ENV_PHYSICAL_HOST "physical_host"
 
 
 #  if SUPPORT_COROSYNC
 #    include <qb/qbipc_common.h>
 #    include <corosync/corotypes.h>
 typedef struct qb_ipc_request_header cs_ipc_header_request_t;
 typedef struct qb_ipc_response_header cs_ipc_header_response_t;
 #  else
 typedef struct {
     int size __attribute__ ((aligned(8)));
     int id __attribute__ ((aligned(8)));
 } __attribute__ ((aligned(8))) cs_ipc_header_request_t;
 
 typedef struct {
     int size __attribute__ ((aligned(8)));
     int id __attribute__ ((aligned(8)));
     int error __attribute__ ((aligned(8)));
 } __attribute__ ((aligned(8))) cs_ipc_header_response_t;
 
 #  endif
 
 void
 attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb);
 void
 stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb);
 
 qb_ipcs_service_t *
 crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb);
 
 void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro,
         qb_ipcs_service_t **ipcs_rw,
         qb_ipcs_service_t **ipcs_shm,
         struct qb_ipcs_service_handlers *ro_cb,
         struct qb_ipcs_service_handlers *rw_cb);
 
 void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
         qb_ipcs_service_t *ipcs_rw,
         qb_ipcs_service_t *ipcs_shm);
 
 static inline void *realloc_safe(void *ptr, size_t size)
 {
     void *ret = realloc(ptr, size);
 
     if (ret == NULL) {
         free(ptr); /* make coverity happy */
         abort();
     }
 
     return ret;
 }
 
 const char *crm_xml_add_last_written(xmlNode *xml_node);
 void crm_xml_dump(xmlNode * data, int options, char **buffer, int *offset, int *max, int depth);
 void crm_buffer_add_char(char **buffer, int *offset, int *max, char c);
 
 gboolean crm_digest_verify(xmlNode *input, const char *expected);
 
 /* cross-platform compatibility functions */
 char *crm_compat_realpath(const char *path);
 
 /* IPC Proxy Backend Shared Functions */
 typedef struct remote_proxy_s {
     char *node_name;
     char *session_id;
 
     gboolean is_local;
 
     crm_ipc_t *ipc;
     mainloop_io_t *source;
     uint32_t last_request_id;
     lrmd_t *lrm;
 
 } remote_proxy_t;
 
 remote_proxy_t *remote_proxy_new(
     lrmd_t *lrmd, struct ipc_client_callbacks *proxy_callbacks,
     const char *node_name, const char *session_id, const char *channel);
 
 int  remote_proxy_check(lrmd_t *lrmd, GHashTable *hash);
 void remote_proxy_cb(lrmd_t *lrmd, const char *node_name, xmlNode *msg);
 void remote_proxy_ack_shutdown(lrmd_t *lrmd);
 void remote_proxy_nack_shutdown(lrmd_t *lrmd);
 
 int  remote_proxy_dispatch(const char *buffer, ssize_t length, gpointer userdata);
 void remote_proxy_disconnected(gpointer data);
 void remote_proxy_free(gpointer data);
 
 void remote_proxy_relay_event(remote_proxy_t *proxy, xmlNode *msg);
 void remote_proxy_relay_response(remote_proxy_t *proxy, xmlNode *msg, int msg_id);
 
 #endif                          /* CRM_INTERNAL__H */
diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am
index 1fce0b927b..7546fe9fc1 100644
--- a/lib/common/Makefile.am
+++ b/lib/common/Makefile.am
@@ -1,50 +1,39 @@
 #
-# Copyright (C) 2004 Andrew Beekhof
+# Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
 #
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-# 
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+# This source code is licensed under the GNU General Public License version 2
+# or later (GPLv2+) WITHOUT ANY WARRANTY.
 #
 include $(top_srcdir)/Makefile.common
 
 AM_CPPFLAGS		+= -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu
 
 ## libraries
 lib_LTLIBRARIES	= libcrmcommon.la
 
 # Can't use -Wcast-qual here because glib insists on pretending things are const  
 # when they're not and thus we need the crm_element_value_const() hack
 
 # s390 needs -fPIC 
 # s390-suse-linux/bin/ld: .libs/ipc.o: relocation R_390_PC32DBL against `__stack_chk_fail@@GLIBC_2.4' can not be used when making a shared object; recompile with -fPIC
 
 CFLAGS		= $(CFLAGS_COPY:-Wcast-qual=) -fPIC
 
 libcrmcommon_la_LDFLAGS	= -version-info 13:0:0
 
 libcrmcommon_la_CFLAGS	= $(CFLAGS_HARDENED_LIB)
 libcrmcommon_la_LDFLAGS	+= $(LDFLAGS_HARDENED_LIB)
 
 libcrmcommon_la_LIBADD	= @LIBADD_DL@ $(GNUTLSLIBS)
 
 libcrmcommon_la_SOURCES	= compat.c digest.c ipc.c io.c procfs.c utils.c xml.c	\
 			  iso8601.c remote.c mainloop.c logging.c watchdog.c	\
 			  schemas.c strings.c xpath.c attrd_client.c alerts.c	\
-			  operations.c results.c
+			  operations.c pid.c results.c
 if BUILD_CIBSECRETS
 libcrmcommon_la_SOURCES	+= cib_secrets.c
 endif
 libcrmcommon_la_SOURCES	+= $(top_builddir)/lib/gnu/md5.c
 
 clean-generic:
 	rm -f *.log *.debug *.xml *~
diff --git a/lib/common/pid.c b/lib/common/pid.c
new file mode 100644
index 0000000000..803799e640
--- /dev/null
+++ b/lib/common/pid.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#ifndef _GNU_SOURCE
+#  define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <crm/crm.h>
+
+int
+crm_pid_active(long pid, const char *daemon)
+{
+    static int have_proc_pid = 0;
+
+    if (have_proc_pid == 0) {
+        char proc_path[PATH_MAX], exe_path[PATH_MAX];
+
+        // Make sure pid hasn't been reused by another process
+        snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe",
+                 (long unsigned int)getpid());
+
+        have_proc_pid = 1;
+        if (readlink(proc_path, exe_path, PATH_MAX - 1) < 0) {
+            have_proc_pid = -1;
+        }
+    }
+
+    if (pid <= 0) {
+        return -1;
+
+    } else if ((kill(pid, 0) < 0) && (errno == ESRCH)) {
+        return 0;
+
+    } else if ((daemon == NULL) || (have_proc_pid == -1)) {
+        return 1;
+
+    } else {
+        int rc = 0;
+        char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
+
+        // Make sure pid hasn't been reused by another process
+        snprintf(proc_path, sizeof(proc_path), "/proc/%ld/exe", pid);
+
+        rc = readlink(proc_path, exe_path, PATH_MAX - 1);
+        if ((rc < 0) && (errno == EACCES)) {
+            crm_perror(LOG_INFO, "Could not read from %s", proc_path);
+            return 1;
+        } else if (rc < 0) {
+            crm_perror(LOG_ERR, "Could not read from %s", proc_path);
+            return 0;
+        }
+
+        exe_path[rc] = 0;
+
+        if (daemon[0] != '/') {
+            rc = snprintf(myexe_path, sizeof(proc_path), CRM_DAEMON_DIR"/%s",
+                          daemon);
+            myexe_path[rc] = 0;
+        } else {
+            rc = snprintf(myexe_path, sizeof(proc_path), "%s", daemon);
+            myexe_path[rc] = 0;
+        }
+
+        if (strcmp(exe_path, myexe_path) == 0) {
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+#define	LOCKSTRLEN	11
+
+long
+crm_read_pidfile(const char *filename)
+{
+    int fd;
+    struct stat sbuf;
+    long pid = -ENOENT;
+    char buf[LOCKSTRLEN + 1];
+
+    fd = open(filename, O_RDONLY);
+    if (fd < 0) {
+        goto bail;
+    }
+
+    if ((fstat(fd, &sbuf) >= 0) && (sbuf.st_size < LOCKSTRLEN)) {
+        sleep(2);           /* if someone was about to create one,
+                             * give'm a sec to do so
+                             */
+    }
+
+    if (read(fd, buf, sizeof(buf)) < 1) {
+        goto bail;
+    }
+
+    if (sscanf(buf, "%ld", &pid) > 0) {
+        if (pid <= 0) {
+            pid = -ESRCH;
+        } else {
+            crm_trace("Got pid %lu from %s\n", pid, filename);
+        }
+    }
+
+  bail:
+    if (fd >= 0) {
+        close(fd);
+    }
+    return pid;
+}
+
+long
+crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
+{
+    long pid = crm_read_pidfile(filename);
+
+    if (pid < 2) {
+        // Invalid pid
+        pid = -ENOENT;
+        unlink(filename);
+
+    } else if (mypid && (pid == mypid)) {
+        // In use by us
+        pid = pcmk_ok;
+
+    } else if (crm_pid_active(pid, daemon) == FALSE) {
+        // Contains a stale value
+        unlink(filename);
+        pid = -ENOENT;
+
+    } else if (mypid && (pid != mypid)) {
+        // Locked by existing process
+        pid = -EEXIST;
+    }
+
+    return pid;
+}
+
+int
+crm_lock_pidfile(const char *filename, const char *name)
+{
+    long mypid = 0;
+    int fd = 0;
+    int rc = 0;
+    char buf[LOCKSTRLEN + 2];
+
+    mypid = (unsigned long) getpid();
+
+    rc = crm_pidfile_inuse(filename, 0, name);
+    if (rc == -ENOENT) {
+        // Exists, but the process is not active
+
+    } else if (rc != pcmk_ok) {
+        // Locked by existing process
+        return rc;
+    }
+
+    fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644);
+    if (fd < 0) {
+        return -errno;
+    }
+
+    snprintf(buf, sizeof(buf), "%*ld\n", LOCKSTRLEN - 1, mypid);
+    rc = write(fd, buf, LOCKSTRLEN);
+    close(fd);
+
+    if (rc != LOCKSTRLEN) {
+        crm_perror(LOG_ERR, "Incomplete write to %s", filename);
+        return -errno;
+    }
+
+    return crm_pidfile_inuse(filename, mypid, name);
+}
diff --git a/lib/common/utils.c b/lib/common/utils.c
index 582838e46b..07ba1b7d1d 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,1515 +1,1353 @@
 /*
  * Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 #include <dlfcn.h>
 
 #ifndef _GNU_SOURCE
 #  define _GNU_SOURCE
 #endif
 
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
 #include <sys/utsname.h>
 
 #include <stdio.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdlib.h>
 #include <limits.h>
 #include <pwd.h>
 #include <time.h>
 #include <libgen.h>
 #include <signal.h>
 
 #include <qb/qbdefs.h>
 
 #include <crm/crm.h>
 #include <crm/services.h>
 #include <crm/msg_xml.h>
 #include <crm/cib/internal.h>
 #include <crm/common/xml.h>
 #include <crm/common/util.h>
 #include <crm/common/ipc.h>
 #include <crm/common/iso8601.h>
 #include <crm/common/mainloop.h>
 #include <libxml2/libxml/relaxng.h>
 
 #ifndef MAXLINE
 #  define MAXLINE 512
 #endif
 
 #ifdef HAVE_GETOPT_H
 #  include <getopt.h>
 #endif
 
 #ifndef PW_BUFFER_LEN
 #  define PW_BUFFER_LEN		500
 #endif
 
 CRM_TRACE_INIT_DATA(common);
 
 gboolean crm_config_error = FALSE;
 gboolean crm_config_warning = FALSE;
 char *crm_system_name = NULL;
 
 int node_score_red = 0;
 int node_score_green = 0;
 int node_score_yellow = 0;
 
 static struct crm_option *crm_long_options = NULL;
 static const char *crm_app_description = NULL;
 static char *crm_short_options = NULL;
 static const char *crm_app_usage = NULL;
 
 gboolean
 check_time(const char *value)
 {
     if (crm_get_msec(value) < 5000) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_timer(const char *value)
 {
     if (crm_get_msec(value) < 0) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_boolean(const char *value)
 {
     int tmp = FALSE;
 
     if (crm_str_to_boolean(value, &tmp) != 1) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_number(const char *value)
 {
     errno = 0;
     if (value == NULL) {
         return FALSE;
 
     } else if (safe_str_eq(value, CRM_MINUS_INFINITY_S)) {
 
     } else if (safe_str_eq(value, CRM_INFINITY_S)) {
 
     } else {
         crm_int_helper(value, NULL);
     }
 
     if (errno != 0) {
         return FALSE;
     }
     return TRUE;
 }
 
 gboolean
 check_positive_number(const char* value)
 {
     if (safe_str_eq(value, CRM_INFINITY_S) || (crm_int_helper(value, NULL))) {
         return TRUE;
     }
     return FALSE;
 }
 
 gboolean
 check_quorum(const char *value)
 {
     if (safe_str_eq(value, "stop")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "freeze")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "ignore")) {
         return TRUE;
 
     } else if (safe_str_eq(value, "suicide")) {
         return TRUE;
     }
     return FALSE;
 }
 
 gboolean
 check_script(const char *value)
 {
     struct stat st;
 
     if(safe_str_eq(value, "/dev/null")) {
         return TRUE;
     }
 
     if(stat(value, &st) != 0) {
         crm_err("Script %s does not exist", value);
         return FALSE;
     }
 
     if(S_ISREG(st.st_mode) == 0) {
         crm_err("Script %s is not a regular file", value);
         return FALSE;
     }
 
     if( (st.st_mode & (S_IXUSR | S_IXGRP )) == 0) {
         crm_err("Script %s is not executable", value);
         return FALSE;
     }
 
     return TRUE;
 }
 
 gboolean
 check_utilization(const char *value)
 {
     char *end = NULL;
     long number = strtol(value, &end, 10);
 
     if(end && end[0] != '%') {
         return FALSE;
     } else if(number < 0) {
         return FALSE;
     }
 
     return TRUE;
 }
 
 void
 crm_args_fini()
 {
     free(crm_short_options);
     crm_short_options = NULL;
 }
 
 int
 char2score(const char *score)
 {
     int score_f = 0;
 
     if (score == NULL) {
 
     } else if (safe_str_eq(score, CRM_MINUS_INFINITY_S)) {
         score_f = -CRM_SCORE_INFINITY;
 
     } else if (safe_str_eq(score, CRM_INFINITY_S)) {
         score_f = CRM_SCORE_INFINITY;
 
     } else if (safe_str_eq(score, CRM_PLUS_INFINITY_S)) {
         score_f = CRM_SCORE_INFINITY;
 
     } else if (safe_str_eq(score, "red")) {
         score_f = node_score_red;
 
     } else if (safe_str_eq(score, "yellow")) {
         score_f = node_score_yellow;
 
     } else if (safe_str_eq(score, "green")) {
         score_f = node_score_green;
 
     } else {
         score_f = crm_parse_int(score, NULL);
         if (score_f > 0 && score_f > CRM_SCORE_INFINITY) {
             score_f = CRM_SCORE_INFINITY;
 
         } else if (score_f < 0 && score_f < -CRM_SCORE_INFINITY) {
             score_f = -CRM_SCORE_INFINITY;
         }
     }
 
     return score_f;
 }
 
 char *
 score2char_stack(int score, char *buf, size_t len)
 {
     if (score >= CRM_SCORE_INFINITY) {
         strncpy(buf, CRM_INFINITY_S, 9);
     } else if (score <= -CRM_SCORE_INFINITY) {
         strncpy(buf, CRM_MINUS_INFINITY_S , 10);
     } else {
         return crm_itoa_stack(score, buf, len);
     }
 
     return buf;
 }
 
 char *
 score2char(int score)
 {
     if (score >= CRM_SCORE_INFINITY) {
         return strdup(CRM_INFINITY_S);
 
     } else if (score <= -CRM_SCORE_INFINITY) {
         return strdup(CRM_MINUS_INFINITY_S);
     }
     return crm_itoa(score);
 }
 
 const char *
 cluster_option(GHashTable * options, gboolean(*validate) (const char *),
                const char *name, const char *old_name, const char *def_value)
 {
     const char *value = NULL;
     char *new_value = NULL;
 
     CRM_ASSERT(name != NULL);
 
     if (options) {
         value = g_hash_table_lookup(options, name);
 
         if ((value == NULL) && old_name) {
             value = g_hash_table_lookup(options, old_name);
             if (value != NULL) {
                 crm_config_warn("Support for legacy name '%s' for cluster option '%s'"
                                 " is deprecated and will be removed in a future release",
                                 old_name, name);
 
                 // Inserting copy with current name ensures we only warn once
                 new_value = strdup(value);
                 g_hash_table_insert(options, strdup(name), new_value);
                 value = new_value;
             }
         }
 
         if (value && validate && (validate(value) == FALSE)) {
             crm_config_err("Resetting cluster option '%s' to default: value '%s' is invalid",
                            name, value);
             value = NULL;
         }
 
         if (value) {
             return value;
         }
     }
 
     // No value found, use default
     value = def_value;
 
     if (value == NULL) {
         crm_trace("No value or default provided for cluster option '%s'",
                   name);
         return NULL;
     }
 
     if (validate) {
         CRM_CHECK(validate(value) != FALSE,
                   crm_err("Bug: default value for cluster option '%s' is invalid", name);
                   return NULL);
     }
 
     crm_trace("Using default value '%s' for cluster option '%s'",
               value, name);
     if (options) {
         new_value = strdup(value);
         g_hash_table_insert(options, strdup(name), new_value);
         value = new_value;
     }
     return value;
 }
 
 const char *
 get_cluster_pref(GHashTable * options, pe_cluster_option * option_list, int len, const char *name)
 {
     const char *value = NULL;
 
     for (int lpc = 0; lpc < len; lpc++) {
         if (safe_str_eq(name, option_list[lpc].name)) {
             value = cluster_option(options,
                                    option_list[lpc].is_valid,
                                    option_list[lpc].name,
                                    option_list[lpc].alt_name,
                                    option_list[lpc].default_value);
             return value;
         }
     }
     CRM_CHECK(FALSE, crm_err("Bug: looking for unknown option '%s'", name));
     return NULL;
 }
 
 void
 config_metadata(const char *name, const char *version, const char *desc_short,
                 const char *desc_long, pe_cluster_option * option_list, int len)
 {
     int lpc = 0;
 
     fprintf(stdout, "<?xml version=\"1.0\"?>"
             "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
             "<resource-agent name=\"%s\">\n"
             "  <version>%s</version>\n"
             "  <longdesc lang=\"en\">%s</longdesc>\n"
             "  <shortdesc lang=\"en\">%s</shortdesc>\n"
             "  <parameters>\n", name, version, desc_long, desc_short);
 
     for (lpc = 0; lpc < len; lpc++) {
         if (option_list[lpc].description_long == NULL && option_list[lpc].description_short == NULL) {
             continue;
         }
         fprintf(stdout, "    <parameter name=\"%s\" unique=\"0\">\n"
                 "      <shortdesc lang=\"en\">%s</shortdesc>\n"
                 "      <content type=\"%s\" default=\"%s\"/>\n"
                 "      <longdesc lang=\"en\">%s%s%s</longdesc>\n"
                 "    </parameter>\n",
                 option_list[lpc].name,
                 option_list[lpc].description_short,
                 option_list[lpc].type,
                 option_list[lpc].default_value,
                 option_list[lpc].description_long ? option_list[lpc].
                 description_long : option_list[lpc].description_short,
                 option_list[lpc].values ? "  Allowed values: " : "",
                 option_list[lpc].values ? option_list[lpc].values : "");
     }
     fprintf(stdout, "  </parameters>\n</resource-agent>\n");
 }
 
 void
 verify_all_options(GHashTable * options, pe_cluster_option * option_list, int len)
 {
     int lpc = 0;
 
     for (lpc = 0; lpc < len; lpc++) {
         cluster_option(options,
                        option_list[lpc].is_valid,
                        option_list[lpc].name,
                        option_list[lpc].alt_name, option_list[lpc].default_value);
     }
 }
 
 char *
 generate_hash_key(const char *crm_msg_reference, const char *sys)
 {
     char *hash_key = crm_concat(sys ? sys : "none", crm_msg_reference, '_');
 
     crm_trace("created hash key: (%s)", hash_key);
     return hash_key;
 }
 
 
 int
 crm_user_lookup(const char *name, uid_t * uid, gid_t * gid)
 {
     int rc = pcmk_ok;
     char *buffer = NULL;
     struct passwd pwd;
     struct passwd *pwentry = NULL;
 
     buffer = calloc(1, PW_BUFFER_LEN);
     rc = getpwnam_r(name, &pwd, buffer, PW_BUFFER_LEN, &pwentry);
     if (pwentry) {
         if (uid) {
             *uid = pwentry->pw_uid;
         }
         if (gid) {
             *gid = pwentry->pw_gid;
         }
         crm_trace("User %s has uid=%d gid=%d", name, pwentry->pw_uid, pwentry->pw_gid);
 
     } else {
         rc = rc? -rc : -EINVAL;
         crm_info("User %s lookup: %s", name, pcmk_strerror(rc));
     }
 
     free(buffer);
     return rc;
 }
 
 static int
 crm_version_helper(const char *text, char **end_text)
 {
     int atoi_result = -1;
 
     CRM_ASSERT(end_text != NULL);
 
     errno = 0;
 
     if (text != NULL && text[0] != 0) {
         atoi_result = (int)strtol(text, end_text, 10);
 
         if (errno == EINVAL) {
             crm_err("Conversion of '%s' %c failed", text, text[0]);
             atoi_result = -1;
         }
     }
     return atoi_result;
 }
 
 /*
  * version1 < version2 : -1
  * version1 = version2 :  0
  * version1 > version2 :  1
  */
 int
 compare_version(const char *version1, const char *version2)
 {
     int rc = 0;
     int lpc = 0;
     char *ver1_copy = NULL, *ver2_copy = NULL;
     char *rest1 = NULL, *rest2 = NULL;
 
     if (version1 == NULL && version2 == NULL) {
         return 0;
     } else if (version1 == NULL) {
         return -1;
     } else if (version2 == NULL) {
         return 1;
     }
 
     ver1_copy = strdup(version1);
     ver2_copy = strdup(version2);
     rest1 = ver1_copy;
     rest2 = ver2_copy;
 
     while (1) {
         int digit1 = 0;
         int digit2 = 0;
 
         lpc++;
 
         if (rest1 == rest2) {
             break;
         }
 
         if (rest1 != NULL) {
             digit1 = crm_version_helper(rest1, &rest1);
         }
 
         if (rest2 != NULL) {
             digit2 = crm_version_helper(rest2, &rest2);
         }
 
         if (digit1 < digit2) {
             rc = -1;
             break;
 
         } else if (digit1 > digit2) {
             rc = 1;
             break;
         }
 
         if (rest1 != NULL && rest1[0] == '.') {
             rest1++;
         }
         if (rest1 != NULL && rest1[0] == 0) {
             rest1 = NULL;
         }
 
         if (rest2 != NULL && rest2[0] == '.') {
             rest2++;
         }
         if (rest2 != NULL && rest2[0] == 0) {
             rest2 = NULL;
         }
     }
 
     free(ver1_copy);
     free(ver2_copy);
 
     if (rc == 0) {
         crm_trace("%s == %s (%d)", version1, version2, lpc);
     } else if (rc < 0) {
         crm_trace("%s < %s (%d)", version1, version2, lpc);
     } else if (rc > 0) {
         crm_trace("%s > %s (%d)", version1, version2, lpc);
     }
 
     return rc;
 }
 
 gboolean do_stderr = FALSE;
 
 #ifndef NUMCHARS
 #  define	NUMCHARS	"0123456789."
 #endif
 
 #ifndef WHITESPACE
 #  define	WHITESPACE	" \t\n\r\f"
 #endif
 
 guint
 crm_parse_interval_spec(const char *input)
 {
     long long msec = 0;
 
     if (input == NULL) {
         return 0;
 
     } else if (input[0] != 'P') {
         long long tmp = crm_get_msec(input);
 
         if(tmp > 0) {
             msec = tmp;
         }
 
     } else {
         crm_time_t *period_s = crm_time_parse_duration(input);
 
         msec = 1000 * crm_time_get_seconds(period_s);
         crm_time_free(period_s);
     }
 
     return (msec <= 0)? 0 : ((msec >= G_MAXUINT)? G_MAXUINT : (guint) msec);
 }
 
 long long
 crm_get_msec(const char *input)
 {
     const char *cp = input;
     const char *units;
     long long multiplier = 1000;
     long long divisor = 1;
     long long msec = -1;
     char *end_text = NULL;
 
     /* double dret; */
 
     if (input == NULL) {
         return msec;
     }
 
     cp += strspn(cp, WHITESPACE);
     units = cp + strspn(cp, NUMCHARS);
     units += strspn(units, WHITESPACE);
 
     if (strchr(NUMCHARS, *cp) == NULL) {
         return msec;
     }
 
     if (strncasecmp(units, "ms", 2) == 0 || strncasecmp(units, "msec", 4) == 0) {
         multiplier = 1;
         divisor = 1;
     } else if (strncasecmp(units, "us", 2) == 0 || strncasecmp(units, "usec", 4) == 0) {
         multiplier = 1;
         divisor = 1000;
     } else if (strncasecmp(units, "s", 1) == 0 || strncasecmp(units, "sec", 3) == 0) {
         multiplier = 1000;
         divisor = 1;
     } else if (strncasecmp(units, "m", 1) == 0 || strncasecmp(units, "min", 3) == 0) {
         multiplier = 60 * 1000;
         divisor = 1;
     } else if (strncasecmp(units, "h", 1) == 0 || strncasecmp(units, "hr", 2) == 0) {
         multiplier = 60 * 60 * 1000;
         divisor = 1;
     } else if (*units != EOS && *units != '\n' && *units != '\r') {
         return msec;
     }
 
     msec = crm_int_helper(cp, &end_text);
     if (msec > LLONG_MAX/multiplier) {
         /* arithmetics overflow while multiplier/divisor mutually exclusive */
         return LLONG_MAX;
     }
     msec *= multiplier;
     msec /= divisor;
     /* dret += 0.5; */
     /* msec = (long long)dret; */
     return msec;
 }
 
 extern bool crm_is_daemon;
 
 /* coverity[+kill] */
 void
 crm_abort(const char *file, const char *function, int line,
           const char *assert_condition, gboolean do_core, gboolean do_fork)
 {
     int rc = 0;
     int pid = 0;
     int status = 0;
 
     /* Implied by the parent's error logging below */
     /* crm_write_blackbox(0); */
 
     if(crm_is_daemon == FALSE) {
         /* This is a command line tool - do not fork */
 
         /* crm_add_logfile(NULL);   * Record it to a file? */
         crm_enable_stderr(TRUE); /* Make sure stderr is enabled so we can tell the caller */
         do_fork = FALSE;         /* Just crash if needed */
     }
 
     if (do_core == FALSE) {
         crm_err("%s: Triggered assert at %s:%d : %s", function, file, line, assert_condition);
         return;
 
     } else if (do_fork) {
         pid = fork();
 
     } else {
         crm_err("%s: Triggered fatal assert at %s:%d : %s", function, file, line, assert_condition);
     }
 
     if (pid == -1) {
         crm_crit("%s: Cannot create core for non-fatal assert at %s:%d : %s",
                  function, file, line, assert_condition);
         return;
 
     } else if(pid == 0) {
         /* Child process */
         abort();
         return;
     }
 
     /* Parent process */
     crm_err("%s: Forked child %d to record non-fatal assert at %s:%d : %s",
             function, pid, file, line, assert_condition);
     crm_write_blackbox(SIGTRAP, NULL);
 
     do {
         rc = waitpid(pid, &status, 0);
         if(rc == pid) {
             return; /* Job done */
         }
 
     } while(errno == EINTR);
 
     if (errno == ECHILD) {
         /* crm_mon does this */
         crm_trace("Cannot wait on forked child %d - SIGCHLD is probably set to SIG_IGN", pid);
         return;
     }
     crm_perror(LOG_ERR, "Cannot wait on forked child %d", pid);
 }
 
-int
-crm_pid_active(long pid, const char *daemon)
-{
-    static int have_proc_pid = 0;
-
-    if(have_proc_pid == 0) {
-        char proc_path[PATH_MAX], exe_path[PATH_MAX];
-
-        /* check to make sure pid hasn't been reused by another process */
-        snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
-
-        have_proc_pid = 1;
-        if(readlink(proc_path, exe_path, PATH_MAX - 1) < 0) {
-            have_proc_pid = -1;
-        }
-    }
-
-    if (pid <= 0) {
-        return -1;
-
-    } else if (kill(pid, 0) < 0 && errno == ESRCH) {
-        return 0;
-
-    } else if(daemon == NULL || have_proc_pid == -1) {
-        return 1;
-
-    } else {
-        int rc = 0;
-        char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
-
-        /* check to make sure pid hasn't been reused by another process */
-        snprintf(proc_path, sizeof(proc_path), "/proc/%ld/exe", pid);
-
-        rc = readlink(proc_path, exe_path, PATH_MAX - 1);
-        if (rc < 0 && errno == EACCES) {
-            crm_perror(LOG_INFO, "Could not read from %s", proc_path);
-            return 1;
-        } else if (rc < 0) {
-            crm_perror(LOG_ERR, "Could not read from %s", proc_path);
-            return 0;
-        }
-        
-
-        exe_path[rc] = 0;
-
-        if(daemon[0] != '/') {
-            rc = snprintf(myexe_path, sizeof(proc_path), CRM_DAEMON_DIR"/%s", daemon);
-            myexe_path[rc] = 0;
-        } else {
-            rc = snprintf(myexe_path, sizeof(proc_path), "%s", daemon);
-            myexe_path[rc] = 0;
-        }
-        
-        if (strcmp(exe_path, myexe_path) == 0) {
-            return 1;
-        }
-    }
-
-    return 0;
-}
-
-#define	LOCKSTRLEN	11
-
-long
-crm_read_pidfile(const char *filename)
-{
-    int fd;
-    struct stat sbuf;
-    long pid = -ENOENT;
-    char buf[LOCKSTRLEN + 1];
-
-    if ((fd = open(filename, O_RDONLY)) < 0) {
-        goto bail;
-    }
-
-    if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
-        sleep(2);           /* if someone was about to create one,
-                             * give'm a sec to do so
-                             */
-    }
-
-    if (read(fd, buf, sizeof(buf)) < 1) {
-        goto bail;
-    }
-
-    if (sscanf(buf, "%ld", &pid) > 0) {
-        if (pid <= 0) {
-            pid = -ESRCH;
-        } else {
-            crm_trace("Got pid %lu from %s\n", pid, filename);
-        }
-    }
-
-  bail:
-    if (fd >= 0) {
-        close(fd);
-    }
-    return pid;
-}
-
-long
-crm_pidfile_inuse(const char *filename, long mypid, const char *daemon)
-{
-    long pid = crm_read_pidfile(filename);
-
-    if (pid < 2) {
-        /* Invalid pid */
-        pid = -ENOENT;
-        unlink(filename);
-
-    } else if (mypid && pid == mypid) {
-        /* In use by us */
-        pid = pcmk_ok;
-
-    } else if (crm_pid_active(pid, daemon) == FALSE) {
-        /* Contains a stale value */
-        unlink(filename);
-        pid = -ENOENT;
-
-    } else if (mypid && pid != mypid) {
-        /* locked by existing process - give up */
-        pid = -EEXIST;
-    }
-
-    return pid;
-}
-
-static int
-crm_lock_pidfile(const char *filename, const char *name)
-{
-    long mypid = 0;
-    int fd = 0, rc = 0;
-    char buf[LOCKSTRLEN + 2];
-
-    mypid = (unsigned long)getpid();
-
-    rc = crm_pidfile_inuse(filename, 0, name);
-    if (rc == -ENOENT) {
-        /* exists but the process is not active */
-
-    } else if (rc != pcmk_ok) {
-        /* locked by existing process - give up */
-        return rc;
-    }
-
-    if ((fd = open(filename, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
-        /* Hmmh, why did we fail? Anyway, nothing we can do about it */
-        return -errno;
-    }
-
-    snprintf(buf, sizeof(buf), "%*ld\n", LOCKSTRLEN - 1, mypid);
-    rc = write(fd, buf, LOCKSTRLEN);
-    close(fd);
-
-    if (rc != LOCKSTRLEN) {
-        crm_perror(LOG_ERR, "Incomplete write to %s", filename);
-        return -errno;
-    }
-
-    return crm_pidfile_inuse(filename, mypid, name);
-}
-
 void
 crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
 {
     int rc;
     long pid;
     const char *devnull = "/dev/null";
 
     if (daemonize == FALSE) {
         return;
     }
 
     /* Check before we even try... */
     rc = crm_pidfile_inuse(pidfile, 1, name);
     if(rc < pcmk_ok && rc != -ENOENT) {
         pid = crm_read_pidfile(pidfile);
         crm_err("%s: already running [pid %ld in %s]", name, pid, pidfile);
         printf("%s: already running [pid %ld in %s]\n", name, pid, pidfile);
         crm_exit(CRM_EX_ERROR);
     }
 
     pid = fork();
     if (pid < 0) {
         fprintf(stderr, "%s: could not start daemon\n", name);
         crm_perror(LOG_ERR, "fork");
         crm_exit(CRM_EX_OSERR);
 
     } else if (pid > 0) {
         crm_exit(CRM_EX_OK);
     }
 
     rc = crm_lock_pidfile(pidfile, name);
     if(rc < pcmk_ok) {
         crm_err("Could not lock '%s' for %s: %s (%d)", pidfile, name, pcmk_strerror(rc), rc);
         printf("Could not lock '%s' for %s: %s (%d)\n", pidfile, name, pcmk_strerror(rc), rc);
         crm_exit(CRM_EX_ERROR);
     }
 
     umask(S_IWGRP | S_IWOTH | S_IROTH);
 
     close(STDIN_FILENO);
     (void)open(devnull, O_RDONLY);      /* Stdin:  fd 0 */
     close(STDOUT_FILENO);
     (void)open(devnull, O_WRONLY);      /* Stdout: fd 1 */
     close(STDERR_FILENO);
     (void)open(devnull, O_WRONLY);      /* Stderr: fd 2 */
 }
 
 char *
 crm_meta_name(const char *field)
 {
     int lpc = 0;
     int max = 0;
     char *crm_name = NULL;
 
     CRM_CHECK(field != NULL, return NULL);
     crm_name = crm_concat(CRM_META, field, '_');
 
     /* Massage the names so they can be used as shell variables */
     max = strlen(crm_name);
     for (; lpc < max; lpc++) {
         switch (crm_name[lpc]) {
             case '-':
                 crm_name[lpc] = '_';
                 break;
         }
     }
     return crm_name;
 }
 
 const char *
 crm_meta_value(GHashTable * hash, const char *field)
 {
     char *key = NULL;
     const char *value = NULL;
 
     key = crm_meta_name(field);
     if (key) {
         value = g_hash_table_lookup(hash, key);
         free(key);
     }
 
     return value;
 }
 
 static struct option *
 crm_create_long_opts(struct crm_option *long_options)
 {
     struct option *long_opts = NULL;
 
 #ifdef HAVE_GETOPT_H
     int index = 0, lpc = 0;
 
     /*
      * A previous, possibly poor, choice of '?' as the short form of --help
      * means that getopt_long() returns '?' for both --help and for "unknown option"
      *
      * This dummy entry allows us to differentiate between the two in crm_get_option()
      * and exit with the correct error code
      */
     long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = "__dummmy__";
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = '_';
     index++;
 
     for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
         if (long_options[lpc].name[0] == '-') {
             continue;
         }
 
         long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
         /*fprintf(stderr, "Creating %d %s = %c\n", index,
          * long_options[lpc].name, long_options[lpc].val);      */
         long_opts[index].name = long_options[lpc].name;
         long_opts[index].has_arg = long_options[lpc].has_arg;
         long_opts[index].flag = long_options[lpc].flag;
         long_opts[index].val = long_options[lpc].val;
         index++;
     }
 
     /* Now create the list terminator */
     long_opts = realloc_safe(long_opts, (index + 1) * sizeof(struct option));
     long_opts[index].name = NULL;
     long_opts[index].has_arg = 0;
     long_opts[index].flag = 0;
     long_opts[index].val = 0;
 #endif
 
     return long_opts;
 }
 
 void
 crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options,
                 const char *app_desc)
 {
     if (short_options) {
         crm_short_options = strdup(short_options);
 
     } else if (long_options) {
         int lpc = 0;
         int opt_string_len = 0;
         char *local_short_options = NULL;
 
         for (lpc = 0; long_options[lpc].name != NULL; lpc++) {
             if (long_options[lpc].val && long_options[lpc].val != '-' && long_options[lpc].val < UCHAR_MAX) {
                 local_short_options = realloc_safe(local_short_options, opt_string_len + 4);
                 local_short_options[opt_string_len++] = long_options[lpc].val;
                 /* getopt(3) says: Two colons mean an option takes an optional arg; */
                 if (long_options[lpc].has_arg == optional_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 if (long_options[lpc].has_arg >= required_argument) {
                     local_short_options[opt_string_len++] = ':';
                 }
                 local_short_options[opt_string_len] = 0;
             }
         }
         crm_short_options = local_short_options;
         crm_trace("Generated short option string: '%s'", local_short_options);
     }
 
     if (long_options) {
         crm_long_options = long_options;
     }
     if (app_desc) {
         crm_app_description = app_desc;
     }
     if (app_usage) {
         crm_app_usage = app_usage;
     }
 }
 
 int
 crm_get_option(int argc, char **argv, int *index)
 {
     return crm_get_option_long(argc, argv, index, NULL);
 }
 
 int
 crm_get_option_long(int argc, char **argv, int *index, const char **longname)
 {
 #ifdef HAVE_GETOPT_H
     static struct option *long_opts = NULL;
 
     if (long_opts == NULL && crm_long_options) {
         long_opts = crm_create_long_opts(crm_long_options);
     }
 
     *index = 0;
     if (long_opts) {
         int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
 
         switch (flag) {
             case 0:
                 if (long_opts[*index].val) {
                     return long_opts[*index].val;
                 } else if (longname) {
                     *longname = long_opts[*index].name;
                 } else {
                     crm_notice("Unhandled option --%s", long_opts[*index].name);
                     return flag;
                 }
             case -1:           /* End of option processing */
                 break;
             case ':':
                 crm_trace("Missing argument");
                 crm_help('?', CRM_EX_USAGE);
                 break;
             case '?':
                 crm_help('?', (*index? CRM_EX_OK : CRM_EX_USAGE));
                 break;
         }
         return flag;
     }
 #endif
 
     if (crm_short_options) {
         return getopt(argc, argv, crm_short_options);
     }
 
     return -1;
 }
 
 crm_exit_t
 crm_help(char cmd, crm_exit_t exit_code)
 {
     int i = 0;
     FILE *stream = (exit_code ? stderr : stdout);
 
     if (cmd == 'v' || cmd == '$') {
         fprintf(stream, "Pacemaker %s\n", PACEMAKER_VERSION);
         fprintf(stream, "Written by Andrew Beekhof\n");
         goto out;
     }
 
     if (cmd == '!') {
         fprintf(stream, "Pacemaker %s (Build: %s): %s\n", PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
         goto out;
     }
 
     fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
 
     if (crm_app_usage) {
         fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
     }
 
     if (crm_long_options) {
         fprintf(stream, "Options:\n");
         for (i = 0; crm_long_options[i].name != NULL; i++) {
             if (crm_long_options[i].flags & pcmk_option_hidden) {
 
             } else if (crm_long_options[i].flags & pcmk_option_paragraph) {
                 fprintf(stream, "%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].flags & pcmk_option_example) {
                 fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
 
             } else if (crm_long_options[i].val == '-' && crm_long_options[i].desc) {
                 fprintf(stream, "%s\n", crm_long_options[i].desc);
 
             } else {
                 /* is val printable as char ? */
                 if (crm_long_options[i].val && crm_long_options[i].val <= UCHAR_MAX) {
                     fprintf(stream, " -%c,", crm_long_options[i].val);
                 } else {
                     fputs("    ", stream);
                 }
                 fprintf(stream, " --%s%s\t%s\n", crm_long_options[i].name,
                         crm_long_options[i].has_arg == optional_argument ? "[=value]" :
                         crm_long_options[i].has_arg == required_argument ? "=value" : "",
                         crm_long_options[i].desc ? crm_long_options[i].desc : "");
             }
         }
 
     } else if (crm_short_options) {
         fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
         for (i = 0; crm_short_options[i] != 0; i++) {
             int has_arg = no_argument /* 0 */;
 
             if (crm_short_options[i + 1] == ':') {
                 if (crm_short_options[i + 2] == ':')
                     has_arg = optional_argument /* 2 */;
                 else
                     has_arg = required_argument /* 1 */;
             }
 
             fprintf(stream, " -%c %s\n", crm_short_options[i],
                     has_arg == optional_argument ? "[value]" :
                     has_arg == required_argument ? "{value}" : "");
             i += has_arg;
         }
     }
 
     fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
 
   out:
     return crm_exit(exit_code);
 }
 
 void cib_ipc_servers_init(qb_ipcs_service_t **ipcs_ro,
         qb_ipcs_service_t **ipcs_rw,
         qb_ipcs_service_t **ipcs_shm,
         struct qb_ipcs_service_handlers *ro_cb,
         struct qb_ipcs_service_handlers *rw_cb)
 {
     *ipcs_ro = mainloop_add_ipc_server(cib_channel_ro, QB_IPC_NATIVE, ro_cb);
     *ipcs_rw = mainloop_add_ipc_server(cib_channel_rw, QB_IPC_NATIVE, rw_cb);
     *ipcs_shm = mainloop_add_ipc_server(cib_channel_shm, QB_IPC_SHM, rw_cb);
 
     if (*ipcs_ro == NULL || *ipcs_rw == NULL || *ipcs_shm == NULL) {
         crm_err("Failed to create cib servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 void cib_ipc_servers_destroy(qb_ipcs_service_t *ipcs_ro,
         qb_ipcs_service_t *ipcs_rw,
         qb_ipcs_service_t *ipcs_shm)
 {
     qb_ipcs_destroy(ipcs_ro);
     qb_ipcs_destroy(ipcs_rw);
     qb_ipcs_destroy(ipcs_shm);
 }
 
 qb_ipcs_service_t *
 crmd_ipc_server_init(struct qb_ipcs_service_handlers *cb)
 {
     return mainloop_add_ipc_server(CRM_SYSTEM_CRMD, QB_IPC_NATIVE, cb);
 }
 
 void
 attrd_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
 {
     *ipcs = mainloop_add_ipc_server(T_ATTRD, QB_IPC_NATIVE, cb);
 
     if (*ipcs == NULL) {
         crm_err("Failed to create attrd servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 void
 stonith_ipc_server_init(qb_ipcs_service_t **ipcs, struct qb_ipcs_service_handlers *cb)
 {
     *ipcs = mainloop_add_ipc_server("stonith-ng", QB_IPC_NATIVE, cb);
 
     if (*ipcs == NULL) {
         crm_err("Failed to create stonith-ng servers: exiting and inhibiting respawn.");
         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
         crm_exit(CRM_EX_FATAL);
     }
 }
 
 bool
 pcmk_acl_required(const char *user) 
 {
 #if ENABLE_ACL
     if(user == NULL || strlen(user) == 0) {
         crm_trace("no user set");
         return FALSE;
 
     } else if (strcmp(user, CRM_DAEMON_USER) == 0) {
         return FALSE;
 
     } else if (strcmp(user, "root") == 0) {
         return FALSE;
     }
     crm_trace("acls required for %s", user);
     return TRUE;
 #else
     crm_trace("acls not supported");
     return FALSE;
 #endif
 }
 
 #if ENABLE_ACL
 char *
 uid2username(uid_t uid)
 {
     struct passwd *pwent = getpwuid(uid);
 
     if (pwent == NULL) {
         crm_perror(LOG_ERR, "Cannot get password entry of uid: %d", uid);
         return NULL;
 
     } else {
         return strdup(pwent->pw_name);
     }
 }
 
 const char *
 crm_acl_get_set_user(xmlNode * request, const char *field, const char *peer_user)
 {
     /* field is only checked for backwards compatibility */
     static const char *effective_user = NULL;
     const char *requested_user = NULL;
     const char *user = NULL;
 
     if(effective_user == NULL) {
         effective_user = uid2username(geteuid());
     }
 
     requested_user = crm_element_value(request, XML_ACL_TAG_USER);
     if(requested_user == NULL) {
         requested_user = crm_element_value(request, field);
     }
 
     if (is_privileged(effective_user) == FALSE) {
         /* We're not running as a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
         user = effective_user;
 
     } else if(peer_user == NULL && requested_user == NULL) {
         /* No user known or requested, use 'effective_user' and make sure one is set for the request */
         user = effective_user;
 
     } else if(peer_user == NULL) {
         /* No user known, trusting 'requested_user' */
         user = requested_user;
 
     } else if (is_privileged(peer_user) == FALSE) {
         /* The peer is not a privileged user, set or overwrite any existing value for $XML_ACL_TAG_USER */
         user = peer_user;
 
     } else if (requested_user == NULL) {
         /* Even if we're privileged, make sure there is always a value set */
         user = peer_user;
 
     } else {
         /* Legal delegation to 'requested_user' */
         user = requested_user;
     }
 
     // This requires pointer comparison, not string comparison
     if(user != crm_element_value(request, XML_ACL_TAG_USER)) {
         crm_xml_add(request, XML_ACL_TAG_USER, user);
     }
 
     if(field != NULL && user != crm_element_value(request, field)) {
         crm_xml_add(request, field, user);
     }
 
     return requested_user;
 }
 
 void
 determine_request_user(const char *user, xmlNode * request, const char *field)
 {
     /* Get our internal validation out of the way first */
     CRM_CHECK(user != NULL && request != NULL && field != NULL, return);
 
     /* If our peer is a privileged user, we might be doing something on behalf of someone else */
     if (is_privileged(user) == FALSE) {
         /* We're not a privileged user, set or overwrite any existing value for $field */
         crm_xml_replace(request, field, user);
 
     } else if (crm_element_value(request, field) == NULL) {
         /* Even if we're privileged, make sure there is always a value set */
         crm_xml_replace(request, field, user);
 
 /*  } else { Legal delegation */
     }
 
     crm_trace("Processing msg as user '%s'", crm_element_value(request, field));
 }
 #endif
 
 void *
 find_library_function(void **handle, const char *lib, const char *fn, gboolean fatal)
 {
     char *error;
     void *a_function;
 
     if (*handle == NULL) {
         *handle = dlopen(lib, RTLD_LAZY);
     }
 
     if (!(*handle)) {
         crm_err("%sCould not open %s: %s", fatal ? "Fatal: " : "", lib, dlerror());
         if (fatal) {
             crm_exit(CRM_EX_FATAL);
         }
         return NULL;
     }
 
     a_function = dlsym(*handle, fn);
     if (a_function == NULL) {
         error = dlerror();
         crm_err("%sCould not find %s in %s: %s", fatal ? "Fatal: " : "", fn, lib, error);
         if (fatal) {
             crm_exit(CRM_EX_FATAL);
         }
     }
 
     return a_function;
 }
 
 void *
 convert_const_pointer(const void *ptr)
 {
     /* Worst function ever */
     return (void *)ptr;
 }
 
 #ifdef HAVE_UUID_UUID_H
 #  include <uuid/uuid.h>
 #endif
 
 char *
 crm_generate_uuid(void)
 {
     unsigned char uuid[16];
     char *buffer = malloc(37);  /* Including NUL byte */
 
     uuid_generate(uuid);
     uuid_unparse(uuid, buffer);
     return buffer;
 }
 
 /*!
  * \brief Check whether a string represents a cluster daemon name
  *
  * \param[in] name  String to check
  *
  * \return TRUE if name is standard client name used by daemons, FALSE otherwise
  */
 bool
 crm_is_daemon_name(const char *name)
 {
     return (name &&
             (!strcmp(name, CRM_SYSTEM_CRMD)
             || !strcmp(name, CRM_SYSTEM_STONITHD)
             || !strcmp(name, T_ATTRD)
             || !strcmp(name, CRM_SYSTEM_CIB)
             || !strcmp(name, CRM_SYSTEM_MCP)
             || !strcmp(name, CRM_SYSTEM_DC)
             || !strcmp(name, CRM_SYSTEM_TENGINE)
             || !strcmp(name, CRM_SYSTEM_LRMD)));
 }
 
 #include <md5.h>
 
 char *
 crm_md5sum(const char *buffer)
 {
     int lpc = 0, len = 0;
     char *digest = NULL;
     unsigned char raw_digest[MD5_DIGEST_SIZE];
 
     if (buffer == NULL) {
         buffer = "";
     }
     len = strlen(buffer);
 
     crm_trace("Beginning digest of %d bytes", len);
     digest = malloc(2 * MD5_DIGEST_SIZE + 1);
     if(digest) {
         md5_buffer(buffer, len, raw_digest);
         for (lpc = 0; lpc < MD5_DIGEST_SIZE; lpc++) {
             sprintf(digest + (2 * lpc), "%02x", raw_digest[lpc]);
         }
         digest[(2 * MD5_DIGEST_SIZE)] = 0;
         crm_trace("Digest %s.", digest);
 
     } else {
         crm_err("Could not create digest");
     }
     return digest;
 }
 
 #ifdef HAVE_GNUTLS_GNUTLS_H
 void
 crm_gnutls_global_init(void)
 {
     signal(SIGPIPE, SIG_IGN);
     gnutls_global_init();
 }
 #endif
 
 char *
 crm_generate_ra_key(const char *standard, const char *provider, const char *type)
 {
     if (!standard && !provider && !type) {
         return NULL;
     }
 
     return crm_strdup_printf("%s%s%s:%s",
                              (standard? standard : ""),
                              (provider? ":" : ""), (provider? provider : ""),
                              (type? type : ""));
 }
 
 /*!
  * \brief Check whether a resource standard requires a provider to be specified
  *
  * \param[in] standard  Standard name
  *
  * \return TRUE if standard requires a provider, FALSE otherwise
  */
 bool
 crm_provider_required(const char *standard)
 {
     CRM_CHECK(standard != NULL, return FALSE);
 
     /* @TODO
      * - this should probably be case-sensitive, but isn't,
      *   for backward compatibility
      * - it might be nice to keep standards' capabilities (supports provider,
      *   can be promotable, etc.) as structured data somewhere
      */
     if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF)) {
         return TRUE;
     }
     return FALSE;
 }
 
 /*!
  * \brief Parse a "standard[:provider]:type" agent specification
  *
  * \param[in]  spec      Agent specification
  * \param[out] standard  Newly allocated memory containing agent standard (or NULL)
  * \param[out] provider  Newly allocated memory containing agent provider (or NULL)
  * \param[put] type      Newly allocated memory containing agent type (or NULL)
  *
  * \return pcmk_ok if the string could be parsed, -EINVAL otherwise
  *
  * \note It is acceptable for the type to contain a ':' if the standard supports
  *       that. For example, systemd supports the form "systemd:UNIT@A:B".
  * \note It is the caller's responsibility to free the returned values.
  */
 int
 crm_parse_agent_spec(const char *spec, char **standard, char **provider,
                      char **type)
 {
     char *colon;
 
     CRM_CHECK(spec && standard && provider && type, return -EINVAL);
     *standard = NULL;
     *provider = NULL;
     *type = NULL;
 
     colon = strchr(spec, ':');
     if ((colon == NULL) || (colon == spec)) {
         return -EINVAL;
     }
 
     *standard = strndup(spec, colon - spec);
     spec = colon + 1;
 
     if (crm_provider_required(*standard)) {
         colon = strchr(spec, ':');
         if ((colon == NULL) || (colon == spec)) {
             free(*standard);
             return -EINVAL;
         }
         *provider = strndup(spec, colon - spec);
         spec = colon + 1;
     }
 
     if (*spec == '\0') {
         free(*standard);
         free(*provider);
         return -EINVAL;
     }
 
     *type = strdup(spec);
     return pcmk_ok;
 }