diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index 22d3844..ae1d8a3 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -9,7 +9,7 @@ DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 LIBDEPS = -lpthread -ldl -ldevmapper -ludev
 
-OBJS = memory.o parser.o vector.o devmapper.o \
+OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        hwtable.o blacklist.o util.o dmparser.o config.o \
        structs.o discovery.o propsel.o dict.o \
        pgpolicies.o debug.o regex.o defaults.o uevent.o \
diff --git a/libmultipath/callout.c b/libmultipath/callout.c
new file mode 100644
index 0000000..c35c7c0
--- /dev/null
+++ b/libmultipath/callout.c
@@ -0,0 +1,216 @@
+/*
+ * Source: copy of the udev package source file
+ *
+ * Copyrights of the source file apply
+ * Copyright (c) 2004 Christophe Varoqui
+ */
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include "checkers.h"
+#include "vector.h"
+#include "structs.h"
+#include "util.h"
+#include "debug.h"
+
+int execute_program(char *path, char *value, int len)
+{
+	int retval;
+	int count;
+	int status;
+	int fds[2], null_fd;
+	pid_t pid;
+	char *pos;
+	char arg[CALLOUT_MAX_SIZE];
+	int argc = sizeof(arg) / 2;
+	char *argv[argc + 1];
+	int i;
+
+	i = 0;
+
+	if (strchr(path, ' ')) {
+		strlcpy(arg, path, sizeof(arg));
+		pos = arg;
+		while (pos != NULL && i < argc) {
+			if (pos[0] == '\'') {
+				/* don't separate if in apostrophes */
+				pos++;
+				argv[i] = strsep(&pos, "\'");
+				while (pos[0] == ' ')
+					pos++;
+			} else {
+				argv[i] = strsep(&pos, " ");
+			}
+			i++;
+		}
+	} else {
+		argv[i++] = path;
+	}
+	argv[i] =  NULL;
+
+	retval = pipe(fds);
+
+	if (retval != 0) {
+		condlog(0, "error creating pipe for callout: %s", strerror(errno));
+		return -1;
+	}
+
+	pid = fork();
+
+	switch(pid) {
+	case 0:
+		/* child */
+		close(STDOUT_FILENO);
+
+		/* dup write side of pipe to STDOUT */
+		if (dup(fds[1]) < 0)
+			return -1;
+
+		/* Ignore writes to stderr */
+		null_fd = open("/dev/null", O_WRONLY);
+		if (null_fd > 0) {
+			close(STDERR_FILENO);
+			retval = dup(null_fd);
+			close(null_fd);
+		}
+
+		retval = execv(argv[0], argv);
+		condlog(0, "error execing %s : %s", argv[0], strerror(errno));
+		exit(-1);
+	case -1:
+		condlog(0, "fork failed: %s", strerror(errno));
+		close(fds[0]);
+		close(fds[1]);
+		return -1;
+	default:
+		/* parent reads from fds[0] */
+		close(fds[1]);
+		retval = 0;
+		i = 0;
+		while (1) {
+			count = read(fds[0], value + i, len - i-1);
+			if (count <= 0)
+				break;
+
+			i += count;
+			if (i >= len-1) {
+				condlog(0, "not enough space for response from %s", argv[0]);
+				retval = -1;
+				break;
+			}
+		}
+
+		if (count < 0) {
+			condlog(0, "no response from %s", argv[0]);
+			retval = -1;
+		}
+
+		if (i > 0 && value[i-1] == '\n')
+			i--;
+		value[i] = '\0';
+
+		wait(&status);
+		close(fds[0]);
+
+		retval = -1;
+		if (WIFEXITED(status)) {
+			status = WEXITSTATUS(status);
+			if (status == 0)
+				retval = 0;
+			else
+				condlog(0, "%s exitted with %d", argv[0], status);
+		}
+		else if (WIFSIGNALED(status))
+			condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
+		else
+			condlog(0, "%s terminated abnormally", argv[0]);
+	}
+	return retval;
+}
+
+extern int
+apply_format (char * string, char * cmd, struct path * pp)
+{
+	char * pos;
+	char * dst;
+	char * p;
+	char * q;
+	int len;
+	int myfree;
+
+	if (!string)
+		return 1;
+
+	if (!cmd)
+		return 1;
+
+	dst = cmd;
+	p = dst;
+	pos = strchr(string, '%');
+	myfree = CALLOUT_MAX_SIZE;
+
+	if (!pos) {
+		strcpy(dst, string);
+		return 0;
+	}
+
+	len = (int) (pos - string) + 1;
+	myfree -= len;
+
+	if (myfree < 2)
+		return 1;
+
+	snprintf(p, len, "%s", string);
+	p += len - 1;
+	pos++;
+
+	switch (*pos) {
+	case 'n':
+		len = strlen(pp->dev) + 1;
+		myfree -= len;
+
+		if (myfree < 2)
+			return 1;
+
+		snprintf(p, len, "%s", pp->dev);
+		for (q = p; q < p + len; q++) {
+			if (q && *q == '!')
+				*q = '/';
+		}
+		p += len - 1;
+		break;
+	case 'd':
+		len = strlen(pp->dev_t) + 1;
+		myfree -= len;
+
+		if (myfree < 2)
+			return 1;
+
+		snprintf(p, len, "%s", pp->dev_t);
+		p += len - 1;
+		break;
+	default:
+		break;
+	}
+	pos++;
+
+	if (!*pos)
+		return 0;
+
+	len = strlen(pos) + 1;
+	myfree -= len;
+
+	if (myfree < 2)
+		return 1;
+
+	snprintf(p, len, "%s", pos);
+	condlog(3, "reformated callout = %s", dst);
+	return 0;
+}
diff --git a/libmultipath/callout.h b/libmultipath/callout.h
new file mode 100644
index 0000000..ab648e8
--- /dev/null
+++ b/libmultipath/callout.h
@@ -0,0 +1,7 @@
+#ifndef _CALLOUT_H
+#define _CALLOUT_H
+
+int execute_program(char *, char *, int);
+int apply_format (char *, char *, struct path *);
+
+#endif /* _CALLOUT_H */
diff --git a/libmultipath/config.c b/libmultipath/config.c
index 138e6b4..cc44244 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -167,6 +167,9 @@ free_hwe (struct hwentry * hwe)
 	if (hwe->revision)
 		FREE(hwe->revision);
 
+	if (hwe->getuid)
+		FREE(hwe->getuid);
+
 	if (hwe->uid_attribute)
 		FREE(hwe->uid_attribute);
 
@@ -224,6 +227,9 @@ free_mpe (struct mpentry * mpe)
 	if (mpe->selector)
 		FREE(mpe->selector);
 
+	if (mpe->getuid)
+		FREE(mpe->getuid);
+
 	if (mpe->uid_attribute)
 		FREE(mpe->uid_attribute);
 
@@ -312,6 +318,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
 	merge_str(vendor);
 	merge_str(product);
 	merge_str(revision);
+	merge_str(getuid);
 	merge_str(uid_attribute);
 	merge_str(features);
 	merge_str(hwhandler);
@@ -369,6 +376,9 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
 	if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute)))
 		goto out;
 
+	if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid)))
+		goto out;
+
 	if (dhwe->features && !(hwe->features = set_param_str(dhwe->features)))
 		goto out;
 
@@ -473,6 +483,9 @@ free_config (struct config * conf)
 	if (conf->uid_attribute)
 		FREE(conf->uid_attribute);
 
+	if (conf->getuid)
+		FREE(conf->getuid);
+
 	if (conf->features)
 		FREE(conf->features);
 
diff --git a/libmultipath/config.h b/libmultipath/config.h
index f1578a8..ca65f88 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -27,6 +27,7 @@ struct hwentry {
 	char * product;
 	char * revision;
 	char * uid_attribute;
+	char * getuid;
 	char * features;
 	char * hwhandler;
 	char * selector;
@@ -54,6 +55,7 @@ struct mpentry {
 	char * wwid;
 	char * alias;
 	char * uid_attribute;
+	char * getuid;
 	char * selector;
 	char * features;
 
@@ -116,6 +118,7 @@ struct config {
 	char * multipath_dir;
 	char * selector;
 	char * uid_attribute;
+	char * getuid;
 	char * features;
 	char * hwhandler;
 	char * bindings_file;
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 757d220..b747fcd 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -161,6 +161,17 @@ def_uid_attribute_handler(vector strvec)
 }
 
 static int
+def_getuid_callout_handler(vector strvec)
+{
+	conf->getuid = set_value(strvec);
+
+	if (!conf->getuid)
+		return 1;
+
+	return 0;
+}
+
+static int
 def_prio_handler(vector strvec)
 {
 	conf->prio_name = set_value(strvec);
@@ -973,6 +984,19 @@ hw_uid_attribute_handler(vector strvec)
 }
 
 static int
+hw_getuid_callout_handler(vector strvec)
+{
+	struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
+
+	hwe->getuid = set_value(strvec);
+
+	if (!hwe->getuid)
+		return 1;
+
+	return 0;
+}
+
+static int
 hw_selector_handler(vector strvec)
 {
 	struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable);
@@ -2118,6 +2142,17 @@ snprint_hw_uid_attribute (char * buff, int len, void * data)
 }
 
 static int
+snprint_hw_getuid_callout (char * buff, int len, void * data)
+{
+	struct hwentry * hwe = (struct hwentry *)data;
+
+	if (!hwe->getuid)
+		return 0;
+
+	return snprintf(buff, len, "\"%s\"", hwe->getuid);
+}
+
+static int
 snprint_hw_prio (char * buff, int len, void * data)
 {
 	struct hwentry * hwe = (struct hwentry *)data;
@@ -2443,6 +2478,15 @@ snprint_def_uid_attribute (char * buff, int len, void * data)
 }
 
 static int
+snprint_def_getuid_callout (char * buff, int len, void * data)
+{
+	if (!conf->getuid)
+		return 0;
+
+	return snprintf(buff, len, "\"%s\"", conf->getuid);
+}
+
+static int
 snprint_def_prio (char * buff, int len, void * data)
 {
 	if (!conf->prio_name)
@@ -2738,6 +2782,7 @@ init_keywords(void)
 	install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
 	install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_path_grouping_policy);
 	install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
+	install_keyword("getuid_callout", &def_getuid_callout_handler, &snprint_def_getuid_callout);
 	install_keyword("prio", &def_prio_handler, &snprint_def_prio);
 	install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args);
 	install_keyword("features", &def_features_handler, &snprint_def_features);
@@ -2769,6 +2814,7 @@ init_keywords(void)
 	__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
 	__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
 	__deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
+	__deprecated install_keyword("default_getuid_callout", &def_getuid_callout_handler, NULL);
 	__deprecated install_keyword("default_features", &def_features_handler, NULL);
 	__deprecated install_keyword("default_path_checker", &def_path_checker_handler, NULL);
 
@@ -2809,6 +2855,7 @@ init_keywords(void)
 	install_keyword("product_blacklist", &bl_product_handler, &snprint_hw_bl_product);
 	install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_path_grouping_policy);
 	install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute);
+	install_keyword("getuid_callout", &hw_getuid_callout_handler, &snprint_hw_getuid_callout);
 	install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector);
 	install_keyword("path_checker", &hw_path_checker_handler, &snprint_hw_path_checker);
 	install_keyword("checker", &hw_path_checker_handler, NULL);
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 6af5083..fcb8e4f 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -20,6 +20,7 @@
 #include "structs.h"
 #include "config.h"
 #include "blacklist.h"
+#include "callout.h"
 #include "debug.h"
 #include "propsel.h"
 #include "sg_include.h"
@@ -974,9 +975,9 @@ static int
 get_uid (struct path * pp)
 {
 	char *c;
-	const char *value;
+	const char *origin;
 
-	if (!pp->uid_attribute)
+	if (!pp->uid_attribute && !pp->getuid)
 		select_getuid(pp);
 
 	if (!pp->udev) {
@@ -985,23 +986,41 @@ get_uid (struct path * pp)
 	}
 
 	memset(pp->wwid, 0, WWID_SIZE);
-	value = udev_device_get_property_value(pp->udev, pp->uid_attribute);
-	if ((!value || strlen(value) == 0) && conf->dry_run == 2)
-		value = getenv(pp->uid_attribute);
-	if (value && strlen(value)) {
-		size_t len = WWID_SIZE;
-
-		if (strlen(value) + 1 > WWID_SIZE) {
-			condlog(0, "%s: wwid overflow", pp->dev);
-		} else {
-			len = strlen(value);
+	if (pp->getuid) {
+		char buff[CALLOUT_MAX_SIZE];
+
+		/* Use 'getuid' callout, deprecated */
+		condlog(1, "%s: using deprecated getuid callout", pp->dev);
+		if (apply_format(pp->getuid, &buff[0], pp)) {
+			condlog(0, "error formatting uid callout command");
+			memset(pp->wwid, 0, WWID_SIZE);
+		} else if (execute_program(buff, pp->wwid, WWID_SIZE)) {
+			condlog(3, "error calling out %s", buff);
+			memset(pp->wwid, 0, WWID_SIZE);
 		}
-		strncpy(pp->wwid, value, len);
+		origin = "callout";
 	} else {
-		condlog(3, "%s: no %s attribute", pp->dev,
-			pp->uid_attribute);
+		const char *value;
+
+		value = udev_device_get_property_value(pp->udev,
+						       pp->uid_attribute);
+		if ((!value || strlen(value) == 0) && conf->dry_run == 2)
+			value = getenv(pp->uid_attribute);
+		if (value && strlen(value)) {
+			size_t len = WWID_SIZE;
+
+			if (strlen(value) + 1 > WWID_SIZE) {
+				condlog(0, "%s: wwid overflow", pp->dev);
+			} else {
+				len = strlen(value);
+			}
+			strncpy(pp->wwid, value, len);
+		} else {
+			condlog(3, "%s: no %s attribute", pp->dev,
+				pp->uid_attribute);
+		}
+		origin = "udev";
 	}
-
 	/* Strip any trailing blanks */
 	c = strchr(pp->wwid, '\0');
 	c--;
@@ -1009,8 +1028,8 @@ get_uid (struct path * pp)
 		*c = '\0';
 		c--;
 	}
-	condlog(3, "%s: uid = %s (udev)", pp->dev,
-		*pp->wwid == '\0' ? "<empty>" : pp->wwid);
+	condlog(3, "%s: uid = %s (%s)", pp->dev,
+		*pp->wwid == '\0' ? "<empty>" : pp->wwid, origin);
 	return 0;
 }
 
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 613dafc..65c2c7f 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -371,12 +371,24 @@ select_getuid (struct path * pp)
 			pp->dev, pp->uid_attribute);
 		return 0;
 	}
+	if (pp->hwe && pp->hwe->getuid) {
+		pp->getuid = pp->hwe->getuid;
+		condlog(3, "%s: getuid = %s (deprecated) (controller setting)",
+			pp->dev, pp->getuid);
+		return 0;
+	}
 	if (conf->uid_attribute) {
 		pp->uid_attribute = conf->uid_attribute;
 		condlog(3, "%s: uid_attribute = %s (config file default)",
 			pp->dev, pp->uid_attribute);
 		return 0;
 	}
+	if (conf->getuid) {
+		pp->getuid = conf->getuid;
+		condlog(3, "%s: getuid = %s (deprecated) (config file default)",
+			pp->dev, pp->getuid);
+		return 0;
+	}
 	pp->uid_attribute = STRDUP(DEFAULT_UID_ATTRIBUTE);
 	condlog(3, "%s: uid_attribute = %s (internal default)",
 		pp->dev, pp->uid_attribute);
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index ef3d76f..64de06e 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -173,6 +173,7 @@ struct path {
 	int pgindex;
 	int detect_prio;
 	char * uid_attribute;
+	char * getuid;
 	struct prio prio;
 	char * prio_args;
 	struct checker checker;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 186cd0e..993d3e0 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -82,6 +82,7 @@ orphan_path (struct path * pp)
 	pp->mpp = NULL;
 	pp->dmstate = PSTATE_UNDEF;
 	pp->uid_attribute = NULL;
+	pp->getuid = NULL;
 	prio_put(&pp->prio);
 	checker_put(&pp->checker);
 	if (pp->fd >= 0)
diff --git a/multipath.conf.annotated b/multipath.conf.annotated
index 09ed632..15ec468 100644
--- a/multipath.conf.annotated
+++ b/multipath.conf.annotated
@@ -59,10 +59,20 @@
 #	path_grouping_policy	multibus
 #
 #	#
+#	# name    : uid_attribute
+#	# scope   : multipath & multipathd
+#	# desc    : the default udev attribute from which the path
+#	# 	    identifier should be generated.
+#	# default : ID_SERIAL
+#	#
+#	uid_attribute	"ID_SERIAL"
+#
+#	#
 #	# name    : getuid_callout
 #	# scope   : multipath & multipathd
 #	# desc    : the default program and args to callout to obtain a unique 
-#	#           path identifier. Absolute path required
+#	#           path identifier. This parameter is deprecated.
+#	#           This parameter is deprecated, superseded by uid_attribute
 #	# default : /lib/udev/scsi_id --whitelisted --device=/dev/%n
 #	#
 #	getuid_callout	"/lib/udev/scsi_id --whitelisted --device=/dev/%n"
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 70d15f3..ade9885 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -144,6 +144,11 @@ The udev attribute providing a unique path
 identifier. Default value is
 .I ID_SERIAL
 .TP
+.B getuid_callout
+The default program and args to callout to obtain a unique path
+identifier. Should be specified with an absolute path.
+This parameter is deprecated; \fIuid_attribute\fR should be used instead.
+.TP
 .B prio
 The name of the path priority routine. The specified routine
 should return a numeric value specifying the relative priority
