1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008-2009 Fredrik Lindberg 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 * 27 * $FreeBSD$ 28 */ 29 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/socket.h> 33 #include <sys/sockio.h> 34 #include <sys/select.h> 35 #include <sys/stat.h> 36 #include <sys/sysctl.h> 37 #include <sys/time.h> 38 #include <sys/queue.h> 39 40 #include <arpa/inet.h> 41 #include <net/if.h> 42 #include <net/if_dl.h> 43 #include <net/route.h> 44 #include <netinet/in.h> 45 #include <netinet/in_var.h> 46 47 #include <err.h> 48 #include <errno.h> 49 #include <fcntl.h> 50 #include <termios.h> 51 #include <stdarg.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <stdint.h> 55 #include <string.h> 56 #include <signal.h> 57 #include <syslog.h> 58 #include <unistd.h> 59 #include <ifaddrs.h> 60 #include <libutil.h> 61 #include <time.h> 62 63 /* 64 * Connection utility to ease connectivity using the raw IP packet interface 65 * available on uhso(4) devices. 66 */ 67 68 #define TTY_NAME "/dev/%s" 69 #define SYSCTL_TEST "dev.uhso.%d.%%driver" 70 #define SYSCTL_LOCATION "dev.uhso.%d.%%location" 71 #define SYSCTL_PORTS "dev.uhso.%d.ports" 72 #define SYSCTL_NETIF "dev.uhso.%d.netif" 73 #define SYSCTL_NAME_TTY "dev.uhso.%d.port.%s.tty" 74 #define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc" 75 #define RESOLV_PATH "/etc/resolv.conf" 76 #define PIDFILE "/var/run/uhsoctl.%s.pid" 77 78 static const char *network_access_type[] = { 79 "GSM", 80 "Compact GSM", 81 "UMTS", 82 "GSM (EGPRS)", 83 "HSDPA", 84 "HSUPA", 85 "HSDPA/HSUPA" 86 }; 87 88 static const char *network_reg_status[] = { 89 "Not registered", 90 "Registered", 91 "Searching for network", 92 "Network registration denied", 93 "Unknown", 94 "Registered (roaming)" 95 }; 96 97 struct ctx { 98 int fd; 99 int flags; 100 #define IPASSIGNED 0x01 101 #define FLG_NODAEMON 0x02 /* Don't detach from terminal */ 102 #define FLG_DAEMON 0x04 /* Running as daemon */ 103 #define FLG_DELAYED 0x08 /* Fork into background after connect */ 104 #define FLG_NEWDATA 0x10 105 #define FLG_WATCHDOG 0x20 /* Watchdog enabled */ 106 #define FLG_WDEXP 0x40 /* Watchdog expired */ 107 const char *ifnam; 108 const char *pin; /* device PIN */ 109 110 char pidfile[128]; 111 struct pidfh *pfh; 112 113 time_t watchdog; 114 115 /* PDP context settings */ 116 int pdp_ctx; 117 const char *pdp_apn; 118 const char *pdp_user; 119 const char *pdp_pwd; 120 121 /* Connection status */ 122 int con_status; /* Connected? */ 123 char *con_apn; /* Connected APN */ 124 char *con_oper; /* Operator name */ 125 int con_net_stat; /* Network connection status */ 126 int con_net_type; /* Network connection type */ 127 128 /* Misc. status */ 129 int dbm; 130 131 /* IP and nameserver settings */ 132 struct in_addr ip; 133 char **ns; 134 const char *resolv_path; 135 char *resolv; /* Old resolv.conf */ 136 size_t resolv_sz; 137 }; 138 139 static int readline_buf(const char *, const char *, char *, size_t); 140 static int readline(int, char *, size_t); 141 static void daemonize(struct ctx *); 142 143 static int at_cmd_async(int, const char *, ...); 144 145 typedef union { 146 void *ptr; 147 uint32_t int32; 148 } resp_data; 149 typedef struct { 150 resp_data val[2]; 151 } resp_arg; 152 typedef void (*resp_cb)(resp_arg *, const char *, const char *); 153 154 typedef void (*async_cb)(void *, const char *); 155 struct async_handle { 156 const char *cmd; 157 async_cb func; 158 }; 159 160 static void at_async_creg(void *, const char *); 161 static void at_async_cgreg(void *, const char *); 162 static void at_async_cops(void *, const char *); 163 static void at_async_owancall(void *, const char *); 164 static void at_async_owandata(void *, const char *); 165 static void at_async_csq(void *, const char *); 166 167 static struct async_handle async_cmd[] = { 168 { "+CREG", at_async_creg }, 169 { "+CGREG", at_async_cgreg }, 170 { "+COPS", at_async_cops }, 171 { "+CSQ", at_async_csq }, 172 { "_OWANCALL", at_async_owancall }, 173 { "_OWANDATA", at_async_owandata }, 174 { NULL, NULL } 175 }; 176 177 struct timer_entry; 178 struct timers { 179 TAILQ_HEAD(, timer_entry) head; 180 int res; 181 }; 182 183 typedef void (*tmr_cb)(int, void *); 184 struct timer_entry { 185 TAILQ_ENTRY(timer_entry) next; 186 int id; 187 int timeout; 188 tmr_cb func; 189 void *arg; 190 }; 191 192 193 static struct timers timers; 194 static volatile int running = 1; 195 static int syslog_open = 0; 196 static char syslog_title[64]; 197 198 /* Periodic timer, runs ready timer tasks every tick */ 199 static void 200 tmr_run(struct timers *tmrs) 201 { 202 struct timer_entry *te, *te2; 203 204 te = TAILQ_FIRST(&tmrs->head); 205 if (te == NULL) 206 return; 207 208 te->timeout -= tmrs->res; 209 while (te->timeout <= 0) { 210 te2 = TAILQ_NEXT(te, next); 211 TAILQ_REMOVE(&tmrs->head, te, next); 212 te->func(te->id, te->arg); 213 free(te); 214 te = te2; 215 if (te == NULL) 216 break; 217 } 218 } 219 220 /* Add a new timer */ 221 static void 222 tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg) 223 { 224 struct timer_entry *te, *te2, *te3; 225 226 te = malloc(sizeof(struct timer_entry)); 227 memset(te, 0, sizeof(struct timer_entry)); 228 229 te->timeout = timeout; 230 te->func = func; 231 te->arg = arg; 232 te->id = id; 233 234 te2 = TAILQ_FIRST(&tmrs->head); 235 236 if (TAILQ_EMPTY(&tmrs->head)) { 237 TAILQ_INSERT_HEAD(&tmrs->head, te, next); 238 } else if (te->timeout < te2->timeout) { 239 te2->timeout -= te->timeout; 240 TAILQ_INSERT_HEAD(&tmrs->head, te, next); 241 } else { 242 while (te->timeout >= te2->timeout) { 243 te->timeout -= te2->timeout; 244 te3 = TAILQ_NEXT(te2, next); 245 if (te3 == NULL || te3->timeout > te->timeout) 246 break; 247 te2 = te3; 248 } 249 TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next); 250 } 251 } 252 253 #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG 254 #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG 255 256 static void 257 watchdog_reset(struct ctx *ctx, int timeout) 258 { 259 struct timespec tp; 260 261 clock_gettime(CLOCK_MONOTONIC, &tp), 262 ctx->watchdog = tp.tv_sec + timeout; 263 264 watchdog_enable(ctx); 265 } 266 267 static void 268 tmr_creg(int id, void *arg) 269 { 270 struct ctx *ctx = arg; 271 272 at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 273 watchdog_reset(ctx, 10); 274 } 275 276 static void 277 tmr_cgreg(int id, void *arg) 278 { 279 struct ctx *ctx = arg; 280 281 at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 282 watchdog_reset(ctx, 10); 283 } 284 285 static void 286 tmr_status(int id, void *arg) 287 { 288 struct ctx *ctx = arg; 289 290 at_cmd_async(ctx->fd, "AT+CSQ\r\n"); 291 watchdog_reset(ctx, 10); 292 } 293 294 static void 295 tmr_watchdog(int id, void *arg) 296 { 297 struct ctx *ctx = arg; 298 pid_t self; 299 struct timespec tp; 300 301 tmr_add(&timers, 1, 5, tmr_watchdog, ctx); 302 303 if (!(ctx->flags & FLG_WATCHDOG)) 304 return; 305 306 clock_gettime(CLOCK_MONOTONIC, &tp); 307 308 if (tp.tv_sec >= ctx->watchdog) { 309 #ifdef DEBUG 310 fprintf(stderr, "Watchdog expired\n"); 311 #endif 312 ctx->flags |= FLG_WDEXP; 313 self = getpid(); 314 kill(self, SIGHUP); 315 } 316 } 317 318 static void 319 sig_handle(int sig) 320 { 321 322 switch (sig) { 323 case SIGHUP: 324 case SIGINT: 325 case SIGQUIT: 326 case SIGTERM: 327 running = 0; 328 break; 329 case SIGALRM: 330 tmr_run(&timers); 331 break; 332 } 333 } 334 335 static void 336 logger(int pri, const char *fmt, ...) 337 { 338 char *buf; 339 va_list ap; 340 341 va_start(ap, fmt); 342 vasprintf(&buf, fmt, ap); 343 if (syslog_open) 344 syslog(pri, "%s", buf); 345 else { 346 switch (pri) { 347 case LOG_INFO: 348 case LOG_NOTICE: 349 printf("%s\n", buf); 350 break; 351 default: 352 fprintf(stderr, "%s: %s\n", getprogname(), buf); 353 break; 354 } 355 } 356 357 free(buf); 358 va_end(ap); 359 } 360 361 /* Add/remove IP address from an interface */ 362 static int 363 ifaddr_ad(unsigned long d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 364 { 365 struct ifaliasreq req; 366 int fd, error; 367 368 fd = socket(AF_INET, SOCK_DGRAM, 0); 369 if (fd < 0) 370 return (-1); 371 372 memset(&req, 0, sizeof(struct ifaliasreq)); 373 strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name)); 374 memcpy(&req.ifra_addr, sa, sa->sa_len); 375 memcpy(&req.ifra_mask, mask, mask->sa_len); 376 377 error = ioctl(fd, d, (char *)&req); 378 close(fd); 379 return (error); 380 } 381 382 #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP) 383 #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP) 384 385 static int 386 if_setflags(const char *ifnam, int flags) 387 { 388 struct ifreq ifr; 389 int fd, error; 390 unsigned int oflags = 0; 391 392 memset(&ifr, 0, sizeof(struct ifreq)); 393 strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 394 395 fd = socket(AF_INET, SOCK_DGRAM, 0); 396 if (fd < 0) 397 return (-1); 398 399 error = ioctl(fd, SIOCGIFFLAGS, &ifr); 400 if (error == 0) { 401 oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 402 } 403 404 if (flags < 0) 405 oflags &= ~(-flags); 406 else 407 oflags |= flags; 408 409 ifr.ifr_flags = oflags & 0xffff; 410 ifr.ifr_flagshigh = oflags >> 16; 411 412 error = ioctl(fd, SIOCSIFFLAGS, &ifr); 413 if (error != 0) 414 warn("ioctl SIOCSIFFLAGS"); 415 416 close(fd); 417 return (error); 418 } 419 420 static int 421 ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 422 { 423 int error; 424 425 error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask); 426 if (error != 0) 427 warn("ioctl SIOCAIFADDR"); 428 return (error); 429 } 430 431 static int 432 ifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 433 { 434 int error; 435 436 error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask); 437 if (error != 0) 438 warn("ioctl SIOCDIFADDR"); 439 return (error); 440 } 441 442 static int 443 set_nameservers(struct ctx *ctx, const char *respath, int ns, ...) 444 { 445 int i, n, fd; 446 FILE *fp; 447 char *p; 448 va_list ap; 449 struct stat sb; 450 char buf[512]; 451 452 if (ctx->ns != NULL) { 453 for (i = 0; ctx->ns[i] != NULL; i++) { 454 free(ctx->ns[i]); 455 } 456 free(ctx->ns); 457 ctx->ns = NULL; 458 } 459 460 fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666); 461 if (fd < 0) 462 return (-1); 463 464 if (ns == 0) { 465 /* Attempt to restore old resolv.conf */ 466 if (ctx->resolv != NULL) { 467 ftruncate(fd, 0); 468 lseek(fd, 0, SEEK_SET); 469 write(fd, ctx->resolv, ctx->resolv_sz); 470 free(ctx->resolv); 471 ctx->resolv = NULL; 472 ctx->resolv_sz = 0; 473 } 474 close(fd); 475 return (0); 476 } 477 478 479 ctx->ns = malloc(sizeof(char *) * (ns + 1)); 480 if (ctx->ns == NULL) { 481 close(fd); 482 return (-1); 483 } 484 485 va_start(ap, ns); 486 for (i = 0; i < ns; i++) { 487 p = va_arg(ap, char *); 488 ctx->ns[i] = strdup(p); 489 } 490 ctx->ns[i] = NULL; 491 va_end(ap); 492 493 /* Attempt to backup the old resolv.conf */ 494 if (ctx->resolv == NULL) { 495 i = fstat(fd, &sb); 496 if (i == 0 && sb.st_size != 0) { 497 ctx->resolv_sz = sb.st_size; 498 ctx->resolv = malloc(sb.st_size); 499 if (ctx->resolv != NULL) { 500 n = read(fd, ctx->resolv, sb.st_size); 501 if (n != sb.st_size) { 502 free(ctx->resolv); 503 ctx->resolv = NULL; 504 } 505 } 506 } 507 } 508 509 510 ftruncate(fd, 0); 511 lseek(fd, 0, SEEK_SET); 512 fp = fdopen(fd, "w"); 513 514 /* 515 * Write back everything other than nameserver entries to the 516 * new resolv.conf 517 */ 518 if (ctx->resolv != NULL) { 519 p = ctx->resolv; 520 while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 521 sizeof(buf))) > 0) { 522 p += i; 523 if (strncasecmp(buf, "nameserver", 10) == 0) 524 continue; 525 fprintf(fp, "%s", buf); 526 } 527 } 528 529 for (i = 0; ctx->ns[i] != NULL; i++) { 530 fprintf(fp, "nameserver %s\n", ctx->ns[i]); 531 } 532 fclose(fp); 533 return (0); 534 } 535 536 /* Read a \n-terminated line from buffer */ 537 static int 538 readline_buf(const char *s, const char *e, char *buf, size_t bufsz) 539 { 540 int pos = 0; 541 char *p = buf; 542 543 for (; s < e; s++) { 544 *p = *s; 545 pos++; 546 if (pos >= (bufsz - 1)) 547 break; 548 if (*p++ == '\n') 549 break; 550 } 551 *p = '\0'; 552 return (pos); 553 } 554 555 /* Read a \n-terminated line from file */ 556 static int 557 readline(int fd, char *buf, size_t bufsz) 558 { 559 int n = 0, pos = 0; 560 char *p = buf; 561 562 for (;;) { 563 n = read(fd, p, 1); 564 if (n <= 0) 565 break; 566 pos++; 567 if (pos >= (bufsz - 1)) 568 break; 569 if (*p++ == '\n') 570 break; 571 } 572 *p = '\0'; 573 return (n <= 0 ? n : pos); 574 } 575 576 /* 577 * Synchronous AT command 578 */ 579 static int 580 at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 581 { 582 char buf[512]; 583 char cmd[64]; 584 size_t l; 585 int n, error, retval = 0; 586 va_list ap; 587 fd_set set; 588 char *p; 589 590 va_start(ap, cf); 591 vsnprintf(cmd, sizeof(cmd), cf, ap); 592 va_end(ap); 593 594 #ifdef DEBUG 595 fprintf(stderr, "SYNC_CMD: %s", cmd); 596 #endif 597 598 l = strlen(cmd); 599 n = write(ctx->fd, cmd, l); 600 if (n <= 0) 601 return (-1); 602 603 if (resp != NULL) { 604 l = strlen(resp); 605 #ifdef DEBUG 606 fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l); 607 #endif 608 } 609 610 for (;;) { 611 bzero(buf, sizeof(buf)); 612 613 FD_ZERO(&set); 614 watchdog_reset(ctx, 5); 615 do { 616 FD_SET(ctx->fd, &set); 617 error = select(ctx->fd + 1, &set, NULL, NULL, NULL); 618 if (ctx->flags & FLG_WDEXP) { 619 watchdog_disable(ctx); 620 return (-2); 621 } 622 } while (error <= 0 && errno == EINTR); 623 watchdog_disable(ctx); 624 625 if (error <= 0) { 626 retval = -2; 627 break; 628 } 629 630 n = readline(ctx->fd, buf, sizeof(buf)); 631 if (n <= 0) { 632 retval = -2; 633 break; 634 } 635 636 if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) 637 continue; 638 639 if ((p = strchr(buf, '\r')) != NULL) 640 *p = '\0'; 641 else if ((p = strchr(buf, '\n')) != NULL) 642 *p = '\0'; 643 #ifdef DEBUG 644 fprintf(stderr, "SYNC_RESP: %s\n", buf); 645 #endif 646 647 /* Skip local echo */ 648 if (strncasecmp(cmd, buf, strlen(buf)) == 0) 649 continue; 650 651 if (cb != NULL) 652 cb(ra, cmd, buf); 653 654 if (strncmp(buf, "OK", 2) == 0) { 655 retval = retval ? retval : 0; 656 break; 657 } else if (strstr(buf, "ERROR") != NULL) { 658 retval = -1; 659 break; 660 } 661 if (resp != NULL) 662 retval = strncmp(buf, resp, l); 663 } 664 #ifdef DEBUG 665 fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 666 #endif 667 return (retval); 668 } 669 670 static int 671 at_cmd_async(int fd, const char *cf, ...) 672 { 673 size_t l; 674 va_list ap; 675 char cmd[64]; 676 677 va_start(ap, cf); 678 vsnprintf(cmd, sizeof(cmd), cf, ap); 679 va_end(ap); 680 681 #ifdef DEBUG 682 fprintf(stderr, "CMD: %s", cmd); 683 #endif 684 l = strlen(cmd); 685 return (write(fd, cmd, l)); 686 } 687 688 static void 689 saveresp(resp_arg *ra, const char *cmd, const char *resp) 690 { 691 char **buf; 692 int i = ra->val[1].int32; 693 694 #ifdef DEBUG 695 fprintf(stderr, "Save '%s'\n", resp); 696 #endif 697 698 buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 699 if (buf == NULL) 700 return; 701 702 buf[i] = strdup(resp); 703 704 ra->val[0].ptr = buf; 705 ra->val[1].int32 = i + 1; 706 } 707 708 static void 709 freeresp(resp_arg *ra) 710 { 711 char **buf; 712 int i; 713 714 buf = ra->val[0].ptr; 715 for (i = 0; i < ra->val[1].int32; i++) { 716 free(buf[i]); 717 } 718 free(buf); 719 } 720 721 static void 722 at_async_creg(void *arg, const char *resp) 723 { 724 struct ctx *ctx = arg; 725 int n, reg; 726 727 n = sscanf(resp, "+CREG: %*d,%d", ®); 728 if (n != 1) { 729 n = sscanf(resp, "+CREG: %d", ®); 730 if (n != 1) 731 return; 732 } 733 734 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 735 tmr_add(&timers, 1, 1, tmr_creg, ctx); 736 } 737 else { 738 tmr_add(&timers, 1, 30, tmr_creg, ctx); 739 } 740 741 if (ctx->con_net_stat == reg) 742 return; 743 744 ctx->con_net_stat = reg; 745 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 746 } 747 748 static void 749 at_async_cgreg(void *arg, const char *resp) 750 { 751 struct ctx *ctx = arg; 752 int n, reg; 753 754 n = sscanf(resp, "+CGREG: %*d,%d", ®); 755 if (n != 1) { 756 n = sscanf(resp, "+CGREG: %d", ®); 757 if (n != 1) 758 return; 759 } 760 761 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 762 tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 763 } 764 else { 765 tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 766 } 767 768 if (ctx->con_net_stat == reg) 769 return; 770 771 ctx->con_net_stat = reg; 772 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 773 } 774 775 776 static void 777 at_async_cops(void *arg, const char *resp) 778 { 779 struct ctx *ctx = arg; 780 int n, at; 781 char opr[64]; 782 783 n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 784 opr, &at); 785 if (n != 2) 786 return; 787 788 if (ctx->con_oper != NULL) { 789 if (ctx->con_net_type == at && 790 strcasecmp(opr, ctx->con_oper) == 0) 791 return; 792 free(ctx->con_oper); 793 } 794 795 ctx->con_oper = strdup(opr); 796 ctx->con_net_type = at; 797 798 if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 799 logger(LOG_NOTICE, "%s to \"%s\" (%s)", 800 network_reg_status[ctx->con_net_stat], 801 ctx->con_oper, network_access_type[ctx->con_net_type]); 802 if (ctx->con_status != 1) { 803 at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 804 ctx->pdp_ctx); 805 } 806 } 807 else { 808 logger(LOG_NOTICE, "%s (%s)", 809 network_reg_status[ctx->con_net_stat], 810 network_access_type[ctx->con_net_type]); 811 } 812 } 813 814 /* 815 * Signal strength for pretty console output 816 * 817 * From 3GPP TS 27.007 V8.3.0, Section 8.5 818 * 0 = -113 dBm or less 819 * 1 = -111 dBm 820 * 2...30 = -109...-53 dBm 821 * 31 = -51 dBm or greater 822 * 823 * So, dbm = (rssi * 2) - 113 824 */ 825 static void 826 at_async_csq(void *arg, const char *resp) 827 { 828 struct ctx *ctx = arg; 829 int n, rssi; 830 831 n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 832 if (n != 1) 833 return; 834 if (rssi == 99) 835 ctx->dbm = 0; 836 else { 837 ctx->dbm = (rssi * 2) - 113; 838 tmr_add(&timers, 1, 15, tmr_status, ctx); 839 } 840 841 ctx->flags |= FLG_NEWDATA; 842 } 843 844 static void 845 at_async_owancall(void *arg, const char *resp) 846 { 847 struct ctx *ctx = arg; 848 int n, i; 849 850 n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 851 if (n != 1) 852 return; 853 854 if (i == ctx->con_status) 855 return; 856 857 at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 858 859 ctx->con_status = i; 860 if (ctx->con_status == 1) { 861 logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 862 ctx->con_oper, ctx->con_apn, 863 network_access_type[ctx->con_net_type]); 864 } 865 else { 866 logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 867 ctx->con_oper, ctx->con_apn); 868 } 869 } 870 871 static void 872 at_async_owandata(void *arg, const char *resp) 873 { 874 struct ctx *ctx = arg; 875 char ip[40], ns1[40], ns2[40]; 876 int n, error, rs; 877 struct ifaddrs *ifap, *ifa; 878 struct sockaddr_in sin, mask; 879 struct sockaddr_dl sdl; 880 struct { 881 struct rt_msghdr rtm; 882 char buf[512]; 883 } r; 884 char *cp = r.buf; 885 886 n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 887 ip, ns1, ns2); 888 if (n != 3) 889 return; 890 891 /* XXX: AF_INET assumption */ 892 893 logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 894 895 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 896 memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 897 sin.sin_family = mask.sin_family = AF_INET; 898 899 if (ctx->flags & IPASSIGNED) { 900 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 901 sizeof(sin.sin_addr.s_addr)); 902 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 903 (struct sockaddr *)&mask); 904 } 905 inet_pton(AF_INET, ip, &ctx->ip.s_addr); 906 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 907 sizeof(sin.sin_addr.s_addr)); 908 909 error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 910 (struct sockaddr *)&mask); 911 if (error != 0) { 912 logger(LOG_ERR, "failed to set ip-address"); 913 return; 914 } 915 916 if_ifup(ctx->ifnam); 917 918 ctx->flags |= IPASSIGNED; 919 920 set_nameservers(ctx, ctx->resolv_path, 0); 921 error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 922 if (error != 0) { 923 logger(LOG_ERR, "failed to set nameservers"); 924 } 925 926 error = getifaddrs(&ifap); 927 if (error != 0) { 928 logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 929 return; 930 } 931 932 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 933 if (ifa->ifa_addr->sa_family != AF_LINK) 934 continue; 935 if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 936 memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 937 sizeof(struct sockaddr_dl)); 938 break; 939 } 940 } 941 if (ifa == NULL) 942 return; 943 944 rs = socket(PF_ROUTE, SOCK_RAW, 0); 945 if (rs < 0) { 946 logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 947 return; 948 } 949 950 memset(&r, 0, sizeof(r)); 951 952 r.rtm.rtm_version = RTM_VERSION; 953 r.rtm.rtm_type = RTM_ADD; 954 r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 955 r.rtm.rtm_pid = getpid(); 956 memset(&sin, 0, sizeof(struct sockaddr_in)); 957 sin.sin_family = AF_INET; 958 sin.sin_len = sizeof(struct sockaddr_in); 959 960 memcpy(cp, &sin, sin.sin_len); 961 cp += SA_SIZE(&sin); 962 memcpy(cp, &sdl, sdl.sdl_len); 963 cp += SA_SIZE(&sdl); 964 memcpy(cp, &sin, sin.sin_len); 965 r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 966 r.rtm.rtm_msglen = sizeof(r); 967 968 n = write(rs, &r, r.rtm.rtm_msglen); 969 if (n != r.rtm.rtm_msglen) { 970 r.rtm.rtm_type = RTM_DELETE; 971 n = write(rs, &r, r.rtm.rtm_msglen); 972 r.rtm.rtm_type = RTM_ADD; 973 n = write(rs, &r, r.rtm.rtm_msglen); 974 } 975 976 if (n != r.rtm.rtm_msglen) { 977 logger(LOG_ERR, "failed to set default route: %s", 978 strerror(errno)); 979 } 980 close(rs); 981 982 /* Delayed daemonization */ 983 if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 984 daemonize(ctx); 985 } 986 987 static int 988 at_async(struct ctx *ctx, void *arg) 989 { 990 int n, i; 991 size_t l; 992 char buf[512]; 993 994 watchdog_reset(ctx, 15); 995 996 bzero(buf, sizeof(buf)); 997 n = readline(ctx->fd, buf, sizeof(buf)); 998 if (n <= 0) 999 return (n <= 0 ? -1 : 0); 1000 1001 #ifdef DEBUG 1002 fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 1003 #endif 1004 for (i = 0; async_cmd[i].cmd != NULL; i++) { 1005 l = strlen(async_cmd[i].cmd); 1006 if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 1007 async_cmd[i].func(arg, buf); 1008 } 1009 } 1010 return (0); 1011 } 1012 1013 static const char *port_type_list[] = { 1014 "control", "application", "application2", NULL 1015 }; 1016 1017 /* 1018 * Attempts to find a list of control tty for the interface 1019 * FreeBSD attaches USB devices per interface so we have to go through 1020 * hoops to find which ttys that belong to our network interface. 1021 */ 1022 static char ** 1023 get_tty(struct ctx *ctx) 1024 { 1025 char buf[64], data[128]; 1026 int error, i, usbport, usbport0, list_size = 0; 1027 char **list = NULL; 1028 size_t len; 1029 const char **p, *q; 1030 1031 /* 1032 * Look for the network interface first 1033 */ 1034 for (i = 0; ; i++) { 1035 /* Check if we still have uhso nodes to check */ 1036 snprintf(buf, 64, SYSCTL_TEST, i); 1037 len = 127; 1038 error = sysctlbyname(buf, data, &len, NULL, 0); 1039 data[len] = '\0'; 1040 #ifdef DEBUG 1041 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1042 buf, error, error == 0 ? data : "FAILED"); 1043 #endif 1044 if (error < 0 || strcasecmp(data, "uhso") != 0) 1045 return NULL; 1046 1047 /* Check if this node contains the network interface we want */ 1048 snprintf(buf, 64, SYSCTL_NETIF, i); 1049 len = 127; 1050 error = sysctlbyname(buf, data, &len, NULL, 0); 1051 data[len] = '\0'; 1052 #ifdef DEBUG 1053 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1054 buf, error, error == 0 ? data : "FAILED"); 1055 #endif 1056 if (error == 0 && strcasecmp(data, ctx->ifnam) == 0) 1057 break; 1058 } 1059 1060 /* Figure out the USB port location */ 1061 snprintf(buf, 64, SYSCTL_LOCATION, i); 1062 len = 127; 1063 error = sysctlbyname(buf, data, &len, NULL, 0); 1064 data[len] = '\0'; 1065 #ifdef DEBUG 1066 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1067 buf, error, error == 0 ? data : "FAILED"); 1068 #endif 1069 if (error != 0) 1070 return (NULL); 1071 1072 q = strstr(data, "port="); 1073 if (q != NULL) { 1074 error = sscanf(q, " port=%d", &usbport); 1075 if (error != 1) { 1076 #ifdef DEBUG 1077 fprintf(stderr, "failed to read usb port location from '%s'\n", data); 1078 #endif 1079 return (NULL); 1080 } 1081 } else { 1082 #ifdef DEBUG 1083 fprintf(stderr, "failed to parse location '%s'\n", data); 1084 #endif 1085 return (NULL); 1086 } 1087 #ifdef DEBUG 1088 fprintf(stderr, "USB port location=%d\n", usbport); 1089 #endif 1090 1091 /* 1092 * Now go through it all again but only look at those matching the 1093 * usb port location we found. 1094 */ 1095 for (i = 0; ; i++) { 1096 snprintf(buf, 64, SYSCTL_LOCATION, i); 1097 len = 127; 1098 memset(&data, 0, sizeof(data)); 1099 error = sysctlbyname(buf, data, &len, NULL, 0); 1100 if (error != 0) 1101 break; 1102 data[len] = '\0'; 1103 q = strstr(data, "port="); 1104 if (q == NULL) 1105 continue; 1106 sscanf(q, " port=%d", &usbport0); 1107 if (usbport != usbport0) 1108 continue; 1109 1110 /* Try to add ports */ 1111 for (p = port_type_list; *p != NULL; p++) { 1112 snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1113 len = 127; 1114 memset(&data, 0, sizeof(data)); 1115 error = sysctlbyname(buf, data, &len, NULL, 0); 1116 data[len] = '\0'; 1117 #ifdef DEBUG 1118 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1119 buf, error, error == 0 ? data : "FAILED"); 1120 #endif 1121 if (error == 0) { 1122 list = realloc(list, (list_size + 1) * sizeof(char *)); 1123 list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1124 sprintf(list[list_size], TTY_NAME, data); 1125 list_size++; 1126 } 1127 } 1128 } 1129 list = realloc(list, (list_size + 1) * sizeof(char *)); 1130 list[list_size] = NULL; 1131 return (list); 1132 } 1133 1134 static int 1135 do_connect(struct ctx *ctx, const char *tty) 1136 { 1137 int i, error, needcfg; 1138 resp_arg ra; 1139 struct termios t; 1140 char **buf; 1141 1142 #ifdef DEBUG 1143 fprintf(stderr, "Attempting to open %s\n", tty); 1144 #endif 1145 1146 ctx->fd = open(tty, O_RDWR); 1147 if (ctx->fd < 0) { 1148 #ifdef DEBUG 1149 fprintf(stderr, "Failed to open %s\n", tty); 1150 #endif 1151 return (-1); 1152 } 1153 1154 tcgetattr(ctx->fd, &t); 1155 t.c_oflag = 0; 1156 t.c_iflag = 0; 1157 t.c_cflag = CLOCAL | CREAD; 1158 t.c_lflag = 0; 1159 tcsetattr(ctx->fd, TCSAFLUSH, &t); 1160 1161 error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1162 if (error == -2) { 1163 warnx("failed to read from device %s", tty); 1164 return (-1); 1165 } 1166 1167 /* Check for PIN */ 1168 error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1169 if (error != 0) { 1170 ra.val[0].ptr = NULL; 1171 ra.val[1].int32 = 0; 1172 error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n"); 1173 if (ra.val[1].int32 > 0) { 1174 char *p; 1175 1176 buf = ra.val[0].ptr; 1177 if (strstr(buf[0], "+CME ERROR:") != NULL) { 1178 buf[0] += 12; 1179 errx(1, "%s", buf[0]); 1180 } 1181 freeresp(&ra); 1182 } else 1183 freeresp(&ra); 1184 1185 if (ctx->pin == NULL) { 1186 errx(1, "device requires PIN"); 1187 } 1188 1189 error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1190 ctx->pin); 1191 if (error != 0) { 1192 errx(1, "wrong PIN"); 1193 } 1194 } 1195 1196 /* 1197 * Check if a PDP context has been configured and configure one 1198 * if needed. 1199 */ 1200 ra.val[0].ptr = NULL; 1201 ra.val[1].int32 = 0; 1202 error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1203 buf = ra.val[0].ptr; 1204 needcfg = 1; 1205 for (i = 0; i < ra.val[1].int32; i++) { 1206 char apn[256]; 1207 int cid; 1208 error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1209 &cid, apn); 1210 if (error != 2) { 1211 free(buf[i]); 1212 continue; 1213 } 1214 1215 if (cid == ctx->pdp_ctx) { 1216 ctx->con_apn = strdup(apn); 1217 if (ctx->pdp_apn != NULL) { 1218 if (strcmp(apn, ctx->pdp_apn) == 0) 1219 needcfg = 0; 1220 } 1221 else { 1222 needcfg = 0; 1223 } 1224 } 1225 free(buf[i]); 1226 } 1227 free(buf); 1228 1229 if (needcfg) { 1230 if (ctx->pdp_apn == NULL) 1231 errx(1, "device is not configured and no APN given"); 1232 1233 error = at_cmd(ctx, NULL, NULL, NULL, 1234 "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1235 if (error != 0) { 1236 errx(1, "failed to configure device"); 1237 } 1238 ctx->con_apn = strdup(ctx->pdp_apn); 1239 } 1240 1241 if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1242 at_cmd(ctx, NULL, NULL, NULL, 1243 "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1244 (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1245 (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1246 } 1247 1248 error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1249 ctx->pdp_ctx); 1250 if (error != 0) 1251 return (-1); 1252 1253 at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1254 at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1255 1256 tmr_add(&timers, 1, 5, tmr_status, ctx); 1257 return (0); 1258 } 1259 1260 static void 1261 do_disconnect(struct ctx *ctx) 1262 { 1263 struct sockaddr_in sin, mask; 1264 1265 /* Disconnect */ 1266 at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1267 ctx->pdp_ctx); 1268 close(ctx->fd); 1269 1270 /* Remove ip-address from interface */ 1271 if (ctx->flags & IPASSIGNED) { 1272 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1273 memset(&mask.sin_addr.s_addr, 0xff, 1274 sizeof(mask.sin_addr.s_addr)); 1275 sin.sin_family = mask.sin_family = AF_INET; 1276 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1277 sizeof(sin.sin_addr.s_addr)); 1278 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1279 (struct sockaddr *)&mask); 1280 1281 if_ifdown(ctx->ifnam); 1282 ctx->flags &= ~IPASSIGNED; 1283 } 1284 1285 /* Attempt to reset resolv.conf */ 1286 set_nameservers(ctx, ctx->resolv_path, 0); 1287 } 1288 1289 static void 1290 daemonize(struct ctx *ctx) 1291 { 1292 struct pidfh *pfh; 1293 pid_t opid; 1294 1295 snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1296 1297 pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1298 if (pfh == NULL) { 1299 warn("Cannot create pidfile %s", ctx->pidfile); 1300 return; 1301 } 1302 1303 if (daemon(0, 0) == -1) { 1304 warn("Cannot daemonize"); 1305 pidfile_remove(pfh); 1306 return; 1307 } 1308 1309 pidfile_write(pfh); 1310 ctx->pfh = pfh; 1311 ctx->flags |= FLG_DAEMON; 1312 1313 snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1314 openlog(syslog_title, LOG_PID, LOG_USER); 1315 syslog_open = 1; 1316 } 1317 1318 static void 1319 send_disconnect(const char *ifnam) 1320 { 1321 char pidfile[128]; 1322 FILE *fp; 1323 pid_t pid; 1324 int n; 1325 1326 snprintf(pidfile, 127, PIDFILE, ifnam); 1327 fp = fopen(pidfile, "r"); 1328 if (fp == NULL) { 1329 warn("Cannot open %s", pidfile); 1330 return; 1331 } 1332 1333 n = fscanf(fp, "%d", &pid); 1334 fclose(fp); 1335 if (n != 1) { 1336 warnx("unable to read daemon pid"); 1337 return; 1338 } 1339 #ifdef DEBUG 1340 fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1341 #endif 1342 kill(pid, SIGTERM); 1343 } 1344 1345 static void 1346 usage(const char *exec) 1347 { 1348 1349 printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1350 "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1351 printf("usage %s -d interface\n", exec); 1352 } 1353 1354 enum { 1355 MODE_CONN, 1356 MODE_DISC 1357 }; 1358 1359 int 1360 main(int argc, char *argv[]) 1361 { 1362 int ch, error, mode; 1363 const char *ifnam = NULL; 1364 char *tty = NULL; 1365 char **p, **tty_list; 1366 fd_set set; 1367 struct ctx ctx; 1368 struct itimerval it; 1369 1370 TAILQ_INIT(&timers.head); 1371 timers.res = 1; 1372 1373 ctx.pdp_ctx = 1; 1374 ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1375 ctx.pin = NULL; 1376 1377 ctx.con_status = 0; 1378 ctx.con_apn = NULL; 1379 ctx.con_oper = NULL; 1380 ctx.con_net_stat = 0; 1381 ctx.con_net_type = -1; 1382 ctx.flags = 0; 1383 ctx.resolv_path = RESOLV_PATH; 1384 ctx.resolv = NULL; 1385 ctx.ns = NULL; 1386 ctx.dbm = 0; 1387 1388 mode = MODE_CONN; 1389 ctx.flags |= FLG_DELAYED; 1390 1391 while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1392 switch (ch) { 1393 case 'a': 1394 ctx.pdp_apn = argv[optind - 1]; 1395 break; 1396 case 'c': 1397 ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1398 if (ctx.pdp_ctx < 1) { 1399 warnx("Invalid context ID, defaulting to 1"); 1400 ctx.pdp_ctx = 1; 1401 } 1402 break; 1403 case 'p': 1404 ctx.pin = argv[optind - 1]; 1405 break; 1406 case 'u': 1407 ctx.pdp_user = argv[optind - 1]; 1408 break; 1409 case 'k': 1410 ctx.pdp_pwd = argv[optind - 1]; 1411 break; 1412 case 'r': 1413 ctx.resolv_path = argv[optind - 1]; 1414 break; 1415 case 'd': 1416 mode = MODE_DISC; 1417 break; 1418 case 'b': 1419 ctx.flags &= ~FLG_DELAYED; 1420 break; 1421 case 'n': 1422 ctx.flags |= FLG_NODAEMON; 1423 break; 1424 case 'f': 1425 tty = argv[optind - 1]; 1426 break; 1427 case 'h': 1428 case '?': 1429 default: 1430 usage(argv[0]); 1431 exit(EXIT_SUCCESS); 1432 } 1433 } 1434 1435 argc -= optind; 1436 argv += optind; 1437 1438 if (argc < 1) 1439 errx(1, "no interface given"); 1440 1441 ifnam = argv[argc - 1]; 1442 ctx.ifnam = strdup(ifnam); 1443 1444 switch (mode) { 1445 case MODE_DISC: 1446 printf("Disconnecting %s\n", ifnam); 1447 send_disconnect(ifnam); 1448 exit(EXIT_SUCCESS); 1449 default: 1450 break; 1451 } 1452 1453 signal(SIGHUP, sig_handle); 1454 signal(SIGINT, sig_handle); 1455 signal(SIGQUIT, sig_handle); 1456 signal(SIGTERM, sig_handle); 1457 signal(SIGALRM, sig_handle); 1458 1459 it.it_interval.tv_sec = 1; 1460 it.it_interval.tv_usec = 0; 1461 it.it_value.tv_sec = 1; 1462 it.it_value.tv_usec = 0; 1463 error = setitimer(ITIMER_REAL, &it, NULL); 1464 if (error != 0) 1465 errx(1, "setitimer"); 1466 1467 tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1468 watchdog_reset(&ctx, 15); 1469 1470 if (tty != NULL) { 1471 error = do_connect(&ctx, tty); 1472 if (error != 0) 1473 errx(1, "Failed to open %s", tty); 1474 } 1475 else { 1476 tty_list = get_tty(&ctx); 1477 if (tty_list == NULL) 1478 errx(1, "%s does not appear to be a uhso device", ifnam); 1479 #ifdef DEBUG 1480 if (tty_list == NULL) { 1481 fprintf(stderr, "get_tty returned empty list\n"); 1482 } else { 1483 fprintf(stderr, "tty list:\n"); 1484 for (p = tty_list; *p != NULL; p++) { 1485 fprintf(stderr, "\t %s\n", *p); 1486 } 1487 } 1488 #endif 1489 for (p = tty_list; *p != NULL; p++) { 1490 error = do_connect(&ctx, *p); 1491 if (error == 0) { 1492 tty = *p; 1493 break; 1494 } 1495 } 1496 if (*p == NULL) 1497 errx(1, "Failed to obtain a control port, " 1498 "try specifying one manually"); 1499 } 1500 1501 if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1502 daemonize(&ctx); 1503 1504 1505 FD_ZERO(&set); 1506 FD_SET(ctx.fd, &set); 1507 for (;;) { 1508 1509 watchdog_disable(&ctx); 1510 error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1511 if (error <= 0) { 1512 if (running && errno == EINTR) 1513 continue; 1514 if (ctx.flags & FLG_WDEXP) { 1515 ctx.flags &= ~FLG_WDEXP; 1516 watchdog_reset(&ctx, 5); 1517 do_disconnect(&ctx); 1518 watchdog_reset(&ctx, 15); 1519 do_connect(&ctx, tty); 1520 running = 1; 1521 continue; 1522 } 1523 1524 break; 1525 } 1526 1527 if (FD_ISSET(ctx.fd, &set)) { 1528 watchdog_reset(&ctx, 15); 1529 error = at_async(&ctx, &ctx); 1530 if (error != 0) 1531 break; 1532 } 1533 FD_SET(ctx.fd, &set); 1534 1535 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1536 printf("Status: %s (%s)", 1537 ctx.con_status ? "connected" : "disconnected", 1538 network_access_type[ctx.con_net_type]); 1539 if (ctx.dbm < 0) 1540 printf(", signal: %d dBm", ctx.dbm); 1541 printf("\t\t\t\r"); 1542 fflush(stdout); 1543 } 1544 } 1545 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1546 printf("\n"); 1547 1548 signal(SIGHUP, SIG_DFL); 1549 signal(SIGINT, SIG_DFL); 1550 signal(SIGQUIT, SIG_DFL); 1551 signal(SIGTERM, SIG_DFL); 1552 signal(SIGALRM, SIG_IGN); 1553 1554 do_disconnect(&ctx); 1555 1556 if (ctx.flags & FLG_DAEMON) { 1557 pidfile_remove(ctx.pfh); 1558 if (syslog_open) 1559 closelog(); 1560 } 1561 1562 return (0); 1563 } 1564