132fe4284SNolan Lum /*
232fe4284SNolan Lum * Copyright (c) 2010
332fe4284SNolan Lum * The DragonFly Project. All rights reserved.
432fe4284SNolan Lum *
532fe4284SNolan Lum * This code is derived from software contributed to The DragonFly Project
632fe4284SNolan Lum * by Nolan Lum <nol888@gmail.com>
732fe4284SNolan Lum *
832fe4284SNolan Lum * Redistribution and use in source and binary forms, with or without
932fe4284SNolan Lum * modification, are permitted provided that the following conditions
1032fe4284SNolan Lum * are met:
1132fe4284SNolan Lum *
1232fe4284SNolan Lum * 1. Redistributions of source code must retain the above copyright
1332fe4284SNolan Lum * notice, this list of conditions and the following disclaimer.
1432fe4284SNolan Lum * 2. Redistributions in binary form must reproduce the above copyright
1532fe4284SNolan Lum * notice, this list of conditions and the following disclaimer in
1632fe4284SNolan Lum * the documentation and/or other materials provided with the
1732fe4284SNolan Lum * distribution.
1832fe4284SNolan Lum * 3. Neither the name of The DragonFly Project nor the names of its
1932fe4284SNolan Lum * contributors may be used to endorse or promote products derived
2032fe4284SNolan Lum * from this software without specific, prior written permission.
2132fe4284SNolan Lum *
2232fe4284SNolan Lum * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
2332fe4284SNolan Lum * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2432fe4284SNolan Lum * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
2532fe4284SNolan Lum * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2632fe4284SNolan Lum * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2732fe4284SNolan Lum * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2832fe4284SNolan Lum * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2932fe4284SNolan Lum * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3032fe4284SNolan Lum * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3132fe4284SNolan Lum * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
3232fe4284SNolan Lum * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3332fe4284SNolan Lum * SUCH DAMAGE.
3432fe4284SNolan Lum */
3532fe4284SNolan Lum
3632fe4284SNolan Lum #include <sys/queue.h>
3732fe4284SNolan Lum
3832fe4284SNolan Lum #include <ctype.h>
3932fe4284SNolan Lum #include <devattr.h>
4032fe4284SNolan Lum #include <err.h>
4132fe4284SNolan Lum #include <inttypes.h>
4232fe4284SNolan Lum #include <string.h>
4332fe4284SNolan Lum #include <stdlib.h>
4432fe4284SNolan Lum #include <stdio.h>
4532fe4284SNolan Lum #include <sysexits.h>
4632fe4284SNolan Lum #include <unistd.h>
4732fe4284SNolan Lum
4832fe4284SNolan Lum static SLIST_HEAD(, sl_entry) props = SLIST_HEAD_INITIALIZER(props);
4932fe4284SNolan Lum struct sl_entry {
5032fe4284SNolan Lum char *val;
5132fe4284SNolan Lum SLIST_ENTRY(sl_entry) entries;
5232fe4284SNolan Lum } *ent;
5332fe4284SNolan Lum
54*1de39603SMatthew Dillon static int MonitorOpt;
55*1de39603SMatthew Dillon
5632fe4284SNolan Lum static void
usage(const char * name)5714861de4SSascha Wildner usage(const char *name)
5814861de4SSascha Wildner {
5914861de4SSascha Wildner fprintf(stderr,
6014861de4SSascha Wildner "usage: %s [-Ah] [-p property] [-d device] [-m key:value] [-r key:value]\n"
6132fe4284SNolan Lum "Valid options are:\n"
6232fe4284SNolan Lum " -A\n"
6332fe4284SNolan Lum " Don't display aliases.\n"
6432fe4284SNolan Lum " -h\n"
6532fe4284SNolan Lum " Print this help message.\n\n"
66*1de39603SMatthew Dillon " -M\n"
67*1de39603SMatthew Dillon " Continue monitoring after initial scan.\n\n"
6832fe4284SNolan Lum "Valid options with their arguments are:\n"
6932fe4284SNolan Lum " -p <property>\n"
7032fe4284SNolan Lum " Only display property; can be specified multiple times and\n"
7132fe4284SNolan Lum " combined with all other options.\n"
7232fe4284SNolan Lum " -d <device>\n"
7332fe4284SNolan Lum " Only display devices with name `device'. When used with\n"
7432fe4284SNolan Lum " -p, only properties `-p' of device `-d' are listed. Can be\n"
7532fe4284SNolan Lum " specified multiple times. Allows wildcards.\n"
7632fe4284SNolan Lum " -m <key:value>\n"
7732fe4284SNolan Lum " Only display devices whose property `key' matches with wildcards\n"
7832fe4284SNolan Lum " value `value' unless the key-value pair starts with ~, in which\n"
7932fe4284SNolan Lum " case the match is inverted. Stacks with -p, -d, and -m.\n"
8032fe4284SNolan Lum " Can be specified multiple times.\n"
8132fe4284SNolan Lum " -r <key:value>\n"
8214861de4SSascha Wildner " Behaves similarly to `-m', but matches with regex.\n",
8314861de4SSascha Wildner name);
8414861de4SSascha Wildner exit(EX_USAGE);
8532fe4284SNolan Lum }
8632fe4284SNolan Lum
8714861de4SSascha Wildner static void
parse_args(int argc,char * argv[],struct udev_enumerate * enumerate)8814861de4SSascha Wildner parse_args(int argc, char *argv[], struct udev_enumerate *enumerate)
8914861de4SSascha Wildner {
9032fe4284SNolan Lum int ch, invert;
9132fe4284SNolan Lum char *colon;
9232fe4284SNolan Lum
9332fe4284SNolan Lum SLIST_INIT(&props);
9432fe4284SNolan Lum
9532fe4284SNolan Lum /* A = no aliases */
9632fe4284SNolan Lum /* p = properties to list (defaults to all) */
9732fe4284SNolan Lum /* d = devices to list (defaults to all) */
9832fe4284SNolan Lum /* m = display only devices in d that match these prop values */
9932fe4284SNolan Lum /* r = display only devices in d that match these prop values (regex) */
100*1de39603SMatthew Dillon while ((ch = getopt(argc, argv, "AMp:d:m:r:h")) != -1) {
10132fe4284SNolan Lum invert = false;
10232fe4284SNolan Lum
10332fe4284SNolan Lum switch (ch) {
10432fe4284SNolan Lum case 'A':
10514861de4SSascha Wildner udev_enumerate_add_match_property(enumerate, "alias",
10614861de4SSascha Wildner "0");
10732fe4284SNolan Lum break;
10832fe4284SNolan Lum case 'p':
10932fe4284SNolan Lum ent = malloc(sizeof(struct sl_entry));
11032fe4284SNolan Lum ent->val = optarg;
11132fe4284SNolan Lum SLIST_INSERT_HEAD(&props, ent, entries);
11232fe4284SNolan Lum break;
113*1de39603SMatthew Dillon case 'M':
114*1de39603SMatthew Dillon MonitorOpt = 1;
115*1de39603SMatthew Dillon break;
11632fe4284SNolan Lum case 'd':
11714861de4SSascha Wildner udev_enumerate_add_match_expr(enumerate, "name",
11814861de4SSascha Wildner optarg);
11932fe4284SNolan Lum break;
12032fe4284SNolan Lum case 'm':
12132fe4284SNolan Lum case 'r':
12232fe4284SNolan Lum /* Check for exclusion. */
12332fe4284SNolan Lum invert = *optarg == '~';
12432fe4284SNolan Lum
12532fe4284SNolan Lum /* Split into key/value. */
12632fe4284SNolan Lum colon = strchr(optarg, ':');
12732fe4284SNolan Lum if (colon == NULL) {
12832fe4284SNolan Lum fprintf(stderr,
12932fe4284SNolan Lum "Invalid property key/value pair `%s'.\n",
13032fe4284SNolan Lum optarg);
13114861de4SSascha Wildner return;
13232fe4284SNolan Lum }
13332fe4284SNolan Lum
13432fe4284SNolan Lum *colon = '\0';
13532fe4284SNolan Lum if (invert) {
13632fe4284SNolan Lum if (ch == 'r')
13732fe4284SNolan Lum udev_enumerate_add_nomatch_regex(enumerate,
13832fe4284SNolan Lum optarg + 1, colon + 1);
13932fe4284SNolan Lum else
14032fe4284SNolan Lum udev_enumerate_add_nomatch_expr(enumerate,
14132fe4284SNolan Lum optarg + 1, colon + 1);
14232fe4284SNolan Lum } else {
14332fe4284SNolan Lum if (ch == 'r')
14432fe4284SNolan Lum udev_enumerate_add_match_regex(enumerate,
14532fe4284SNolan Lum optarg, colon + 1);
14632fe4284SNolan Lum else
14732fe4284SNolan Lum udev_enumerate_add_match_expr(enumerate,
14832fe4284SNolan Lum optarg, colon + 1);
14932fe4284SNolan Lum }
15032fe4284SNolan Lum break;
15132fe4284SNolan Lum case 'h':
15214861de4SSascha Wildner default:
15332fe4284SNolan Lum usage(argv[0]);
15432fe4284SNolan Lum }
15532fe4284SNolan Lum }
15614861de4SSascha Wildner return;
15732fe4284SNolan Lum }
15832fe4284SNolan Lum
15932fe4284SNolan Lum static void
print_prop(const char * key,prop_object_t value)16014861de4SSascha Wildner print_prop(const char* key, prop_object_t value)
16114861de4SSascha Wildner {
16232fe4284SNolan Lum char *val_str;
16332fe4284SNolan Lum
16432fe4284SNolan Lum printf("\t%s = ", key);
16532fe4284SNolan Lum
16632fe4284SNolan Lum prop_type_t val_type = prop_object_type(value);
16732fe4284SNolan Lum switch (val_type) {
16832fe4284SNolan Lum case PROP_TYPE_BOOL:
16914861de4SSascha Wildner printf("%s\n", prop_bool_true((prop_bool_t)value) ?
17014861de4SSascha Wildner "true" : "false");
17132fe4284SNolan Lum break;
17232fe4284SNolan Lum case PROP_TYPE_NUMBER:
17332fe4284SNolan Lum if (prop_number_unsigned((prop_number_t)value))
17432fe4284SNolan Lum printf("%1$"PRIu64" (0x%1$"PRIx64")\n",
17532fe4284SNolan Lum prop_number_unsigned_integer_value((prop_number_t)value));
17632fe4284SNolan Lum else
17732fe4284SNolan Lum printf("%"PRId64"\n",
17832fe4284SNolan Lum prop_number_integer_value((prop_number_t)value));
17932fe4284SNolan Lum break;
18032fe4284SNolan Lum case PROP_TYPE_STRING:
18132fe4284SNolan Lum val_str = prop_string_cstring(value);
18232fe4284SNolan Lum printf("%s\n", val_str);
18332fe4284SNolan Lum free(val_str);
18432fe4284SNolan Lum break;
18532fe4284SNolan Lum default:
18632fe4284SNolan Lum break;
18732fe4284SNolan Lum }
18832fe4284SNolan Lum }
18932fe4284SNolan Lum
19032fe4284SNolan Lum int
main(int argc,char * argv[])19114861de4SSascha Wildner main(int argc, char* argv[])
19214861de4SSascha Wildner {
19332fe4284SNolan Lum struct udev *ctx;
19432fe4284SNolan Lum struct udev_enumerate *enumerate;
195*1de39603SMatthew Dillon struct udev_monitor *mon;
19632fe4284SNolan Lum struct udev_list_entry *current;
19732fe4284SNolan Lum struct udev_device *dev;
19832fe4284SNolan Lum prop_object_t key_val;
19932fe4284SNolan Lum prop_dictionary_t dict;
20032fe4284SNolan Lum prop_dictionary_keysym_t cur_key;
20132fe4284SNolan Lum prop_object_iterator_t iter;
20232fe4284SNolan Lum const char *key_str;
20332fe4284SNolan Lum char *dev_name;
20432fe4284SNolan Lum int ret;
20532fe4284SNolan Lum
20632fe4284SNolan Lum ctx = udev_new();
207636d9c9eSAlex Hornung if (ctx == NULL) {
208636d9c9eSAlex Hornung fprintf(stderr, "Make sure udevd is running\n");
20932fe4284SNolan Lum err(EX_UNAVAILABLE, "udev_new");
210636d9c9eSAlex Hornung }
21132fe4284SNolan Lum
21232fe4284SNolan Lum enumerate = udev_enumerate_new(ctx);
21332fe4284SNolan Lum if (enumerate == NULL)
21432fe4284SNolan Lum err(EX_UNAVAILABLE, "udev_enumerate_new");
21532fe4284SNolan Lum
21614861de4SSascha Wildner parse_args(argc, argv, enumerate);
21732fe4284SNolan Lum
218*1de39603SMatthew Dillon /*
219*1de39603SMatthew Dillon * Don't lose any races, start monitor before scanning devices.
220*1de39603SMatthew Dillon */
221*1de39603SMatthew Dillon if (MonitorOpt) {
222*1de39603SMatthew Dillon mon = udev_monitor_new(ctx);
223*1de39603SMatthew Dillon udev_monitor_enable_receiving(mon);
224*1de39603SMatthew Dillon } else {
225*1de39603SMatthew Dillon mon = NULL;
226*1de39603SMatthew Dillon }
227*1de39603SMatthew Dillon
22832fe4284SNolan Lum ret = udev_enumerate_scan_devices(enumerate);
22932fe4284SNolan Lum if (ret != 0)
23014861de4SSascha Wildner err(EX_UNAVAILABLE, "udev_enumerate_scan_devices ret = %d",
23114861de4SSascha Wildner ret);
23232fe4284SNolan Lum
23332fe4284SNolan Lum current = udev_enumerate_get_list_entry(enumerate);
23432fe4284SNolan Lum if (current == NULL) {
23532fe4284SNolan Lum printf("No devices found.\n");
23632fe4284SNolan Lum } else {
23732fe4284SNolan Lum udev_list_entry_foreach(current, current) {
23832fe4284SNolan Lum dev = udev_list_entry_get_device(current);
23932fe4284SNolan Lum if (dev == NULL)
24032fe4284SNolan Lum continue;
24132fe4284SNolan Lum dict = udev_device_get_dictionary(dev);
24232fe4284SNolan Lum if (dict == NULL)
24332fe4284SNolan Lum continue;
24432fe4284SNolan Lum iter = prop_dictionary_iterator(dict);
24532fe4284SNolan Lum cur_key = NULL;
24632fe4284SNolan Lum
24732fe4284SNolan Lum dev_name = prop_string_cstring(prop_dictionary_get(dict, "name"));
24832fe4284SNolan Lum printf("Device %s:\n", dev_name);
24932fe4284SNolan Lum free(dev_name);
25032fe4284SNolan Lum
25132fe4284SNolan Lum if (!SLIST_EMPTY(&props)) {
25232fe4284SNolan Lum SLIST_FOREACH(ent, &props, entries) {
25314861de4SSascha Wildner key_val = prop_dictionary_get(dict,
25414861de4SSascha Wildner ent->val);
25532fe4284SNolan Lum if (key_val != NULL)
25632fe4284SNolan Lum print_prop(ent->val, key_val);
25732fe4284SNolan Lum }
25832fe4284SNolan Lum } else {
25932fe4284SNolan Lum while ((cur_key = (prop_dictionary_keysym_t)prop_object_iterator_next(iter)) != NULL) {
26032fe4284SNolan Lum key_str = prop_dictionary_keysym_cstring_nocopy(cur_key);
26114861de4SSascha Wildner key_val = prop_dictionary_get_keysym(dict,
26214861de4SSascha Wildner cur_key);
26332fe4284SNolan Lum print_prop(key_str, key_val);
26432fe4284SNolan Lum }
26532fe4284SNolan Lum }
26632fe4284SNolan Lum
26732fe4284SNolan Lum printf("\n");
26832fe4284SNolan Lum }
26932fe4284SNolan Lum }
27032fe4284SNolan Lum
27132fe4284SNolan Lum udev_enumerate_unref(enumerate);
272*1de39603SMatthew Dillon
273*1de39603SMatthew Dillon if (mon) {
274*1de39603SMatthew Dillon while ((dev = udev_monitor_receive_device(mon)) != NULL) {
275*1de39603SMatthew Dillon dict = udev_device_get_dictionary(dev);
276*1de39603SMatthew Dillon if (dict == NULL)
277*1de39603SMatthew Dillon continue;
278*1de39603SMatthew Dillon iter = prop_dictionary_iterator(dict);
279*1de39603SMatthew Dillon cur_key =NULL;
280*1de39603SMatthew Dillon
281*1de39603SMatthew Dillon dev_name = prop_string_cstring(prop_dictionary_get(dict, "name"));
282*1de39603SMatthew Dillon printf("Device %s:\n", dev_name);
283*1de39603SMatthew Dillon free(dev_name);
284*1de39603SMatthew Dillon
285*1de39603SMatthew Dillon if (!SLIST_EMPTY(&props)) {
286*1de39603SMatthew Dillon SLIST_FOREACH(ent, &props, entries) {
287*1de39603SMatthew Dillon key_val = prop_dictionary_get(dict,
288*1de39603SMatthew Dillon ent->val);
289*1de39603SMatthew Dillon if (key_val != NULL)
290*1de39603SMatthew Dillon print_prop(ent->val, key_val);
291*1de39603SMatthew Dillon }
292*1de39603SMatthew Dillon } else {
293*1de39603SMatthew Dillon while ((cur_key = (prop_dictionary_keysym_t)prop_object_iterator_next(iter)) != NULL) {
294*1de39603SMatthew Dillon key_str = prop_dictionary_keysym_cstring_nocopy(cur_key);
295*1de39603SMatthew Dillon key_val = prop_dictionary_get_keysym(dict,
296*1de39603SMatthew Dillon cur_key);
297*1de39603SMatthew Dillon print_prop(key_str, key_val);
298*1de39603SMatthew Dillon }
299*1de39603SMatthew Dillon }
300*1de39603SMatthew Dillon
301*1de39603SMatthew Dillon printf("\n");
302*1de39603SMatthew Dillon }
303*1de39603SMatthew Dillon udev_monitor_unref(mon);
304*1de39603SMatthew Dillon }
305*1de39603SMatthew Dillon
30632fe4284SNolan Lum udev_unref(ctx);
30732fe4284SNolan Lum
30832fe4284SNolan Lum return (0);
30932fe4284SNolan Lum }
310