xref: /dragonfly/sbin/devattr/devattr.c (revision cd1c6085)
1 /*
2  * Copyright (c) 2010
3  *		The DragonFly Project.	All rights reserved.
4  *
5  * This code is derived from software contributed to The DragonFly Project
6  * by Nolan Lum <nol888@gmail.com>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *	  notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *	  notice, this list of conditions and the following disclaimer in
16  *	  the documentation and/or other materials provided with the
17  *	  distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *	  contributors may be used to endorse or promote products derived
20  *	  from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.	 IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/queue.h>
37 
38 #include <ctype.h>
39 #include <devattr.h>
40 #include <err.h>
41 #include <inttypes.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <sysexits.h>
46 #include <unistd.h>
47 
48 static SLIST_HEAD(, sl_entry) props = SLIST_HEAD_INITIALIZER(props);
49 struct sl_entry {
50 	char *val;
51 	SLIST_ENTRY(sl_entry) entries;
52 } *ent;
53 
54 static int MonitorOpt;
55 
56 static void
57 usage(const char *name)
58 {
59 	fprintf(stderr,
60 	    "usage: %s [-Ah] [-p property] [-d device] [-m key:value] [-r key:value]\n"
61 	    "Valid options are:\n"
62 	    " -A\n"
63 	    "	Don't display aliases.\n"
64 	    " -h\n"
65 	    "	Print this help message.\n\n"
66 	    " -M\n"
67 	    "   Continue monitoring after initial scan.\n\n"
68 	    "Valid options with their arguments are:\n"
69 	    " -p <property>\n"
70 	    "	Only display property; can be specified multiple times and\n"
71 	    "	combined with all other options.\n"
72 	    " -d <device>\n"
73 	    "	Only display devices with name `device'. When used with\n"
74 	    "	-p, only properties `-p' of device `-d' are listed. Can be\n"
75 	    "	specified multiple times. Allows wildcards.\n"
76 	    " -m <key:value>\n"
77 	    "	Only display devices whose property `key' matches with wildcards\n"
78 	    "	value `value' unless the key-value pair starts with ~, in which\n"
79 	    "	case the match is inverted. Stacks with -p, -d, and -m.\n"
80 	    "	Can be specified multiple times.\n"
81 	    " -r <key:value>\n"
82 	    "	Behaves similarly to `-m', but matches with regex.\n",
83 	    name);
84 	exit(EX_USAGE);
85 }
86 
87 static void
88 parse_args(int argc, char *argv[], struct udev_enumerate *enumerate)
89 {
90 	int ch, invert;
91 	char *colon;
92 
93 	SLIST_INIT(&props);
94 
95 	/* A = no aliases */
96 	/* p = properties to list (defaults to all) */
97 	/* d = devices to list (defaults to all) */
98 	/* m = display only devices in d that match these prop values */
99 	/* r = display only devices in d that match these prop values (regex) */
100 	while ((ch = getopt(argc, argv, "AMp:d:m:r:h")) != -1) {
101 		invert = false;
102 
103 		switch (ch) {
104 		case 'A':
105 			udev_enumerate_add_match_property(enumerate, "alias",
106 			    "0");
107 			break;
108 		case 'p':
109 			ent = malloc(sizeof(struct sl_entry));
110 			ent->val = optarg;
111 			SLIST_INSERT_HEAD(&props, ent, entries);
112 			break;
113 		case 'M':
114 			MonitorOpt = 1;
115 			break;
116 		case 'd':
117 			udev_enumerate_add_match_expr(enumerate, "name",
118 			    optarg);
119 			break;
120 		case 'm':
121 		case 'r':
122 			/* Check for exclusion. */
123 			invert = *optarg == '~';
124 
125 			/* Split into key/value. */
126 			colon = strchr(optarg, ':');
127 			if (colon == NULL) {
128 				fprintf(stderr,
129 				    "Invalid property key/value pair `%s'.\n",
130 				    optarg);
131 				return;
132 			}
133 
134 			*colon = '\0';
135 			if (invert) {
136 				if (ch == 'r')
137 					udev_enumerate_add_nomatch_regex(enumerate,
138 					    optarg + 1, colon + 1);
139 				else
140 					udev_enumerate_add_nomatch_expr(enumerate,
141 					    optarg + 1, colon + 1);
142 			} else {
143 				if (ch == 'r')
144 					udev_enumerate_add_match_regex(enumerate,
145 					    optarg, colon + 1);
146 				else
147 					udev_enumerate_add_match_expr(enumerate,
148 					    optarg, colon + 1);
149 			}
150 			break;
151 		case 'h':
152 		default:
153 			usage(argv[0]);
154 		}
155 	}
156 	return;
157 }
158 
159 static void
160 print_prop(const char* key, prop_object_t value)
161 {
162 	char *val_str;
163 
164 	printf("\t%s = ", key);
165 
166 	prop_type_t val_type = prop_object_type(value);
167 	switch (val_type) {
168 	case PROP_TYPE_BOOL:
169 		printf("%s\n", prop_bool_true((prop_bool_t)value) ?
170 		    "true" : "false");
171 		break;
172 	case PROP_TYPE_NUMBER:
173 		if (prop_number_unsigned((prop_number_t)value))
174 			printf("%1$"PRIu64" (0x%1$"PRIx64")\n",
175 			    prop_number_unsigned_integer_value((prop_number_t)value));
176 		else
177 			printf("%"PRId64"\n",
178 			    prop_number_integer_value((prop_number_t)value));
179 		break;
180 	case PROP_TYPE_STRING:
181 		val_str = prop_string_cstring(value);
182 		printf("%s\n", val_str);
183 		free(val_str);
184 		break;
185 	default:
186 		break;
187 	}
188 }
189 
190 int
191 main(int argc, char* argv[])
192 {
193 	struct udev *ctx;
194 	struct udev_enumerate *enumerate;
195 	struct udev_monitor *mon;
196 	struct udev_list_entry *current;
197 	struct udev_device *dev;
198 	prop_object_t key_val;
199 	prop_dictionary_t dict;
200 	prop_dictionary_keysym_t cur_key;
201 	prop_object_iterator_t iter;
202 	const char *key_str;
203 	char *dev_name;
204 	int ret;
205 
206 	ctx = udev_new();
207 	if (ctx == NULL) {
208 		fprintf(stderr, "Make sure udevd is running\n");
209 		err(EX_UNAVAILABLE, "udev_new");
210 	}
211 
212 	enumerate = udev_enumerate_new(ctx);
213 	if (enumerate == NULL)
214 		err(EX_UNAVAILABLE, "udev_enumerate_new");
215 
216 	parse_args(argc, argv, enumerate);
217 
218 	/*
219 	 * Don't lose any races, start monitor before scanning devices.
220 	 */
221 	if (MonitorOpt) {
222 		mon = udev_monitor_new(ctx);
223 		udev_monitor_enable_receiving(mon);
224 	} else {
225 		mon = NULL;
226 	}
227 
228 	ret = udev_enumerate_scan_devices(enumerate);
229 	if (ret != 0)
230 		err(EX_UNAVAILABLE, "udev_enumerate_scan_devices ret = %d",
231 		    ret);
232 
233 	current = udev_enumerate_get_list_entry(enumerate);
234 	if (current == NULL) {
235 		printf("No devices found.\n");
236 	} else {
237 		udev_list_entry_foreach(current, current) {
238 			dev = udev_list_entry_get_device(current);
239 			if (dev == NULL)
240 				continue;
241 			dict = udev_device_get_dictionary(dev);
242 			if (dict == NULL)
243 				continue;
244 			iter = prop_dictionary_iterator(dict);
245 			cur_key = NULL;
246 
247 			dev_name = prop_string_cstring(prop_dictionary_get(dict, "name"));
248 			printf("Device %s:\n", dev_name);
249 			free(dev_name);
250 
251 			if (!SLIST_EMPTY(&props)) {
252 				SLIST_FOREACH(ent, &props, entries) {
253 					key_val = prop_dictionary_get(dict,
254 					    ent->val);
255 					if (key_val != NULL)
256 						print_prop(ent->val, key_val);
257 				}
258 			} else {
259 				while ((cur_key = (prop_dictionary_keysym_t)prop_object_iterator_next(iter)) != NULL) {
260 					key_str = prop_dictionary_keysym_cstring_nocopy(cur_key);
261 					key_val = prop_dictionary_get_keysym(dict,
262 					    cur_key);
263 					print_prop(key_str, key_val);
264 				}
265 			}
266 
267 			printf("\n");
268 		}
269 	}
270 
271 	udev_enumerate_unref(enumerate);
272 
273 	if (mon) {
274 		while ((dev = udev_monitor_receive_device(mon)) != NULL) {
275 			dict = udev_device_get_dictionary(dev);
276 			if (dict == NULL)
277 				continue;
278 			iter = prop_dictionary_iterator(dict);
279 			cur_key =NULL;
280 
281 			dev_name = prop_string_cstring(prop_dictionary_get(dict, "name"));
282 			printf("Device %s:\n", dev_name);
283 			free(dev_name);
284 
285 			if (!SLIST_EMPTY(&props)) {
286 				SLIST_FOREACH(ent, &props, entries) {
287 					key_val = prop_dictionary_get(dict,
288 					    ent->val);
289 					if (key_val != NULL)
290 						print_prop(ent->val, key_val);
291 				}
292 			} else {
293 				while ((cur_key = (prop_dictionary_keysym_t)prop_object_iterator_next(iter)) != NULL) {
294 					key_str = prop_dictionary_keysym_cstring_nocopy(cur_key);
295 					key_val = prop_dictionary_get_keysym(dict,
296 					    cur_key);
297 					print_prop(key_str, key_val);
298 				}
299 			}
300 
301 			printf("\n");
302 		}
303 		udev_monitor_unref(mon);
304 	}
305 
306 	udev_unref(ctx);
307 
308 	return (0);
309 }
310