media.c revision 1.1 1 #include <sys/cdefs.h>
2 #ifndef lint
3 __RCSID("$NetBSD: media.c,v 1.1 2008/07/02 07:44:15 dyoung Exp $");
4 #endif /* not lint */
5
6 #include <assert.h>
7 #include <err.h>
8 #include <errno.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <util.h>
13
14 #include <sys/ioctl.h>
15
16 #include <net/if.h>
17 #include <net/if_dl.h>
18 #include <net/if_media.h>
19
20 #include <prop/proplib.h>
21
22 #include "env.h"
23 #include "extern.h"
24 #include "media.h"
25 #include "parse.h"
26 #include "util.h"
27
28 static void init_current_media(prop_dictionary_t, prop_dictionary_t);
29 static void media_constructor(void) __attribute__((constructor));
30 static int setmedia(prop_dictionary_t, prop_dictionary_t);
31 static int setmediainst(prop_dictionary_t, prop_dictionary_t);
32 static int setmediamode(prop_dictionary_t, prop_dictionary_t);
33 static int setmediaopt(prop_dictionary_t, prop_dictionary_t);
34 static int unsetmediaopt(prop_dictionary_t, prop_dictionary_t);
35
36 /*
37 * Media stuff. Whenever a media command is first performed, the
38 * currently select media is grabbed for this interface. If `media'
39 * is given, the current media word is modifed. `mediaopt' commands
40 * only modify the set and clear words. They then operate on the
41 * current media word later.
42 */
43 static int media_current;
44 static int mediaopt_set;
45 static int mediaopt_clear;
46
47 static const int ifm_status_valid_list[] = IFM_STATUS_VALID_LIST;
48
49 static const struct ifmedia_status_description ifm_status_descriptions[] =
50 IFM_STATUS_DESCRIPTIONS;
51
52 static struct pstr mediamode = PSTR_INITIALIZER(&mediamode, "mediamode",
53 setmediamode, "mediamode", &command_root.pb_parser);
54
55 static struct pinteger mediainst = PINTEGER_INITIALIZER1(&mediainst,
56 "mediainst", 0, IFM_INST_MAX, 10, setmediainst, "mediainst",
57 &command_root.pb_parser);
58
59 static struct pstr unmediaopt = PSTR_INITIALIZER(&unmediaopt, "-mediaopt",
60 unsetmediaopt, "unmediaopt", &command_root.pb_parser);
61
62 static struct pstr mediaopt = PSTR_INITIALIZER(&mediaopt, "mediaopt",
63 setmediaopt, "mediaopt", &command_root.pb_parser);
64
65 static struct pstr media = PSTR_INITIALIZER(&media, "media", setmedia, "media",
66 &command_root.pb_parser);
67
68 static const struct kwinst mediakw[] = {
69 {.k_word = "instance", .k_key = "anymedia", .k_type = KW_T_BOOL,
70 .k_bool = true, .k_act = "media", .k_deact = "mediainst",
71 .k_nextparser = &mediainst.pi_parser}
72 , {.k_word = "inst", .k_key = "anymedia", .k_type = KW_T_BOOL,
73 .k_bool = true, .k_act = "media", .k_deact = "mediainst",
74 .k_nextparser = &mediainst.pi_parser}
75 , {.k_word = "media", .k_key = "anymedia", .k_type = KW_T_BOOL,
76 .k_bool = true, .k_deact = "media", .k_altdeact = "anymedia",
77 .k_nextparser = &media.ps_parser}
78 , {.k_word = "mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
79 .k_bool = true, .k_deact = "mediaopt", .k_altdeact = "instance",
80 .k_nextparser = &mediaopt.ps_parser}
81 , {.k_word = "-mediaopt", .k_key = "anymedia", .k_type = KW_T_BOOL,
82 .k_bool = true, .k_deact = "unmediaopt", .k_altdeact = "media",
83 .k_nextparser = &unmediaopt.ps_parser}
84 , {.k_word = "mode", .k_key = "anymedia", .k_type = KW_T_BOOL,
85 .k_bool = true, .k_deact = "mode",
86 .k_nextparser = &mediamode.ps_parser}
87 };
88
89 struct pkw kwmedia = PKW_INITIALIZER(&kwmedia, "media keywords", NULL, NULL,
90 mediakw, __arraycount(mediakw), NULL);
91
92 static void
93 media_error(int type, const char *val, const char *opt)
94 {
95 errx(EXIT_FAILURE, "unknown %s media %s: %s",
96 get_media_type_string(type), opt, val);
97 }
98
99 void
100 init_current_media(prop_dictionary_t env, prop_dictionary_t oenv)
101 {
102 const char *ifname;
103 struct ifmediareq ifmr;
104
105 if ((ifname = getifname(env)) == NULL)
106 err(EXIT_FAILURE, "getifname");
107
108 /*
109 * If we have not yet done so, grab the currently-selected
110 * media.
111 */
112
113 if (prop_dictionary_get(env, "initmedia") == NULL) {
114 memset(&ifmr, 0, sizeof(ifmr));
115
116 if (direct_ioctl(env, SIOCGIFMEDIA, &ifmr) == -1) {
117 /*
118 * If we get E2BIG, the kernel is telling us
119 * that there are more, so we can ignore it.
120 */
121 if (errno != E2BIG)
122 err(EXIT_FAILURE, "SIOCGIFMEDIA");
123 }
124
125 if (!prop_dictionary_set_bool(oenv, "initmedia", true)) {
126 err(EXIT_FAILURE, "%s: prop_dictionary_set_bool",
127 __func__);
128 }
129 media_current = ifmr.ifm_current;
130 }
131
132 /* Sanity. */
133 if (IFM_TYPE(media_current) == 0)
134 errx(EXIT_FAILURE, "%s: no link type?", ifname);
135 }
136
137 void
138 process_media_commands(prop_dictionary_t env)
139 {
140 struct ifreq ifr;
141
142 if (prop_dictionary_get(env, "media") == NULL &&
143 prop_dictionary_get(env, "mediaopt") == NULL &&
144 prop_dictionary_get(env, "unmediaopt") == NULL &&
145 prop_dictionary_get(env, "mediamode") == NULL) {
146 /* Nothing to do. */
147 return;
148 }
149
150 /*
151 * Media already set up, and commands sanity-checked. Set/clear
152 * any options, and we're ready to go.
153 */
154 media_current |= mediaopt_set;
155 media_current &= ~mediaopt_clear;
156
157 memset(&ifr, 0, sizeof(ifr));
158 ifr.ifr_media = media_current;
159
160 if (direct_ioctl(env, SIOCSIFMEDIA, &ifr) == -1)
161 err(EXIT_FAILURE, "SIOCSIFMEDIA");
162 }
163
164 static int
165 setmedia(prop_dictionary_t env, prop_dictionary_t xenv)
166 {
167 int type, subtype, inst;
168 prop_data_t data;
169 char *val;
170
171 init_current_media(env, xenv);
172
173 data = (prop_data_t)prop_dictionary_get(env, "media");
174 assert(data != NULL);
175
176 /* Only one media command may be given. */
177 /* Must not come after mode commands */
178 /* Must not come after mediaopt commands */
179
180 /*
181 * No need to check if `instance' has been issued; setmediainst()
182 * craps out if `media' has not been specified.
183 */
184
185 type = IFM_TYPE(media_current);
186 inst = IFM_INST(media_current);
187
188 val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
189 if (val == NULL)
190 return -1;
191
192 /* Look up the subtype. */
193 subtype = get_media_subtype(type, val);
194 if (subtype == -1)
195 media_error(type, val, "subtype");
196
197 /* Build the new current media word. */
198 media_current = IFM_MAKEWORD(type, subtype, 0, inst);
199
200 /* Media will be set after other processing is complete. */
201 return 0;
202 }
203
204 static int
205 setmediaopt(prop_dictionary_t env, prop_dictionary_t xenv)
206 {
207 char *invalid;
208 prop_data_t data;
209 char *val;
210
211 init_current_media(env, xenv);
212
213 data = (prop_data_t)prop_dictionary_get(env, "mediaopt");
214 assert(data != NULL);
215
216 /* Can only issue `mediaopt' once. */
217 /* Can't issue `mediaopt' if `instance' has already been issued. */
218
219 val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
220 if (val == NULL)
221 return -1;
222
223 mediaopt_set = get_media_options(media_current, val, &invalid);
224 free(val);
225 if (mediaopt_set == -1)
226 media_error(media_current, invalid, "option");
227
228 /* Media will be set after other processing is complete. */
229 return 0;
230 }
231
232 static int
233 unsetmediaopt(prop_dictionary_t env, prop_dictionary_t xenv)
234 {
235 char *invalid, *val;
236 prop_data_t data;
237
238 init_current_media(env, xenv);
239
240 data = (prop_data_t)prop_dictionary_get(env, "unmediaopt");
241 if (data == NULL) {
242 errno = ENOENT;
243 return -1;
244 }
245
246 val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
247 if (val == NULL)
248 return -1;
249
250 /*
251 * No need to check for A_MEDIAINST, since the test for A_MEDIA
252 * implicitly checks for A_MEDIAINST.
253 */
254
255 mediaopt_clear = get_media_options(media_current, val, &invalid);
256 free(val);
257 if (mediaopt_clear == -1)
258 media_error(media_current, invalid, "option");
259
260 /* Media will be set after other processing is complete. */
261 return 0;
262 }
263
264 static int
265 setmediainst(prop_dictionary_t env, prop_dictionary_t xenv)
266 {
267 int type, subtype, options;
268 int64_t inst;
269 bool rc;
270
271 init_current_media(env, xenv);
272
273 rc = prop_dictionary_get_int64(env, "mediainst", &inst);
274 assert(rc);
275
276 /* Can only issue `instance' once. */
277 /* Must have already specified `media' */
278
279 type = IFM_TYPE(media_current);
280 subtype = IFM_SUBTYPE(media_current);
281 options = IFM_OPTIONS(media_current);
282
283 media_current = IFM_MAKEWORD(type, subtype, options, inst);
284
285 /* Media will be set after other processing is complete. */
286 return 0;
287 }
288
289 static int
290 setmediamode(prop_dictionary_t env, prop_dictionary_t xenv)
291 {
292 int type, subtype, options, inst, mode;
293 prop_data_t data;
294 char *val;
295
296 init_current_media(env, xenv);
297
298 data = (prop_data_t)prop_dictionary_get(env, "mediamode");
299 assert(data != NULL);
300
301 type = IFM_TYPE(media_current);
302 subtype = IFM_SUBTYPE(media_current);
303 options = IFM_OPTIONS(media_current);
304 inst = IFM_INST(media_current);
305
306 val = strndup(prop_data_data_nocopy(data), prop_data_size(data));
307 if (val == NULL)
308 return -1;
309
310 mode = get_media_mode(type, val);
311 if (mode == -1)
312 media_error(type, val, "mode");
313
314 free(val);
315
316 media_current = IFM_MAKEWORD(type, subtype, options, inst) | mode;
317
318 /* Media will be set after other processing is complete. */
319 return 0;
320 }
321
322 void
323 print_media_word(int ifmw, const char *opt_sep)
324 {
325 const char *str;
326
327 printf("%s", get_media_subtype_string(ifmw));
328
329 /* Find mode. */
330 if (IFM_MODE(ifmw) != 0) {
331 str = get_media_mode_string(ifmw);
332 if (str != NULL)
333 printf(" mode %s", str);
334 }
335
336 /* Find options. */
337 for (; (str = get_media_option_string(&ifmw)) != NULL; opt_sep = ",")
338 printf("%s%s", opt_sep, str);
339
340 if (IFM_INST(ifmw) != 0)
341 printf(" instance %d", IFM_INST(ifmw));
342 }
343
344 void
345 media_status(prop_dictionary_t env, prop_dictionary_t oenv)
346 {
347 struct ifmediareq ifmr;
348 int af, i, s;
349 int *media_list;
350 const char *ifname;
351
352 if ((ifname = getifname(env)) == NULL)
353 err(EXIT_FAILURE, "getifname");
354 if ((af = getaf(env)) == -1)
355 af = AF_UNSPEC;
356
357 /* get out early if the family is unsupported by the kernel */
358 if ((s = getsock(af)) == -1)
359 err(EXIT_FAILURE, "%s: getsock", __func__);
360
361 memset(&ifmr, 0, sizeof(ifmr));
362 estrlcpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
363
364 if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1) {
365 /*
366 * Interface doesn't support SIOC{G,S}IFMEDIA.
367 */
368 return;
369 }
370
371 if (ifmr.ifm_count == 0) {
372 warnx("%s: no media types?", ifname);
373 return;
374 }
375
376 media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
377 if (media_list == NULL)
378 err(EXIT_FAILURE, "malloc");
379 ifmr.ifm_ulist = media_list;
380
381 if (ioctl(s, SIOCGIFMEDIA, &ifmr) == -1)
382 err(EXIT_FAILURE, "SIOCGIFMEDIA");
383
384 printf("\tmedia: %s ", get_media_type_string(ifmr.ifm_current));
385 print_media_word(ifmr.ifm_current, " ");
386 if (ifmr.ifm_active != ifmr.ifm_current) {
387 printf(" (");
388 print_media_word(ifmr.ifm_active, " ");
389 printf(")");
390 }
391 printf("\n");
392
393 if (ifmr.ifm_status & IFM_STATUS_VALID) {
394 const struct ifmedia_status_description *ifms;
395 int bitno, found = 0;
396
397 printf("\tstatus: ");
398 for (bitno = 0; ifm_status_valid_list[bitno] != 0; bitno++) {
399 for (ifms = ifm_status_descriptions;
400 ifms->ifms_valid != 0; ifms++) {
401 if (ifms->ifms_type !=
402 IFM_TYPE(ifmr.ifm_current) ||
403 ifms->ifms_valid !=
404 ifm_status_valid_list[bitno])
405 continue;
406 printf("%s%s", found ? ", " : "",
407 IFM_STATUS_DESC(ifms, ifmr.ifm_status));
408 found = 1;
409
410 /*
411 * For each valid indicator bit, there's
412 * only one entry for each media type, so
413 * terminate the inner loop now.
414 */
415 break;
416 }
417 }
418
419 if (found == 0)
420 printf("unknown");
421 printf("\n");
422 }
423
424 if (get_flag('m')) {
425 int type, printed_type;
426
427 for (type = IFM_NMIN; type <= IFM_NMAX; type += IFM_NMIN) {
428 for (i = 0, printed_type = 0; i < ifmr.ifm_count; i++) {
429 if (IFM_TYPE(media_list[i]) != type)
430 continue;
431 if (printed_type == 0) {
432 printf("\tsupported %s media:\n",
433 get_media_type_string(type));
434 printed_type = 1;
435 }
436 printf("\t\tmedia ");
437 print_media_word(media_list[i], " mediaopt ");
438 printf("\n");
439 }
440 }
441 }
442
443 free(media_list);
444 }
445
446 static void
447 media_constructor(void)
448 {
449 if (register_flag('m') != 0)
450 err(EXIT_FAILURE, __func__);
451 }
452