umbctl.c revision 1.4 1 1.4 khorben /* $NetBSD: umbctl.c,v 1.4 2020/05/13 21:44:30 khorben Exp $ */
2 1.1 khorben /*
3 1.1 khorben * Copyright (c) 2018 Pierre Pronchery <khorben (at) defora.org>
4 1.1 khorben *
5 1.1 khorben * All rights reserved.
6 1.1 khorben *
7 1.1 khorben * Redistribution and use in source and binary forms, with or without
8 1.1 khorben * modification, are permitted provided that the following conditions
9 1.1 khorben * are met:
10 1.1 khorben * 1. Redistributions of source code must retain the above copyright
11 1.1 khorben * notice, this list of conditions and the following disclaimer.
12 1.1 khorben * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 khorben * notice, this list of conditions and the following disclaimer in the
14 1.1 khorben * documentation and/or other materials provided with the distribution.
15 1.1 khorben *
16 1.1 khorben * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
17 1.1 khorben * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 1.1 khorben * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 1.1 khorben * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 1.1 khorben * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 1.1 khorben * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 1.1 khorben * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 1.1 khorben * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 1.1 khorben * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 1.1 khorben * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 1.1 khorben */
27 1.1 khorben
28 1.1 khorben
29 1.1 khorben
30 1.1 khorben #include <sys/endian.h>
31 1.1 khorben #include <sys/ioctl.h>
32 1.1 khorben #include <sys/socket.h>
33 1.1 khorben
34 1.1 khorben #include <net/if.h>
35 1.1 khorben
36 1.1 khorben #include <ctype.h>
37 1.1 khorben #include <errno.h>
38 1.1 khorben #include <stdarg.h>
39 1.1 khorben #include <stdio.h>
40 1.1 khorben #include <string.h>
41 1.1 khorben #include <unistd.h>
42 1.1 khorben
43 1.1 khorben #include <dev/usb/mbim.h>
44 1.1 khorben #include <dev/usb/if_umbreg.h>
45 1.1 khorben
46 1.1 khorben
47 1.1 khorben /* constants */
48 1.1 khorben static const struct umb_valdescr _umb_regstate[] =
49 1.1 khorben MBIM_REGSTATE_DESCRIPTIONS;
50 1.1 khorben
51 1.1 khorben static const struct umb_valdescr _umb_dataclass[] =
52 1.1 khorben MBIM_DATACLASS_DESCRIPTIONS;
53 1.1 khorben
54 1.1 khorben static const struct umb_valdescr _umb_state[] =
55 1.1 khorben UMB_INTERNAL_STATE_DESCRIPTIONS;
56 1.1 khorben
57 1.1 khorben static const struct umb_valdescr _umb_regmode[] =
58 1.1 khorben {
59 1.1 khorben { MBIM_REGMODE_UNKNOWN, "unknown" },
60 1.1 khorben { MBIM_REGMODE_AUTOMATIC, "automatic" },
61 1.1 khorben { MBIM_REGMODE_MANUAL, "manual" },
62 1.1 khorben { 0, NULL }
63 1.1 khorben };
64 1.1 khorben
65 1.1 khorben static const struct umb_valdescr _umb_ber[] =
66 1.1 khorben {
67 1.1 khorben { UMB_BER_EXCELLENT, "excellent" },
68 1.1 khorben { UMB_BER_VERYGOOD, "very good" },
69 1.1 khorben { UMB_BER_GOOD, "good" },
70 1.1 khorben { UMB_BER_OK, "ok" },
71 1.1 khorben { UMB_BER_MEDIUM, "medium" },
72 1.1 khorben { UMB_BER_BAD, "bad" },
73 1.1 khorben { UMB_BER_VERYBAD, "very bad" },
74 1.1 khorben { UMB_BER_EXTREMELYBAD, "extremely bad" },
75 1.1 khorben { 0, NULL }
76 1.1 khorben };
77 1.1 khorben
78 1.1 khorben
79 1.1 khorben /* prototypes */
80 1.1 khorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen);
81 1.1 khorben static int _error(int ret, char const * format, ...);
82 1.1 khorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[]);
83 1.3 khorben static int _umbctl_file(char const * ifname, char const * filename,
84 1.3 khorben int verbose);
85 1.1 khorben static void _umbctl_info(char const * ifname, struct umb_info * umbi);
86 1.1 khorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
87 1.1 khorben struct ifreq * ifr);
88 1.1 khorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
89 1.1 khorben int argc, char * argv[]);
90 1.1 khorben static int _umbctl_socket(void);
91 1.1 khorben static int _usage(void);
92 1.1 khorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen);
93 1.1 khorben
94 1.1 khorben
95 1.1 khorben /* functions */
96 1.1 khorben /* char_to_utf16 */
97 1.1 khorben /* this function is from OpenBSD's ifconfig(8) */
98 1.1 khorben static int _char_to_utf16(const char * in, uint16_t * out, size_t outlen)
99 1.1 khorben {
100 1.1 khorben int n = 0;
101 1.1 khorben uint16_t c;
102 1.1 khorben
103 1.1 khorben for (;;) {
104 1.1 khorben c = *in++;
105 1.1 khorben
106 1.1 khorben if (c == '\0') {
107 1.1 khorben /*
108 1.1 khorben * NUL termination is not required, but zero out the
109 1.1 khorben * residual buffer
110 1.1 khorben */
111 1.1 khorben memset(out, 0, outlen);
112 1.1 khorben return n;
113 1.1 khorben }
114 1.1 khorben if (outlen < sizeof(*out))
115 1.1 khorben return -1;
116 1.1 khorben
117 1.1 khorben *out++ = htole16(c);
118 1.1 khorben n += sizeof(*out);
119 1.1 khorben outlen -= sizeof(*out);
120 1.1 khorben }
121 1.1 khorben }
122 1.1 khorben
123 1.1 khorben
124 1.1 khorben /* error */
125 1.2 roy __printflike(2, 3) static int _error(int ret, char const * format, ...)
126 1.1 khorben {
127 1.1 khorben va_list ap;
128 1.1 khorben
129 1.1 khorben fputs("umbctl: ", stderr);
130 1.1 khorben va_start(ap, format);
131 1.1 khorben vfprintf(stderr, format, ap);
132 1.1 khorben va_end(ap);
133 1.1 khorben fputs("\n", stderr);
134 1.1 khorben return ret;
135 1.1 khorben }
136 1.1 khorben
137 1.1 khorben
138 1.1 khorben /* umbctl */
139 1.1 khorben static int _umbctl(char const * ifname, int verbose, int argc, char * argv[])
140 1.1 khorben {
141 1.1 khorben int fd;
142 1.1 khorben struct ifreq ifr;
143 1.1 khorben struct umb_info umbi;
144 1.1 khorben struct umb_parameter umbp;
145 1.1 khorben
146 1.1 khorben if((fd = _umbctl_socket()) < 0)
147 1.1 khorben return 2;
148 1.1 khorben memset(&ifr, 0, sizeof(ifr));
149 1.1 khorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
150 1.1 khorben if(argc != 0)
151 1.1 khorben {
152 1.1 khorben memset(&umbp, 0, sizeof(umbp));
153 1.1 khorben ifr.ifr_data = &umbp;
154 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
155 1.1 khorben || _umbctl_set(ifname, &umbp, argc, argv) != 0
156 1.1 khorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM,
157 1.1 khorben &ifr) != 0)
158 1.1 khorben {
159 1.1 khorben close(fd);
160 1.1 khorben return 2;
161 1.1 khorben }
162 1.1 khorben }
163 1.1 khorben if(argc == 0 || verbose > 0)
164 1.1 khorben {
165 1.1 khorben ifr.ifr_data = &umbi;
166 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
167 1.1 khorben {
168 1.1 khorben close(fd);
169 1.1 khorben return 3;
170 1.1 khorben }
171 1.1 khorben _umbctl_info(ifname, &umbi);
172 1.1 khorben }
173 1.1 khorben if(close(fd) != 0)
174 1.1 khorben return _error(2, "%s: %s", ifname, strerror(errno));
175 1.1 khorben return 0;
176 1.1 khorben }
177 1.1 khorben
178 1.1 khorben
179 1.1 khorben /* umbctl_file */
180 1.3 khorben static int _umbctl_file(char const * ifname, char const * filename, int verbose)
181 1.1 khorben {
182 1.4 khorben int ret = 0;
183 1.1 khorben int fd;
184 1.1 khorben struct ifreq ifr;
185 1.1 khorben struct umb_info umbi;
186 1.1 khorben struct umb_parameter umbp;
187 1.1 khorben FILE * fp;
188 1.1 khorben char buf[512];
189 1.4 khorben size_t len;
190 1.4 khorben int i;
191 1.1 khorben int eof;
192 1.1 khorben char * tokens[3] = { buf, NULL, NULL };
193 1.1 khorben char * p;
194 1.1 khorben
195 1.1 khorben if((fp = fopen(filename, "r")) == NULL)
196 1.1 khorben return _error(2, "%s: %s", filename, strerror(errno));
197 1.1 khorben memset(&umbp, 0, sizeof(umbp));
198 1.1 khorben while(fgets(buf, sizeof(buf), fp) != NULL)
199 1.1 khorben {
200 1.1 khorben if(buf[0] == '#')
201 1.1 khorben continue;
202 1.1 khorben buf[sizeof(buf) - 1] = '\0';
203 1.4 khorben if((len = strlen(buf)) > 0)
204 1.4 khorben {
205 1.4 khorben if(buf[len - 1] != '\n')
206 1.4 khorben {
207 1.4 khorben ret = _error(2, "%s: %s", filename,
208 1.4 khorben "Line too long");
209 1.4 khorben while((i = fgetc(fp)) != EOF && i != '\n');
210 1.4 khorben continue;
211 1.4 khorben }
212 1.4 khorben else
213 1.4 khorben buf[len - 1] = '\0';
214 1.4 khorben }
215 1.4 khorben if((p = strchr(buf, '=')) != NULL)
216 1.1 khorben {
217 1.1 khorben tokens[1] = p + 1;
218 1.1 khorben *p = '\0';
219 1.1 khorben } else
220 1.1 khorben tokens[1] = NULL;
221 1.4 khorben ret |= _umbctl_set(ifname, &umbp, (p != NULL) ? 2 : 1, tokens)
222 1.4 khorben ? 2 : 0;
223 1.1 khorben }
224 1.1 khorben eof = feof(fp);
225 1.1 khorben if(fclose(fp) != 0 || !eof)
226 1.1 khorben return _error(2, "%s: %s", filename, strerror(errno));
227 1.4 khorben if(ret != 0)
228 1.4 khorben return ret;
229 1.1 khorben if((fd = _umbctl_socket()) < 0)
230 1.1 khorben return 2;
231 1.1 khorben memset(&ifr, 0, sizeof(ifr));
232 1.1 khorben strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
233 1.1 khorben ifr.ifr_data = &umbp;
234 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBPARAM, &ifr) != 0
235 1.1 khorben || _umbctl_ioctl(ifname, fd, SIOCSUMBPARAM, &ifr) != 0)
236 1.1 khorben {
237 1.1 khorben close(fd);
238 1.1 khorben return 2;
239 1.1 khorben }
240 1.1 khorben if(verbose > 0)
241 1.1 khorben {
242 1.1 khorben ifr.ifr_data = &umbi;
243 1.1 khorben if(_umbctl_ioctl(ifname, fd, SIOCGUMBINFO, &ifr) != 0)
244 1.1 khorben {
245 1.1 khorben close(fd);
246 1.1 khorben return 3;
247 1.1 khorben }
248 1.1 khorben _umbctl_info(ifname, &umbi);
249 1.1 khorben }
250 1.1 khorben if(close(fd) != 0)
251 1.1 khorben return _error(2, "%s: %s", ifname, strerror(errno));
252 1.1 khorben return 0;
253 1.1 khorben }
254 1.1 khorben
255 1.1 khorben
256 1.1 khorben /* umbctl_info */
257 1.1 khorben static void _umbctl_info(char const * ifname, struct umb_info * umbi)
258 1.1 khorben {
259 1.1 khorben char provider[UMB_PROVIDERNAME_MAXLEN + 1];
260 1.1 khorben char pn[UMB_PHONENR_MAXLEN + 1];
261 1.1 khorben char roaming[UMB_ROAMINGTEXT_MAXLEN + 1];
262 1.1 khorben char apn[UMB_APN_MAXLEN + 1];
263 1.1 khorben char fwinfo[UMB_FWINFO_MAXLEN + 1];
264 1.1 khorben char hwinfo[UMB_HWINFO_MAXLEN + 1];
265 1.1 khorben
266 1.1 khorben _utf16_to_char(umbi->provider, UMB_PROVIDERNAME_MAXLEN,
267 1.1 khorben provider, sizeof(provider));
268 1.1 khorben _utf16_to_char(umbi->pn, UMB_PHONENR_MAXLEN, pn, sizeof(pn));
269 1.1 khorben _utf16_to_char(umbi->roamingtxt, UMB_ROAMINGTEXT_MAXLEN,
270 1.1 khorben roaming, sizeof(roaming));
271 1.1 khorben _utf16_to_char(umbi->apn, UMB_APN_MAXLEN, apn, sizeof(apn));
272 1.1 khorben _utf16_to_char(umbi->fwinfo, UMB_FWINFO_MAXLEN, fwinfo, sizeof(fwinfo));
273 1.1 khorben _utf16_to_char(umbi->hwinfo, UMB_HWINFO_MAXLEN, hwinfo, sizeof(hwinfo));
274 1.1 khorben printf("%s: state %s, mode %s, registration %s\n"
275 1.1 khorben "\tprovider \"%s\", dataclass %s, signal %s\n"
276 1.1 khorben "\tphone number \"%s\", roaming \"%s\" (%s)\n"
277 1.1 khorben "\tAPN \"%s\", TX %" PRIu64 ", RX %" PRIu64 "\n"
278 1.1 khorben "\tfirmware \"%s\", hardware \"%s\"\n",
279 1.1 khorben ifname, umb_val2descr(_umb_state, umbi->state),
280 1.1 khorben umb_val2descr(_umb_regmode, umbi->regmode),
281 1.1 khorben umb_val2descr(_umb_regstate, umbi->regstate), provider,
282 1.1 khorben umb_val2descr(_umb_dataclass, umbi->cellclass),
283 1.1 khorben umb_val2descr(_umb_ber, umbi->ber), pn, roaming,
284 1.1 khorben umbi->enable_roaming ? "allowed" : "denied",
285 1.1 khorben apn, umbi->uplink_speed, umbi->downlink_speed,
286 1.1 khorben fwinfo, hwinfo);
287 1.1 khorben }
288 1.1 khorben
289 1.1 khorben
290 1.1 khorben /* umbctl_ioctl */
291 1.1 khorben static int _umbctl_ioctl(char const * ifname, int fd, unsigned long request,
292 1.1 khorben struct ifreq * ifr)
293 1.1 khorben {
294 1.1 khorben if(ioctl(fd, request, ifr) != 0)
295 1.1 khorben return _error(-1, "%s: %s", ifname, strerror(errno));
296 1.1 khorben return 0;
297 1.1 khorben }
298 1.1 khorben
299 1.1 khorben
300 1.1 khorben /* umbctl_set */
301 1.1 khorben /* callbacks */
302 1.1 khorben static int _set_apn(char const *, struct umb_parameter *, char const *);
303 1.1 khorben static int _set_username(char const *, struct umb_parameter *, char const *);
304 1.1 khorben static int _set_password(char const *, struct umb_parameter *, char const *);
305 1.1 khorben static int _set_pin(char const *, struct umb_parameter *, char const *);
306 1.1 khorben static int _set_puk(char const *, struct umb_parameter *, char const *);
307 1.1 khorben static int _set_roaming_allow(char const *, struct umb_parameter *,
308 1.1 khorben char const *);
309 1.1 khorben static int _set_roaming_deny(char const *, struct umb_parameter *,
310 1.1 khorben char const *);
311 1.1 khorben
312 1.1 khorben static int _umbctl_set(char const * ifname, struct umb_parameter * umbp,
313 1.1 khorben int argc, char * argv[])
314 1.1 khorben {
315 1.1 khorben struct
316 1.1 khorben {
317 1.1 khorben char const * name;
318 1.1 khorben int (*callback)(char const *,
319 1.1 khorben struct umb_parameter *, char const *);
320 1.1 khorben int parameter;
321 1.1 khorben } callbacks[] =
322 1.1 khorben {
323 1.1 khorben { "apn", _set_apn, 1 },
324 1.1 khorben { "username", _set_username, 1 },
325 1.1 khorben { "password", _set_password, 1 },
326 1.1 khorben { "pin", _set_pin, 1 },
327 1.1 khorben { "puk", _set_puk, 1 },
328 1.1 khorben { "roaming", _set_roaming_allow, 0 },
329 1.1 khorben { "-roaming", _set_roaming_deny, 0 },
330 1.1 khorben };
331 1.1 khorben int i;
332 1.1 khorben size_t j;
333 1.1 khorben
334 1.1 khorben for(i = 0; i < argc; i++)
335 1.1 khorben {
336 1.1 khorben for(j = 0; j < sizeof(callbacks) / sizeof(*callbacks); j++)
337 1.1 khorben if(strcmp(argv[i], callbacks[j].name) == 0)
338 1.1 khorben {
339 1.1 khorben if(callbacks[j].parameter && i + 1 == argc)
340 1.1 khorben return _error(-1, "%s: Incomplete"
341 1.1 khorben " parameter", argv[i]);
342 1.1 khorben if(callbacks[j].callback(ifname, umbp,
343 1.1 khorben callbacks[j].parameter
344 1.1 khorben ? argv[i + 1] : NULL))
345 1.1 khorben return -1;
346 1.1 khorben if(callbacks[j].parameter)
347 1.1 khorben i++;
348 1.1 khorben break;
349 1.1 khorben }
350 1.1 khorben if(j == sizeof(callbacks) / sizeof(*callbacks))
351 1.1 khorben return _error(-1, "%s: Unknown parameter", argv[i]);
352 1.1 khorben }
353 1.1 khorben return 0;
354 1.1 khorben }
355 1.1 khorben
356 1.1 khorben static int _set_apn(char const * ifname, struct umb_parameter * umbp,
357 1.1 khorben char const * apn)
358 1.1 khorben {
359 1.1 khorben umbp->apnlen = _char_to_utf16(apn, umbp->apn, sizeof(umbp->apn));
360 1.1 khorben if(umbp->apnlen < 0 || (size_t)umbp->apnlen > sizeof(umbp->apn))
361 1.1 khorben return _error(-1, "%s: %s", ifname, "APN too long");
362 1.1 khorben return 0;
363 1.1 khorben }
364 1.1 khorben
365 1.1 khorben static int _set_username(char const * ifname, struct umb_parameter * umbp,
366 1.1 khorben char const * username)
367 1.1 khorben {
368 1.1 khorben umbp->usernamelen = _char_to_utf16(username, umbp->username,
369 1.1 khorben sizeof(umbp->username));
370 1.1 khorben if(umbp->usernamelen < 0
371 1.1 khorben || (size_t)umbp->usernamelen > sizeof(umbp->username))
372 1.1 khorben return _error(-1, "%s: %s", ifname, "Username too long");
373 1.1 khorben return 0;
374 1.1 khorben }
375 1.1 khorben
376 1.1 khorben static int _set_password(char const * ifname, struct umb_parameter * umbp,
377 1.1 khorben char const * password)
378 1.1 khorben {
379 1.1 khorben umbp->passwordlen = _char_to_utf16(password, umbp->password,
380 1.1 khorben sizeof(umbp->password));
381 1.1 khorben if(umbp->passwordlen < 0
382 1.1 khorben || (size_t)umbp->passwordlen > sizeof(umbp->password))
383 1.1 khorben return _error(-1, "%s: %s", ifname, "Password too long");
384 1.1 khorben return 0;
385 1.1 khorben }
386 1.1 khorben
387 1.1 khorben static int _set_pin(char const * ifname, struct umb_parameter * umbp,
388 1.1 khorben char const * pin)
389 1.1 khorben {
390 1.1 khorben umbp->is_puk = 0;
391 1.1 khorben umbp->op = MBIM_PIN_OP_ENTER;
392 1.1 khorben umbp->pinlen = _char_to_utf16(pin, umbp->pin, sizeof(umbp->pin));
393 1.1 khorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen
394 1.1 khorben > sizeof(umbp->pin))
395 1.1 khorben return _error(-1, "%s: %s", ifname, "PIN code too long");
396 1.1 khorben return 0;
397 1.1 khorben }
398 1.1 khorben
399 1.1 khorben static int _set_puk(char const * ifname, struct umb_parameter * umbp,
400 1.1 khorben char const * puk)
401 1.1 khorben {
402 1.1 khorben umbp->is_puk = 1;
403 1.1 khorben umbp->op = MBIM_PIN_OP_ENTER;
404 1.1 khorben umbp->pinlen = _char_to_utf16(puk, umbp->pin, sizeof(umbp->pin));
405 1.1 khorben if(umbp->pinlen < 0 || (size_t)umbp->pinlen > sizeof(umbp->pin))
406 1.1 khorben return _error(-1, "%s: %s", ifname, "PUK code too long");
407 1.1 khorben return 0;
408 1.1 khorben }
409 1.1 khorben
410 1.1 khorben static int _set_roaming_allow(char const * ifname, struct umb_parameter * umbp,
411 1.1 khorben char const * unused)
412 1.1 khorben {
413 1.1 khorben (void) ifname;
414 1.1 khorben (void) unused;
415 1.1 khorben
416 1.1 khorben umbp->roaming = 1;
417 1.1 khorben return 0;
418 1.1 khorben }
419 1.1 khorben
420 1.1 khorben static int _set_roaming_deny(char const * ifname, struct umb_parameter * umbp,
421 1.1 khorben char const * unused)
422 1.1 khorben {
423 1.1 khorben (void) ifname;
424 1.1 khorben (void) unused;
425 1.1 khorben
426 1.1 khorben umbp->roaming = 0;
427 1.1 khorben return 0;
428 1.1 khorben }
429 1.1 khorben
430 1.1 khorben
431 1.1 khorben /* umbctl_socket */
432 1.1 khorben static int _umbctl_socket(void)
433 1.1 khorben {
434 1.1 khorben int fd;
435 1.1 khorben
436 1.1 khorben if((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
437 1.1 khorben return _error(-1, "socket: %s", strerror(errno));
438 1.1 khorben return fd;
439 1.1 khorben }
440 1.1 khorben
441 1.1 khorben
442 1.1 khorben /* usage */
443 1.1 khorben static int _usage(void)
444 1.1 khorben {
445 1.3 khorben fputs("Usage: umbctl [-v] ifname [parameter [value]] [...]\n"
446 1.1 khorben " umbctl -f config-file ifname [...]\n",
447 1.1 khorben stderr);
448 1.1 khorben return 1;
449 1.1 khorben }
450 1.1 khorben
451 1.1 khorben
452 1.1 khorben /* utf16_to_char */
453 1.1 khorben static void _utf16_to_char(uint16_t *in, int inlen, char *out, size_t outlen)
454 1.1 khorben {
455 1.1 khorben uint16_t c;
456 1.1 khorben
457 1.1 khorben while (outlen > 0) {
458 1.1 khorben c = inlen > 0 ? htole16(*in) : 0;
459 1.1 khorben if (c == 0 || --outlen == 0) {
460 1.1 khorben /* always NUL terminate result */
461 1.1 khorben *out = '\0';
462 1.1 khorben break;
463 1.1 khorben }
464 1.1 khorben *out++ = isascii(c) ? (char)c : '?';
465 1.1 khorben in++;
466 1.1 khorben inlen--;
467 1.1 khorben }
468 1.1 khorben }
469 1.1 khorben
470 1.1 khorben
471 1.1 khorben /* main */
472 1.1 khorben int main(int argc, char * argv[])
473 1.1 khorben {
474 1.1 khorben int o;
475 1.1 khorben char const * filename = NULL;
476 1.1 khorben int verbose = 0;
477 1.1 khorben
478 1.1 khorben while((o = getopt(argc, argv, "f:v")) != -1)
479 1.1 khorben switch(o)
480 1.1 khorben {
481 1.1 khorben case 'f':
482 1.1 khorben filename = optarg;
483 1.1 khorben break;
484 1.1 khorben case 'v':
485 1.1 khorben verbose++;
486 1.1 khorben break;
487 1.1 khorben default:
488 1.1 khorben return _usage();
489 1.1 khorben }
490 1.1 khorben if(optind == argc)
491 1.1 khorben return _usage();
492 1.1 khorben if(filename != NULL)
493 1.3 khorben {
494 1.3 khorben if(optind + 1 != argc)
495 1.3 khorben return _usage();
496 1.3 khorben return _umbctl_file(argv[optind], filename, verbose);
497 1.3 khorben }
498 1.1 khorben return _umbctl(argv[optind], verbose, argc - optind - 1,
499 1.1 khorben &argv[optind + 1]);
500 1.1 khorben }
501