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_var.h> 41 #include <net/if_dl.h> 42 #include <net/route.h> 43 #include <netinet/in.h> 44 #include <netinet/in_var.h> 45 46 #include <err.h> 47 #include <errno.h> 48 #include <fcntl.h> 49 #include <termios.h> 50 #include <stdarg.h> 51 #include <stdio.h> 52 #include <stdlib.h> 53 #include <stdint.h> 54 #include <string.h> 55 #include <signal.h> 56 #include <syslog.h> 57 #include <unistd.h> 58 #include <ifaddrs.h> 59 #include <libutil.h> 60 #include <time.h> 61 62 /* 63 * Connection utility to ease connectivity using the raw IP packet interface 64 * available on uhso(4) devices. 65 */ 66 67 #define TTY_NAME "/dev/%s" 68 #define SYSCTL_TEST "dev.uhso.%d.%%driver" 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 if (te2 != NULL) 211 te2->timeout -= tmrs->res; 212 213 te->func(te->id, te->arg); 214 free(te); 215 te = te2; 216 if (te == NULL) 217 break; 218 } 219 } 220 221 /* Add a new timer */ 222 static void 223 tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg) 224 { 225 struct timer_entry *te, *te2, *te3; 226 227 te = malloc(sizeof(struct timer_entry)); 228 memset(te, 0, sizeof(struct timer_entry)); 229 230 te->timeout = timeout; 231 te->func = func; 232 te->arg = arg; 233 te->id = id; 234 235 te2 = TAILQ_FIRST(&tmrs->head); 236 237 if (TAILQ_EMPTY(&tmrs->head)) { 238 TAILQ_INSERT_HEAD(&tmrs->head, te, next); 239 } else if (te->timeout < te2->timeout) { 240 te2->timeout -= te->timeout; 241 TAILQ_INSERT_HEAD(&tmrs->head, te, next); 242 } else { 243 while (te->timeout >= te2->timeout) { 244 te->timeout -= te2->timeout; 245 te3 = TAILQ_NEXT(te2, next); 246 if (te3 == NULL || te3->timeout > te->timeout) 247 break; 248 te2 = te3; 249 } 250 TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next); 251 } 252 } 253 254 #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG 255 #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG 256 257 static void 258 watchdog_reset(struct ctx *ctx, int timeout) 259 { 260 struct timespec tp; 261 262 clock_gettime(CLOCK_MONOTONIC, &tp), 263 ctx->watchdog = tp.tv_sec + timeout; 264 265 watchdog_enable(ctx); 266 } 267 268 static void 269 tmr_creg(int id, void *arg) 270 { 271 struct ctx *ctx = arg; 272 273 at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 274 watchdog_reset(ctx, 10); 275 } 276 277 static void 278 tmr_cgreg(int id, void *arg) 279 { 280 struct ctx *ctx = arg; 281 282 at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 283 watchdog_reset(ctx, 10); 284 } 285 286 static void 287 tmr_status(int id, void *arg) 288 { 289 struct ctx *ctx = arg; 290 291 at_cmd_async(ctx->fd, "AT+CSQ\r\n"); 292 watchdog_reset(ctx, 10); 293 } 294 295 static void 296 tmr_watchdog(int id, void *arg) 297 { 298 struct ctx *ctx = arg; 299 pid_t self; 300 struct timespec tp; 301 302 tmr_add(&timers, 1, 5, tmr_watchdog, ctx); 303 304 if (!(ctx->flags & FLG_WATCHDOG)) 305 return; 306 307 clock_gettime(CLOCK_MONOTONIC, &tp); 308 309 if (tp.tv_sec >= ctx->watchdog) { 310 #ifdef DEBUG 311 fprintf(stderr, "Watchdog expired\n"); 312 #endif 313 ctx->flags |= FLG_WDEXP; 314 self = getpid(); 315 kill(self, SIGHUP); 316 } 317 } 318 319 static void 320 sig_handle(int sig) 321 { 322 323 switch (sig) { 324 case SIGHUP: 325 case SIGINT: 326 case SIGQUIT: 327 case SIGTERM: 328 running = 0; 329 break; 330 case SIGALRM: 331 tmr_run(&timers); 332 break; 333 } 334 } 335 336 static void 337 logger(int pri, const char *fmt, ...) 338 { 339 char *buf; 340 va_list ap; 341 342 va_start(ap, fmt); 343 vasprintf(&buf, fmt, ap); 344 if (syslog_open) 345 syslog(pri, buf); 346 else { 347 switch (pri) { 348 case LOG_INFO: 349 case LOG_NOTICE: 350 printf("%s\n", buf); 351 break; 352 default: 353 fprintf(stderr, "%s: %s\n", getprogname(), buf); 354 break; 355 } 356 } 357 358 free(buf); 359 va_end(ap); 360 } 361 362 /* Add/remove IP address from an interface */ 363 static int 364 ifaddr_ad(int d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 365 { 366 struct ifaliasreq req; 367 int fd, error; 368 369 fd = socket(AF_INET, SOCK_DGRAM, 0); 370 if (fd < 0) 371 return (-1); 372 373 memset(&req, 0, sizeof(struct ifaliasreq)); 374 strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name)); 375 memcpy(&req.ifra_addr, sa, sa->sa_len); 376 memcpy(&req.ifra_mask, mask, mask->sa_len); 377 378 error = ioctl(fd, d, (char *)&req); 379 close(fd); 380 return (error); 381 } 382 383 #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP) 384 #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP) 385 386 static int 387 if_setflags(const char *ifnam, int flags) 388 { 389 struct ifreq ifr; 390 int fd, error; 391 unsigned int oflags = 0; 392 393 memset(&ifr, 0, sizeof(struct ifreq)); 394 strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 395 396 fd = socket(AF_INET, SOCK_DGRAM, 0); 397 if (fd < 0) 398 return (-1); 399 400 error = ioctl(fd, SIOCGIFFLAGS, &ifr); 401 if (error == 0) { 402 oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 403 } 404 405 if (flags < 0) 406 oflags &= ~(-flags); 407 else 408 oflags |= flags; 409 410 ifr.ifr_flags = oflags & 0xffff; 411 ifr.ifr_flagshigh = oflags >> 16; 412 413 error = ioctl(fd, SIOCSIFFLAGS, &ifr); 414 if (error != 0) 415 warn("ioctl SIOCSIFFLAGS"); 416 417 close(fd); 418 return (error); 419 } 420 421 static int 422 ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 423 { 424 int error; 425 426 error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask); 427 if (error != 0) 428 warn("ioctl SIOCAIFADDR"); 429 return (error); 430 } 431 432 static int 433 ifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 434 { 435 int error; 436 437 error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask); 438 if (error != 0) 439 warn("ioctl SIOCDIFADDR"); 440 return (error); 441 } 442 443 static int 444 set_nameservers(struct ctx *ctx, const char *respath, int ns, ...) 445 { 446 int i, n, fd; 447 FILE *fp; 448 char *p; 449 va_list ap; 450 struct stat sb; 451 char buf[512]; 452 453 if (ctx->ns != NULL) { 454 for (i = 0; ctx->ns[i] != NULL; i++) { 455 free(ctx->ns[i]); 456 } 457 free(ctx->ns); 458 } 459 460 fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW); 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 size_t l; 583 int n, error, retval = 0; 584 va_list ap; 585 fd_set set; 586 char buf[512]; 587 char cmd[64]; 588 589 va_start(ap, cf); 590 vsnprintf(cmd, sizeof(cmd), cf, ap); 591 va_end(ap); 592 593 #ifdef DEBUG 594 fprintf(stderr, "SYNC_CMD: %s", cmd); 595 #endif 596 597 l = strlen(cmd); 598 n = write(ctx->fd, cmd, l); 599 if (n <= 0) 600 return (-1); 601 602 if (resp != NULL) { 603 l = strlen(resp); 604 #ifdef DEBUG 605 fprintf(stderr, "SYNC_EXP: %s (%d)\n", resp, l); 606 #endif 607 } 608 609 for (;;) { 610 bzero(buf, sizeof(buf)); 611 612 FD_ZERO(&set); 613 watchdog_reset(ctx, 5); 614 do { 615 FD_SET(ctx->fd, &set); 616 error = select(ctx->fd + 1, &set, NULL, NULL, NULL); 617 if (error < 0 && errno == EINTR && ctx->flags & FLG_WDEXP) { 618 watchdog_disable(ctx); 619 retval = -2; 620 break; 621 } 622 } while (error <= 0 && errno == EINTR); 623 624 if (error <= 0) { 625 retval = -2; 626 break; 627 } 628 629 n = readline(ctx->fd, buf, sizeof(buf)); 630 if (n <= 0) { 631 retval = -2; 632 break; 633 } 634 635 if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) 636 continue; 637 638 #ifdef DEBUG 639 fprintf(stderr, "SYNC_RESP: %s", buf); 640 #endif 641 642 if (strncmp(buf, "OK", 2) == 0) { 643 break; 644 } 645 else if (strncmp(buf, "ERROR", 5) == 0) { 646 retval = -1; 647 break; 648 } 649 650 if (resp != NULL) { 651 retval = strncmp(resp, buf, l); 652 if (retval == 0 && cb != NULL) { 653 cb(ra, cmd, buf); 654 } 655 } 656 } 657 #ifdef DEBUG 658 fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 659 #endif 660 return (retval); 661 } 662 663 static int 664 at_cmd_async(int fd, const char *cf, ...) 665 { 666 size_t l; 667 va_list ap; 668 char cmd[64]; 669 670 va_start(ap, cf); 671 vsnprintf(cmd, sizeof(cmd), cf, ap); 672 va_end(ap); 673 674 #ifdef DEBUG 675 fprintf(stderr, "CMD: %s", cmd); 676 #endif 677 l = strlen(cmd); 678 return (write(fd, cmd, l)); 679 } 680 681 static void 682 saveresp(resp_arg *ra, const char *cmd, const char *resp) 683 { 684 char **buf; 685 int i = ra->val[1].int32; 686 687 buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 688 if (buf == NULL) 689 return; 690 691 buf[i] = strdup(resp); 692 693 ra->val[0].ptr = buf; 694 ra->val[1].int32 = i + 1; 695 } 696 697 static void 698 at_async_creg(void *arg, const char *resp) 699 { 700 struct ctx *ctx = arg; 701 int n, reg; 702 703 n = sscanf(resp, "+CREG: %*d,%d", ®); 704 if (n != 1) { 705 n = sscanf(resp, "+CREG: %d", ®); 706 if (n != 1) 707 return; 708 } 709 710 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 711 tmr_add(&timers, 1, 1, tmr_creg, ctx); 712 } 713 else { 714 tmr_add(&timers, 1, 30, tmr_creg, ctx); 715 } 716 717 if (ctx->con_net_stat == reg) 718 return; 719 720 ctx->con_net_stat = reg; 721 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 722 } 723 724 static void 725 at_async_cgreg(void *arg, const char *resp) 726 { 727 struct ctx *ctx = arg; 728 int n, reg; 729 730 n = sscanf(resp, "+CGREG: %*d,%d", ®); 731 if (n != 1) { 732 n = sscanf(resp, "+CGREG: %d", ®); 733 if (n != 1) 734 return; 735 } 736 737 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 738 tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 739 } 740 else { 741 tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 742 } 743 744 if (ctx->con_net_stat == reg) 745 return; 746 747 ctx->con_net_stat = reg; 748 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 749 } 750 751 752 static void 753 at_async_cops(void *arg, const char *resp) 754 { 755 struct ctx *ctx = arg; 756 int n, at; 757 char opr[64]; 758 759 n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 760 opr, &at); 761 if (n != 2) 762 return; 763 764 if (ctx->con_oper != NULL) { 765 if (ctx->con_net_type == at && 766 strcasecmp(opr, ctx->con_oper) == 0) 767 return; 768 free(ctx->con_oper); 769 } 770 771 ctx->con_oper = strdup(opr); 772 ctx->con_net_type = at; 773 774 if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 775 logger(LOG_NOTICE, "%s to \"%s\" (%s)", 776 network_reg_status[ctx->con_net_stat], 777 ctx->con_oper, network_access_type[ctx->con_net_type]); 778 if (ctx->con_status != 1) { 779 at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 780 ctx->pdp_ctx); 781 } 782 } 783 else { 784 logger(LOG_NOTICE, "%s (%s)", 785 network_reg_status[ctx->con_net_stat], 786 network_access_type[ctx->con_net_type]); 787 } 788 } 789 790 /* 791 * Signal strength for pretty console output 792 * 793 * From 3GPP TS 27.007 V8.3.0, Section 8.5 794 * 0 = -113 dBm or less 795 * 1 = -111 dBm 796 * 2...30 = -109...-53 dBm 797 * 31 = -51 dBm or greater 798 * 799 * So, dbm = (rssi * 2) - 113 800 */ 801 static void 802 at_async_csq(void *arg, const char *resp) 803 { 804 struct ctx *ctx = arg; 805 int n, rssi; 806 807 n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 808 if (n != 1) 809 return; 810 if (rssi == 99) 811 ctx->dbm = 0; 812 else { 813 ctx->dbm = (rssi * 2) - 113; 814 tmr_add(&timers, 1, 15, tmr_status, ctx); 815 } 816 817 ctx->flags |= FLG_NEWDATA; 818 } 819 820 static void 821 at_async_owancall(void *arg, const char *resp) 822 { 823 struct ctx *ctx = arg; 824 int n, i; 825 826 n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 827 if (n != 1) 828 return; 829 830 if (i == ctx->con_status) 831 return; 832 833 at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 834 835 ctx->con_status = i; 836 if (ctx->con_status == 1) { 837 logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 838 ctx->con_oper, ctx->con_apn, 839 network_access_type[ctx->con_net_type]); 840 } 841 else { 842 logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 843 ctx->con_oper, ctx->con_apn); 844 } 845 } 846 847 static void 848 at_async_owandata(void *arg, const char *resp) 849 { 850 struct ctx *ctx = arg; 851 char ip[40], ns1[40], ns2[40]; 852 int n, error, rs; 853 struct ifaddrs *ifap, *ifa; 854 struct sockaddr_in sin, mask; 855 struct sockaddr_dl sdl; 856 struct { 857 struct rt_msghdr rtm; 858 char buf[512]; 859 } r; 860 char *cp = r.buf; 861 862 n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 863 ip, ns1, ns2); 864 if (n != 3) 865 return; 866 867 /* XXX: AF_INET assumption */ 868 869 logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 870 871 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 872 memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 873 sin.sin_family = mask.sin_family = AF_INET; 874 875 if (ctx->flags & IPASSIGNED) { 876 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 877 sizeof(sin.sin_addr.s_addr)); 878 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 879 (struct sockaddr *)&mask); 880 } 881 inet_pton(AF_INET, ip, &ctx->ip.s_addr); 882 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 883 sizeof(sin.sin_addr.s_addr)); 884 885 error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 886 (struct sockaddr *)&mask); 887 if (error != 0) { 888 logger(LOG_ERR, "failed to set ip-address"); 889 return; 890 } 891 892 if_ifup(ctx->ifnam); 893 894 ctx->flags |= IPASSIGNED; 895 896 set_nameservers(ctx, ctx->resolv_path, 0); 897 error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 898 if (error != 0) { 899 logger(LOG_ERR, "failed to set nameservers"); 900 } 901 902 error = getifaddrs(&ifap); 903 if (error != 0) { 904 logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 905 return; 906 } 907 908 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 909 if (ifa->ifa_addr->sa_family != AF_LINK) 910 continue; 911 if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 912 memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 913 sizeof(struct sockaddr_dl)); 914 break; 915 } 916 } 917 if (ifa == NULL) 918 return; 919 920 rs = socket(PF_ROUTE, SOCK_RAW, 0); 921 if (rs < 0) { 922 logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 923 return; 924 } 925 926 memset(&r, 0, sizeof(r)); 927 928 r.rtm.rtm_version = RTM_VERSION; 929 r.rtm.rtm_type = RTM_ADD; 930 r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 931 r.rtm.rtm_pid = getpid(); 932 memset(&sin, 0, sizeof(struct sockaddr_in)); 933 sin.sin_family = AF_INET; 934 sin.sin_len = sizeof(struct sockaddr_in); 935 936 memcpy(cp, &sin, sin.sin_len); 937 cp += SA_SIZE(&sin); 938 memcpy(cp, &sdl, sdl.sdl_len); 939 cp += SA_SIZE(&sdl); 940 memcpy(cp, &sin, sin.sin_len); 941 r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 942 r.rtm.rtm_msglen = sizeof(r); 943 944 n = write(rs, &r, r.rtm.rtm_msglen); 945 if (n != r.rtm.rtm_msglen) { 946 r.rtm.rtm_type = RTM_DELETE; 947 n = write(rs, &r, r.rtm.rtm_msglen); 948 r.rtm.rtm_type = RTM_ADD; 949 n = write(rs, &r, r.rtm.rtm_msglen); 950 } 951 952 if (n != r.rtm.rtm_msglen) { 953 logger(LOG_ERR, "failed to set default route: %s", 954 strerror(errno)); 955 } 956 close(rs); 957 958 /* Delayed daemonization */ 959 if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 960 daemonize(ctx); 961 } 962 963 static int 964 at_async(struct ctx *ctx, void *arg) 965 { 966 int n, i; 967 size_t l; 968 char buf[512]; 969 970 watchdog_reset(ctx, 15); 971 972 bzero(buf, sizeof(buf)); 973 n = readline(ctx->fd, buf, sizeof(buf)); 974 if (n <= 0) 975 return (n <= 0 ? -1 : 0); 976 977 #ifdef DEBUG 978 fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 979 #endif 980 for (i = 0; async_cmd[i].cmd != NULL; i++) { 981 l = strlen(async_cmd[i].cmd); 982 if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 983 async_cmd[i].func(arg, buf); 984 } 985 } 986 return (0); 987 } 988 989 static const char *port_type_list[] = { 990 "control", "application", "application2", NULL 991 }; 992 993 /* 994 * Attempts to find a list of control tty for the interface 995 * FreeBSD attaches USb devices per interface so we have to go through 996 * hoops to find which ttys that belong to our network interface. 997 */ 998 static char ** 999 get_tty(struct ctx *ctx) 1000 { 1001 char buf[64]; 1002 char data[128]; 1003 size_t len; 1004 int error; 1005 unsigned int i; 1006 char **list = NULL; 1007 int list_size = 0; 1008 const char **p; 1009 1010 for (i = 0; ; i++) { 1011 /* Basic test to check if we're even in the right ballpark */ 1012 snprintf(buf, 64, SYSCTL_TEST, i); 1013 len = 127; 1014 error = sysctlbyname(buf, data, &len, NULL, 0); 1015 #ifdef DEBUG 1016 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1017 buf, error, error == 0 ? data : "FAILED"); 1018 #endif 1019 if (error < 0) 1020 return NULL; 1021 if (strcasecmp(data, "uhso") != 0) 1022 return NULL; 1023 1024 /* Check for interface */ 1025 snprintf(buf, 64, SYSCTL_NETIF, i); 1026 len = 127; 1027 error = sysctlbyname(buf, data, &len, NULL, 0); 1028 #ifdef DEBUG 1029 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1030 buf, error, error == 0 ? data : "FAILED"); 1031 #endif 1032 if (error < 0) 1033 continue; 1034 1035 if (strcasecmp(data, ctx->ifnam) != 0) 1036 continue; 1037 #ifdef DEBUG 1038 fprintf(stderr, "Found %s at %s\n", ctx->ifnam, buf); 1039 #endif 1040 break; 1041 } 1042 1043 /* Add multiplexed ports */ 1044 for (p = port_type_list; *p != NULL; p++) { 1045 snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1046 len = 127; 1047 error = sysctlbyname(buf, data, &len, NULL, 0); 1048 #ifdef DEBUG 1049 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1050 buf, error, error == 0 ? data : "FAILED"); 1051 #endif 1052 if (error == 0) { 1053 list = realloc(list, (list_size + 1) * sizeof(char *)); 1054 list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1055 sprintf(list[list_size], TTY_NAME, data); 1056 list_size++; 1057 } 1058 } 1059 1060 /* 1061 * We can return directly if we found multiplexed serial ports because 1062 * devices with these ports only have additional diagnostic ports (useless) 1063 * and modem ports (for used with pppd). 1064 */ 1065 if (list_size > 0) { 1066 list = realloc(list, (list_size + 1) * sizeof(char *)); 1067 list[list_size] = NULL; 1068 return list; 1069 } 1070 1071 /* 1072 * The network port is on a high numbered interface so we walk backwards until 1073 * we hit anything other than application/control. 1074 */ 1075 1076 for (--i; i >= 0; i--) { 1077 /* Basic test to check if we're even in the right ballpark */ 1078 snprintf(buf, 64, SYSCTL_TEST, i); 1079 len = 127; 1080 error = sysctlbyname(buf, data, &len, NULL, 0); 1081 #ifdef DEBUG 1082 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1083 buf, error, error == 0 ? data : "FAILED"); 1084 #endif 1085 if (error < 0) 1086 break; 1087 if (strcasecmp(data, "uhso") != 0) 1088 break; 1089 1090 /* Test for useable ports */ 1091 for (p = port_type_list; *p != NULL; p++) { 1092 snprintf(buf, 64, SYSCTL_NAME_TTY, i, p); 1093 len = 127; 1094 error = sysctlbyname(buf, data, &len, NULL, 0); 1095 if (error == 0) { 1096 list = realloc(list, (list_size + 1) * sizeof(char *)); 1097 list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1098 sprintf(list[list_size], TTY_NAME, data); 1099 list_size++; 1100 } 1101 } 1102 1103 /* HACK! first port is a diagnostic port, we abort here */ 1104 snprintf(buf, 64, SYSCTL_NAME_TTY, i, "diagnostic"); 1105 len = 127; 1106 error = sysctlbyname(buf, data, &len, NULL, 0); 1107 #ifdef DEBUG 1108 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1109 buf, error, error == 0 ? data : "FAILED"); 1110 #endif 1111 if (error == 0) 1112 break; 1113 } 1114 1115 list = realloc(list, (list_size + 1) * sizeof(char *)); 1116 list[list_size] = NULL; 1117 return list; 1118 } 1119 1120 static int 1121 do_connect(struct ctx *ctx, const char *tty) 1122 { 1123 int i, error, needcfg; 1124 resp_arg ra; 1125 struct termios t; 1126 char **buf; 1127 1128 #ifdef DEBUG 1129 fprintf(stderr, "Attempting to open %s\n", tty); 1130 #endif 1131 1132 ctx->fd = open(tty, O_RDWR); 1133 if (ctx->fd < 0) { 1134 #ifdef DEBUG 1135 fprintf(stderr, "Failed to open %s\n", tty); 1136 #endif 1137 return (-1); 1138 } 1139 1140 tcgetattr(ctx->fd, &t); 1141 t.c_oflag = 0; 1142 t.c_iflag = 0; 1143 t.c_cflag = CLOCAL | CREAD; 1144 t.c_lflag = 0; 1145 tcsetattr(ctx->fd, TCSAFLUSH, &t); 1146 1147 error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1148 if (error == -2) { 1149 warnx("failed to read from device"); 1150 return (-1); 1151 } 1152 1153 /* Check for PIN */ 1154 error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1155 if (error != 0) { 1156 if (ctx->pin == NULL) { 1157 errx(1, "device requires PIN"); 1158 } 1159 1160 error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1161 ctx->pin); 1162 if (error != 0) { 1163 errx(1, "wrong PIN"); 1164 } 1165 } 1166 1167 /* 1168 * Check if a PDP context has been configured and configure one 1169 * if needed. 1170 */ 1171 ra.val[0].ptr = NULL; 1172 ra.val[1].int32 = 0; 1173 error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1174 buf = ra.val[0].ptr; 1175 needcfg = 1; 1176 for (i = 0; i < ra.val[1].int32; i++) { 1177 char apn[256]; 1178 int cid; 1179 error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1180 &cid, apn); 1181 if (error != 2) { 1182 free(buf[i]); 1183 continue; 1184 } 1185 1186 if (cid == ctx->pdp_ctx) { 1187 ctx->con_apn = strdup(apn); 1188 if (ctx->pdp_apn != NULL) { 1189 if (strcmp(apn, ctx->pdp_apn) == 0) 1190 needcfg = 0; 1191 } 1192 else { 1193 needcfg = 0; 1194 } 1195 } 1196 free(buf[i]); 1197 } 1198 free(buf); 1199 1200 if (needcfg) { 1201 if (ctx->pdp_apn == NULL) 1202 errx(1, "device is not configured and no APN given"); 1203 1204 error = at_cmd(ctx, NULL, NULL, NULL, 1205 "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1206 if (error != 0) { 1207 errx(1, "failed to configure device"); 1208 } 1209 ctx->con_apn = strdup(ctx->pdp_apn); 1210 } 1211 1212 if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1213 at_cmd(ctx, NULL, NULL, NULL, 1214 "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1215 (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1216 (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1217 } 1218 1219 error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1220 ctx->pdp_ctx); 1221 if (error != 0) 1222 return (-1); 1223 1224 at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1225 at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1226 1227 tmr_add(&timers, 1, 5, tmr_status, ctx); 1228 return (0); 1229 } 1230 1231 static void 1232 do_disconnect(struct ctx *ctx) 1233 { 1234 struct sockaddr_in sin, mask; 1235 1236 /* Disconnect */ 1237 at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1238 ctx->pdp_ctx); 1239 close(ctx->fd); 1240 1241 /* Remove ip-address from interface */ 1242 if (ctx->flags & IPASSIGNED) { 1243 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1244 memset(&mask.sin_addr.s_addr, 0xff, 1245 sizeof(mask.sin_addr.s_addr)); 1246 sin.sin_family = mask.sin_family = AF_INET; 1247 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1248 sizeof(sin.sin_addr.s_addr)); 1249 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1250 (struct sockaddr *)&mask); 1251 1252 if_ifdown(ctx->ifnam); 1253 ctx->flags &= ~IPASSIGNED; 1254 } 1255 1256 /* Attempt to reset resolv.conf */ 1257 set_nameservers(ctx, ctx->resolv_path, 0); 1258 } 1259 1260 static void 1261 daemonize(struct ctx *ctx) 1262 { 1263 struct pidfh *pfh; 1264 pid_t opid; 1265 1266 snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1267 1268 pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1269 if (pfh == NULL) { 1270 warn("Cannot create pidfile %s", ctx->pidfile); 1271 return; 1272 } 1273 1274 if (daemon(0, 0) == -1) { 1275 warn("Cannot daemonize"); 1276 pidfile_remove(pfh); 1277 return; 1278 } 1279 1280 pidfile_write(pfh); 1281 ctx->pfh = pfh; 1282 ctx->flags |= FLG_DAEMON; 1283 1284 snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1285 openlog(syslog_title, LOG_PID, LOG_USER); 1286 syslog_open = 1; 1287 } 1288 1289 static void 1290 send_disconnect(const char *ifnam) 1291 { 1292 char pidfile[128]; 1293 FILE *fp; 1294 pid_t pid; 1295 int n; 1296 1297 snprintf(pidfile, 127, PIDFILE, ifnam); 1298 fp = fopen(pidfile, "r"); 1299 if (fp == NULL) { 1300 warn("Cannot open %s", pidfile); 1301 return; 1302 } 1303 1304 n = fscanf(fp, "%d", &pid); 1305 fclose(fp); 1306 if (n != 1) { 1307 warnx("unable to read daemon pid"); 1308 return; 1309 } 1310 #ifdef DEBUG 1311 fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1312 #endif 1313 kill(pid, SIGTERM); 1314 } 1315 1316 static void 1317 usage(const char *exec) 1318 { 1319 1320 printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1321 "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1322 printf("usage %s -d interface\n", exec); 1323 } 1324 1325 enum { 1326 MODE_CONN, 1327 MODE_DISC 1328 }; 1329 1330 int 1331 main(int argc, char *argv[]) 1332 { 1333 int ch, error, mode; 1334 const char *ifnam = NULL; 1335 char *tty = NULL; 1336 char **p, **tty_list; 1337 fd_set set; 1338 struct ctx ctx; 1339 struct itimerval it; 1340 1341 TAILQ_INIT(&timers.head); 1342 timers.res = 1; 1343 1344 ctx.pdp_ctx = 1; 1345 ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1346 ctx.pin = NULL; 1347 1348 ctx.con_status = 0; 1349 ctx.con_apn = NULL; 1350 ctx.con_oper = NULL; 1351 ctx.con_net_stat = 0; 1352 ctx.con_net_type = -1; 1353 ctx.flags = 0; 1354 ctx.resolv_path = RESOLV_PATH; 1355 ctx.resolv = NULL; 1356 ctx.ns = NULL; 1357 ctx.dbm = 0; 1358 1359 mode = MODE_CONN; 1360 ctx.flags |= FLG_DELAYED; 1361 1362 while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1363 switch (ch) { 1364 case 'a': 1365 ctx.pdp_apn = argv[optind - 1]; 1366 break; 1367 case 'c': 1368 ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1369 if (ctx.pdp_ctx < 1) { 1370 warnx("Invalid context ID, defaulting to 1"); 1371 ctx.pdp_ctx = 1; 1372 } 1373 break; 1374 case 'p': 1375 ctx.pin = argv[optind - 1]; 1376 break; 1377 case 'u': 1378 ctx.pdp_user = argv[optind - 1]; 1379 break; 1380 case 'k': 1381 ctx.pdp_pwd = argv[optind - 1]; 1382 break; 1383 case 'r': 1384 ctx.resolv_path = argv[optind - 1]; 1385 break; 1386 case 'd': 1387 mode = MODE_DISC; 1388 break; 1389 case 'b': 1390 ctx.flags &= ~FLG_DELAYED; 1391 break; 1392 case 'n': 1393 ctx.flags |= FLG_NODAEMON; 1394 break; 1395 case 'f': 1396 tty = argv[optind - 1]; 1397 break; 1398 case 'h': 1399 case '?': 1400 default: 1401 usage(argv[0]); 1402 exit(EXIT_SUCCESS); 1403 } 1404 } 1405 1406 argc -= optind; 1407 argv += optind; 1408 1409 if (argc < 1) 1410 errx(1, "no interface given"); 1411 1412 ifnam = argv[argc - 1]; 1413 ctx.ifnam = strdup(ifnam); 1414 1415 switch (mode) { 1416 case MODE_DISC: 1417 printf("Disconnecting %s\n", ifnam); 1418 send_disconnect(ifnam); 1419 exit(EXIT_SUCCESS); 1420 default: 1421 break; 1422 } 1423 1424 signal(SIGHUP, sig_handle); 1425 signal(SIGINT, sig_handle); 1426 signal(SIGQUIT, sig_handle); 1427 signal(SIGTERM, sig_handle); 1428 signal(SIGALRM, sig_handle); 1429 1430 it.it_interval.tv_sec = 1; 1431 it.it_interval.tv_usec = 0; 1432 it.it_value.tv_sec = 1; 1433 it.it_value.tv_usec = 0; 1434 error = setitimer(ITIMER_REAL, &it, NULL); 1435 if (error != 0) 1436 errx(1, "setitimer"); 1437 1438 tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1439 watchdog_reset(&ctx, 15); 1440 1441 if (tty != NULL) { 1442 error = do_connect(&ctx, tty); 1443 if (error != 0) 1444 errx(1, "Failed to open %s", tty); 1445 } 1446 else { 1447 tty_list = get_tty(&ctx); 1448 #ifdef DEBUG 1449 if (tty_list == NULL) { 1450 fprintf(stderr, "get_tty returned empty list\n"); 1451 } else { 1452 fprintf(stderr, "tty list:\n"); 1453 for (p = tty_list; *p != NULL; p++) { 1454 fprintf(stderr, "\t %s\n", *p); 1455 } 1456 } 1457 #endif 1458 for (p = tty_list; *p != NULL; p++) { 1459 error = do_connect(&ctx, *p); 1460 if (error == 0) { 1461 tty = *p; 1462 break; 1463 } 1464 } 1465 if (*p == NULL) 1466 errx(1, "Failed to obtain a control port, " 1467 "try specifying one manually"); 1468 } 1469 1470 if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1471 daemonize(&ctx); 1472 1473 1474 FD_ZERO(&set); 1475 FD_SET(ctx.fd, &set); 1476 for (;;) { 1477 1478 watchdog_disable(&ctx); 1479 error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1480 if (error <= 0) { 1481 if (running && errno == EINTR) 1482 continue; 1483 if (ctx.flags & FLG_WDEXP) { 1484 ctx.flags &= ~FLG_WDEXP; 1485 watchdog_reset(&ctx, 5); 1486 do_disconnect(&ctx); 1487 watchdog_reset(&ctx, 15); 1488 do_connect(&ctx, tty); 1489 running = 1; 1490 continue; 1491 } 1492 1493 break; 1494 } 1495 1496 if (FD_ISSET(ctx.fd, &set)) { 1497 watchdog_reset(&ctx, 15); 1498 error = at_async(&ctx, &ctx); 1499 if (error != 0) 1500 break; 1501 } 1502 FD_SET(ctx.fd, &set); 1503 1504 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1505 printf("Status: %s (%s)", 1506 ctx.con_status ? "connected" : "disconnected", 1507 network_access_type[ctx.con_net_type]); 1508 if (ctx.dbm < 0) 1509 printf(", signal: %d dBm", ctx.dbm); 1510 printf("\r"); 1511 fflush(stdout); 1512 } 1513 } 1514 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1515 printf("\n"); 1516 1517 signal(SIGHUP, SIG_DFL); 1518 signal(SIGINT, SIG_DFL); 1519 signal(SIGQUIT, SIG_DFL); 1520 signal(SIGTERM, SIG_DFL); 1521 signal(SIGALRM, SIG_IGN); 1522 1523 do_disconnect(&ctx); 1524 1525 if (ctx.flags & FLG_DAEMON) { 1526 pidfile_remove(ctx.pfh); 1527 if (syslog_open) 1528 closelog(); 1529 } 1530 1531 return (0); 1532 } 1533