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