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