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