diff --git a/usr.bin/devattr/devattr.c b/usr.bin/devattr/devattr.c
new file mode 100644
index 0000000..72ed1a7
--- /dev/null
+++ b/usr.bin/devattr/devattr.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
+ *
+ * This code is derived from software contributed to The DragonFly Project
+ * by Antonio Huete Jimenez <tuxillo@quantumachine.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ * 3. Neither the name of The DragonFly Project nor the names of its
+ *    contributors may be used to endorse or promote products derived
+ *    from this software without specific, prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+#include <devattr.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/stat.h>
+
+static void usage(void);
+static int query_devices(struct udev_enumerate *, int, char **);
+static void process_entry(struct udev_list_entry *udev_le, int, char **);
+
+int
+main(int argc, char *argv[])
+{
+	struct udev *ud;
+	struct udev_enumerate *devlist;
+	char *devstr, *token;
+	int c;
+
+	devstr = token = NULL;
+
+	while ((c = getopt(argc, argv, "d:")) != -1) {
+		switch(c) {
+		case 'd':
+			devstr = strdup(optarg);
+			break;
+		default:
+			usage();
+			/* NOT REACHED */
+		}
+	}
+	argc -= optind;
+	argv += optind;
+
+	/* Need device string */
+	if (devstr == NULL)
+		usage();
+
+	ud = udev_new();
+	if (ud == NULL)
+		err(1, "Can't contact udevd");
+
+	devlist = udev_enumerate_new(ud);
+	if (devlist == NULL)
+		err(1, "udev_enumerate_new");
+
+	/* Parse all the devices specified in the comma-separated
+	 * list. Each device will be added as an expression to the
+	 * udev enumerate and pass it to the query_devices() function
+	 * which will properly extract the desired properties if they
+	 * have found. If no key(s) were specified in the command line,
+	 * then all the properties for the matched devices will be shown.
+	 */
+	while ((token = strsep(&devstr, ",")) != NULL) {
+		udev_enumerate_add_match_expr(devlist, "name", token);
+	}
+
+	if (query_devices(devlist, argc, argc > 0 ? argv : NULL) != 0)
+		fprintf(stderr, "No valid device(s) specified\n");
+
+	free(devstr);
+	udev_unref(ud);
+
+	return 0;
+}
+
+
+static void
+usage(void)
+{
+	fprintf(stderr, "usage: devattr [-d devlist] keylist ...\n");
+	exit(1);
+}
+
+static int
+query_devices(struct udev_enumerate *devlist, int argc, char **argv)
+{
+	struct udev_list_entry *udev_le_first, *udev_le;
+	int ret = -1;
+
+	if (udev_enumerate_scan_devices(devlist) != 0)
+		goto fail;
+
+	udev_le_first = udev_enumerate_get_list_entry(devlist);
+	if (udev_le_first == NULL)
+		goto fail;
+
+	udev_list_entry_foreach(udev_le, udev_le_first)
+		process_entry(udev_le, argc, argv);
+
+	ret = 0;
+
+fail:
+	udev_enumerate_unref(devlist);
+	return (ret);
+}
+
+static void
+process_entry(struct udev_list_entry *udev_le, int argc, char **argv)
+{
+	struct udev_device *udev_dev;
+	const char *name, *keystr, *valstr;
+	int i, numkeys;
+
+	name = keystr = valstr = NULL;
+
+	udev_dev = udev_list_entry_get_device(udev_le);
+	if (udev_dev == NULL)
+		return;
+
+	name = udev_device_get_property_value(udev_dev, "name");
+	fprintf(stdout, "Query for device /dev/%s\n", name);
+
+	if (argv != NULL) {
+		for (i = 0; i < argc; i++) {
+			keystr = argv[i];
+			valstr = udev_device_get_property_value(udev_dev, keystr);
+			if (valstr == NULL)
+				continue;
+			fprintf(stdout, "\t%s = %s\n", keystr, valstr);
+		}
+	} else {
+		prop_dictionary_t dd;
+		prop_array_t keylist;
+		prop_dictionary_keysym_t pks;
+		prop_object_t po;
+
+		if ((dd = udev_device_get_dictionary(udev_dev)) == NULL ||
+		    (keylist = prop_dictionary_all_keys(dd)) == NULL)
+			return;
+
+		numkeys = prop_array_count(keylist);
+		for (i = 0; i < numkeys; i++) {
+			po = prop_array_get(keylist, i);
+			pks = po;
+			keystr = prop_dictionary_keysym_cstring_nocopy(pks);
+
+			valstr = udev_device_get_property_value(udev_dev, keystr);
+			fprintf(stdout, "\t%s = %s\n", keystr, valstr);
+		}
+	}
+}
