uhsoctl.c revision 292855
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: stable/10/usr.sbin/uhsoctl/uhsoctl.c 292855 2015-12-29 01:08:58Z ngie $ 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 77static const char *network_access_type[] = { 78 "GSM", 79 "Compact GSM", 80 "UMTS", 81 "GSM (EGPRS)", 82 "HSDPA", 83 "HSUPA", 84 "HSDPA/HSUPA" 85}; 86 87static 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 96struct 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 138static int readline_buf(const char *, const char *, char *, size_t); 139static int readline(int, char *, size_t); 140static void daemonize(struct ctx *); 141 142static int at_cmd_async(int, const char *, ...); 143 144typedef union { 145 void *ptr; 146 uint32_t int32; 147} resp_data; 148typedef struct { 149 resp_data val[2]; 150} resp_arg; 151typedef void (*resp_cb)(resp_arg *, const char *, const char *); 152 153typedef void (*async_cb)(void *, const char *); 154struct async_handle { 155 const char *cmd; 156 async_cb func; 157}; 158 159static void at_async_creg(void *, const char *); 160static void at_async_cgreg(void *, const char *); 161static void at_async_cops(void *, const char *); 162static void at_async_owancall(void *, const char *); 163static void at_async_owandata(void *, const char *); 164static void at_async_csq(void *, const char *); 165 166static 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 176struct timer_entry; 177struct timers { 178 TAILQ_HEAD(, timer_entry) head; 179 int res; 180}; 181 182typedef void (*tmr_cb)(int, void *); 183struct timer_entry { 184 TAILQ_ENTRY(timer_entry) next; 185 int id; 186 int timeout; 187 tmr_cb func; 188 void *arg; 189}; 190 191 192static struct timers timers; 193static volatile int running = 1; 194static int syslog_open = 0; 195static char syslog_title[64]; 196 197/* Periodic timer, runs ready timer tasks every tick */ 198static void 199tmr_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 */ 220static void 221tmr_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 255static void 256watchdog_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 266static void 267tmr_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 275static void 276tmr_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 284static void 285tmr_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 293static void 294tmr_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 317static void 318sig_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 334static void 335logger(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, "%s", 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 */ 361static int 362ifaddr_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 384static int 385if_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 419static int 420ifaddr_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 430static int 431ifaddr_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 441static int 442set_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 ctx->ns = NULL; 457 } 458 459 fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666); 460 if (fd < 0) 461 return (-1); 462 463 if (ns == 0) { 464 /* Attempt to restore old resolv.conf */ 465 if (ctx->resolv != NULL) { 466 ftruncate(fd, 0); 467 lseek(fd, 0, SEEK_SET); 468 write(fd, ctx->resolv, ctx->resolv_sz); 469 free(ctx->resolv); 470 ctx->resolv = NULL; 471 ctx->resolv_sz = 0; 472 } 473 close(fd); 474 return (0); 475 } 476 477 478 ctx->ns = malloc(sizeof(char *) * (ns + 1)); 479 if (ctx->ns == NULL) { 480 close(fd); 481 return (-1); 482 } 483 484 va_start(ap, ns); 485 for (i = 0; i < ns; i++) { 486 p = va_arg(ap, char *); 487 ctx->ns[i] = strdup(p); 488 } 489 ctx->ns[i] = NULL; 490 va_end(ap); 491 492 /* Attempt to backup the old resolv.conf */ 493 if (ctx->resolv == NULL) { 494 i = fstat(fd, &sb); 495 if (i == 0 && sb.st_size != 0) { 496 ctx->resolv_sz = sb.st_size; 497 ctx->resolv = malloc(sb.st_size); 498 if (ctx->resolv != NULL) { 499 n = read(fd, ctx->resolv, sb.st_size); 500 if (n != sb.st_size) { 501 free(ctx->resolv); 502 ctx->resolv = NULL; 503 } 504 } 505 } 506 } 507 508 509 ftruncate(fd, 0); 510 lseek(fd, 0, SEEK_SET); 511 fp = fdopen(fd, "w"); 512 513 /* 514 * Write back everything other than nameserver entries to the 515 * new resolv.conf 516 */ 517 if (ctx->resolv != NULL) { 518 p = ctx->resolv; 519 while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 520 sizeof(buf))) > 0) { 521 p += i; 522 if (strncasecmp(buf, "nameserver", 10) == 0) 523 continue; 524 fprintf(fp, "%s", buf); 525 } 526 } 527 528 for (i = 0; ctx->ns[i] != NULL; i++) { 529 fprintf(fp, "nameserver %s\n", ctx->ns[i]); 530 } 531 fclose(fp); 532 return (0); 533} 534 535/* Read a \n-terminated line from buffer */ 536static int 537readline_buf(const char *s, const char *e, char *buf, size_t bufsz) 538{ 539 int pos = 0; 540 char *p = buf; 541 542 for (; s < e; s++) { 543 *p = *s; 544 pos++; 545 if (pos >= (bufsz - 1)) 546 break; 547 if (*p++ == '\n') 548 break; 549 } 550 *p = '\0'; 551 return (pos); 552} 553 554/* Read a \n-terminated line from file */ 555static int 556readline(int fd, char *buf, size_t bufsz) 557{ 558 int n = 0, pos = 0; 559 char *p = buf; 560 561 for (;;) { 562 n = read(fd, p, 1); 563 if (n <= 0) 564 break; 565 pos++; 566 if (pos >= (bufsz - 1)) 567 break; 568 if (*p++ == '\n') 569 break; 570 } 571 *p = '\0'; 572 return (n <= 0 ? n : pos); 573} 574 575/* 576 * Synchronous AT command 577 */ 578static int 579at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 580{ 581 char buf[512]; 582 char cmd[64]; 583 size_t l; 584 int n, error, retval = 0; 585 va_list ap; 586 fd_set set; 587 char *p; 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 (%zu)\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 (ctx->flags & FLG_WDEXP) { 618 watchdog_disable(ctx); 619 return (-2); 620 } 621 } while (error <= 0 && errno == EINTR); 622 watchdog_disable(ctx); 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 if ((p = strchr(buf, '\r')) != NULL) 639 *p = '\0'; 640 else if ((p = strchr(buf, '\n')) != NULL) 641 *p = '\0'; 642#ifdef DEBUG 643 fprintf(stderr, "SYNC_RESP: %s\n", buf); 644#endif 645 646 /* Skip local echo */ 647 if (strncasecmp(cmd, buf, strlen(buf)) == 0) 648 continue; 649 650 if (cb != NULL) 651 cb(ra, cmd, buf); 652 653 if (strncmp(buf, "OK", 2) == 0) { 654 retval = retval ? retval : 0; 655 break; 656 } else if (strstr(buf, "ERROR") != NULL) { 657 retval = -1; 658 break; 659 } 660 if (resp != NULL) 661 retval = strncmp(buf, resp, l); 662 } 663#ifdef DEBUG 664 fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 665#endif 666 return (retval); 667} 668 669static int 670at_cmd_async(int fd, const char *cf, ...) 671{ 672 size_t l; 673 va_list ap; 674 char cmd[64]; 675 676 va_start(ap, cf); 677 vsnprintf(cmd, sizeof(cmd), cf, ap); 678 va_end(ap); 679 680#ifdef DEBUG 681 fprintf(stderr, "CMD: %s", cmd); 682#endif 683 l = strlen(cmd); 684 return (write(fd, cmd, l)); 685} 686 687static void 688saveresp(resp_arg *ra, const char *cmd, const char *resp) 689{ 690 char **buf; 691 int i = ra->val[1].int32; 692 693#ifdef DEBUG 694 fprintf(stderr, "Save '%s'\n", resp); 695#endif 696 697 buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 698 if (buf == NULL) 699 return; 700 701 buf[i] = strdup(resp); 702 703 ra->val[0].ptr = buf; 704 ra->val[1].int32 = i + 1; 705} 706 707static void 708freeresp(resp_arg *ra) 709{ 710 char **buf; 711 int i; 712 713 buf = ra->val[0].ptr; 714 for (i = 0; i < ra->val[1].int32; i++) { 715 free(buf[i]); 716 } 717 free(buf); 718} 719 720static void 721at_async_creg(void *arg, const char *resp) 722{ 723 struct ctx *ctx = arg; 724 int n, reg; 725 726 n = sscanf(resp, "+CREG: %*d,%d", ®); 727 if (n != 1) { 728 n = sscanf(resp, "+CREG: %d", ®); 729 if (n != 1) 730 return; 731 } 732 733 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 734 tmr_add(&timers, 1, 1, tmr_creg, ctx); 735 } 736 else { 737 tmr_add(&timers, 1, 30, tmr_creg, ctx); 738 } 739 740 if (ctx->con_net_stat == reg) 741 return; 742 743 ctx->con_net_stat = reg; 744 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 745} 746 747static void 748at_async_cgreg(void *arg, const char *resp) 749{ 750 struct ctx *ctx = arg; 751 int n, reg; 752 753 n = sscanf(resp, "+CGREG: %*d,%d", ®); 754 if (n != 1) { 755 n = sscanf(resp, "+CGREG: %d", ®); 756 if (n != 1) 757 return; 758 } 759 760 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 761 tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 762 } 763 else { 764 tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 765 } 766 767 if (ctx->con_net_stat == reg) 768 return; 769 770 ctx->con_net_stat = reg; 771 at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 772} 773 774 775static void 776at_async_cops(void *arg, const char *resp) 777{ 778 struct ctx *ctx = arg; 779 int n, at; 780 char opr[64]; 781 782 n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 783 opr, &at); 784 if (n != 2) 785 return; 786 787 if (ctx->con_oper != NULL) { 788 if (ctx->con_net_type == at && 789 strcasecmp(opr, ctx->con_oper) == 0) 790 return; 791 free(ctx->con_oper); 792 } 793 794 ctx->con_oper = strdup(opr); 795 ctx->con_net_type = at; 796 797 if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 798 logger(LOG_NOTICE, "%s to \"%s\" (%s)", 799 network_reg_status[ctx->con_net_stat], 800 ctx->con_oper, network_access_type[ctx->con_net_type]); 801 if (ctx->con_status != 1) { 802 at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 803 ctx->pdp_ctx); 804 } 805 } 806 else { 807 logger(LOG_NOTICE, "%s (%s)", 808 network_reg_status[ctx->con_net_stat], 809 network_access_type[ctx->con_net_type]); 810 } 811} 812 813/* 814 * Signal strength for pretty console output 815 * 816 * From 3GPP TS 27.007 V8.3.0, Section 8.5 817 * 0 = -113 dBm or less 818 * 1 = -111 dBm 819 * 2...30 = -109...-53 dBm 820 * 31 = -51 dBm or greater 821 * 822 * So, dbm = (rssi * 2) - 113 823*/ 824static void 825at_async_csq(void *arg, const char *resp) 826{ 827 struct ctx *ctx = arg; 828 int n, rssi; 829 830 n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 831 if (n != 1) 832 return; 833 if (rssi == 99) 834 ctx->dbm = 0; 835 else { 836 ctx->dbm = (rssi * 2) - 113; 837 tmr_add(&timers, 1, 15, tmr_status, ctx); 838 } 839 840 ctx->flags |= FLG_NEWDATA; 841} 842 843static void 844at_async_owancall(void *arg, const char *resp) 845{ 846 struct ctx *ctx = arg; 847 int n, i; 848 849 n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 850 if (n != 1) 851 return; 852 853 if (i == ctx->con_status) 854 return; 855 856 at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 857 858 ctx->con_status = i; 859 if (ctx->con_status == 1) { 860 logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 861 ctx->con_oper, ctx->con_apn, 862 network_access_type[ctx->con_net_type]); 863 } 864 else { 865 logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 866 ctx->con_oper, ctx->con_apn); 867 } 868} 869 870static void 871at_async_owandata(void *arg, const char *resp) 872{ 873 struct ctx *ctx = arg; 874 char ip[40], ns1[40], ns2[40]; 875 int n, error, rs; 876 struct ifaddrs *ifap, *ifa; 877 struct sockaddr_in sin, mask; 878 struct sockaddr_dl sdl; 879 struct { 880 struct rt_msghdr rtm; 881 char buf[512]; 882 } r; 883 char *cp = r.buf; 884 885 n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 886 ip, ns1, ns2); 887 if (n != 3) 888 return; 889 890 /* XXX: AF_INET assumption */ 891 892 logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 893 894 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 895 memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 896 sin.sin_family = mask.sin_family = AF_INET; 897 898 if (ctx->flags & IPASSIGNED) { 899 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 900 sizeof(sin.sin_addr.s_addr)); 901 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 902 (struct sockaddr *)&mask); 903 } 904 inet_pton(AF_INET, ip, &ctx->ip.s_addr); 905 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 906 sizeof(sin.sin_addr.s_addr)); 907 908 error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 909 (struct sockaddr *)&mask); 910 if (error != 0) { 911 logger(LOG_ERR, "failed to set ip-address"); 912 return; 913 } 914 915 if_ifup(ctx->ifnam); 916 917 ctx->flags |= IPASSIGNED; 918 919 set_nameservers(ctx, ctx->resolv_path, 0); 920 error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 921 if (error != 0) { 922 logger(LOG_ERR, "failed to set nameservers"); 923 } 924 925 error = getifaddrs(&ifap); 926 if (error != 0) { 927 logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 928 return; 929 } 930 931 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 932 if (ifa->ifa_addr->sa_family != AF_LINK) 933 continue; 934 if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 935 memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 936 sizeof(struct sockaddr_dl)); 937 break; 938 } 939 } 940 if (ifa == NULL) 941 return; 942 943 rs = socket(PF_ROUTE, SOCK_RAW, 0); 944 if (rs < 0) { 945 logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 946 return; 947 } 948 949 memset(&r, 0, sizeof(r)); 950 951 r.rtm.rtm_version = RTM_VERSION; 952 r.rtm.rtm_type = RTM_ADD; 953 r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 954 r.rtm.rtm_pid = getpid(); 955 memset(&sin, 0, sizeof(struct sockaddr_in)); 956 sin.sin_family = AF_INET; 957 sin.sin_len = sizeof(struct sockaddr_in); 958 959 memcpy(cp, &sin, sin.sin_len); 960 cp += SA_SIZE(&sin); 961 memcpy(cp, &sdl, sdl.sdl_len); 962 cp += SA_SIZE(&sdl); 963 memcpy(cp, &sin, sin.sin_len); 964 r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 965 r.rtm.rtm_msglen = sizeof(r); 966 967 n = write(rs, &r, r.rtm.rtm_msglen); 968 if (n != r.rtm.rtm_msglen) { 969 r.rtm.rtm_type = RTM_DELETE; 970 n = write(rs, &r, r.rtm.rtm_msglen); 971 r.rtm.rtm_type = RTM_ADD; 972 n = write(rs, &r, r.rtm.rtm_msglen); 973 } 974 975 if (n != r.rtm.rtm_msglen) { 976 logger(LOG_ERR, "failed to set default route: %s", 977 strerror(errno)); 978 } 979 close(rs); 980 981 /* Delayed daemonization */ 982 if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 983 daemonize(ctx); 984} 985 986static int 987at_async(struct ctx *ctx, void *arg) 988{ 989 int n, i; 990 size_t l; 991 char buf[512]; 992 993 watchdog_reset(ctx, 15); 994 995 bzero(buf, sizeof(buf)); 996 n = readline(ctx->fd, buf, sizeof(buf)); 997 if (n <= 0) 998 return (n <= 0 ? -1 : 0); 999 1000#ifdef DEBUG 1001 fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 1002#endif 1003 for (i = 0; async_cmd[i].cmd != NULL; i++) { 1004 l = strlen(async_cmd[i].cmd); 1005 if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 1006 async_cmd[i].func(arg, buf); 1007 } 1008 } 1009 return (0); 1010} 1011 1012static const char *port_type_list[] = { 1013 "control", "application", "application2", NULL 1014}; 1015 1016/* 1017 * Attempts to find a list of control tty for the interface 1018 * FreeBSD attaches USB devices per interface so we have to go through 1019 * hoops to find which ttys that belong to our network interface. 1020 */ 1021static char ** 1022get_tty(struct ctx *ctx) 1023{ 1024 char buf[64], data[128]; 1025 int error, i, usbport, usbport0, list_size = 0; 1026 char **list = NULL; 1027 size_t len; 1028 const char **p, *q; 1029 1030 /* 1031 * Look for the network interface first 1032 */ 1033 for (i = 0; ; i++) { 1034 /* Check if we still have uhso nodes to check */ 1035 snprintf(buf, 64, SYSCTL_TEST, i); 1036 len = 127; 1037 error = sysctlbyname(buf, data, &len, NULL, 0); 1038 data[len] = '\0'; 1039#ifdef DEBUG 1040 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1041 buf, error, error == 0 ? data : "FAILED"); 1042#endif 1043 if (error < 0 || strcasecmp(data, "uhso") != 0) 1044 return NULL; 1045 1046 /* Check if this node contains the network interface we want */ 1047 snprintf(buf, 64, SYSCTL_NETIF, i); 1048 len = 127; 1049 error = sysctlbyname(buf, data, &len, NULL, 0); 1050 data[len] = '\0'; 1051#ifdef DEBUG 1052 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1053 buf, error, error == 0 ? data : "FAILED"); 1054#endif 1055 if (error == 0 && strcasecmp(data, ctx->ifnam) == 0) 1056 break; 1057 } 1058 1059 /* Figure out the USB port location */ 1060 snprintf(buf, 64, SYSCTL_LOCATION, i); 1061 len = 127; 1062 error = sysctlbyname(buf, data, &len, NULL, 0); 1063 data[len] = '\0'; 1064#ifdef DEBUG 1065 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1066 buf, error, error == 0 ? data : "FAILED"); 1067#endif 1068 if (error != 0) 1069 return (NULL); 1070 1071 q = strstr(data, "port="); 1072 if (q != NULL) { 1073 error = sscanf(q, " port=%d", &usbport); 1074 if (error != 1) { 1075#ifdef DEBUG 1076 fprintf(stderr, "failed to read usb port location from '%s'\n", data); 1077#endif 1078 return (NULL); 1079 } 1080 } else { 1081#ifdef DEBUG 1082 fprintf(stderr, "failed to parse location '%s'\n", data); 1083#endif 1084 return (NULL); 1085 } 1086#ifdef DEBUG 1087 fprintf(stderr, "USB port location=%d\n", usbport); 1088#endif 1089 1090 /* 1091 * Now go through it all again but only look at those matching the 1092 * usb port location we found. 1093 */ 1094 for (i = 0; ; i++) { 1095 snprintf(buf, 64, SYSCTL_LOCATION, i); 1096 len = 127; 1097 memset(&data, 0, sizeof(data)); 1098 error = sysctlbyname(buf, data, &len, NULL, 0); 1099 if (error != 0) 1100 break; 1101 data[len] = '\0'; 1102 q = strstr(data, "port="); 1103 if (q == NULL) 1104 continue; 1105 sscanf(q, " port=%d", &usbport0); 1106 if (usbport != usbport0) 1107 continue; 1108 1109 /* Try to add ports */ 1110 for (p = port_type_list; *p != NULL; p++) { 1111 snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1112 len = 127; 1113 memset(&data, 0, sizeof(data)); 1114 error = sysctlbyname(buf, data, &len, NULL, 0); 1115 data[len] = '\0'; 1116#ifdef DEBUG 1117 fprintf(stderr, "sysctl %s returned(%d): %s\n", 1118 buf, error, error == 0 ? data : "FAILED"); 1119#endif 1120 if (error == 0) { 1121 list = realloc(list, (list_size + 1) * sizeof(char *)); 1122 list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1123 sprintf(list[list_size], TTY_NAME, data); 1124 list_size++; 1125 } 1126 } 1127 } 1128 list = realloc(list, (list_size + 1) * sizeof(char *)); 1129 list[list_size] = NULL; 1130 return (list); 1131} 1132 1133static int 1134do_connect(struct ctx *ctx, const char *tty) 1135{ 1136 int i, error, needcfg; 1137 resp_arg ra; 1138 struct termios t; 1139 char **buf; 1140 1141#ifdef DEBUG 1142 fprintf(stderr, "Attempting to open %s\n", tty); 1143#endif 1144 1145 ctx->fd = open(tty, O_RDWR); 1146 if (ctx->fd < 0) { 1147#ifdef DEBUG 1148 fprintf(stderr, "Failed to open %s\n", tty); 1149#endif 1150 return (-1); 1151 } 1152 1153 tcgetattr(ctx->fd, &t); 1154 t.c_oflag = 0; 1155 t.c_iflag = 0; 1156 t.c_cflag = CLOCAL | CREAD; 1157 t.c_lflag = 0; 1158 tcsetattr(ctx->fd, TCSAFLUSH, &t); 1159 1160 error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1161 if (error == -2) { 1162 warnx("failed to read from device %s", tty); 1163 return (-1); 1164 } 1165 1166 /* Check for PIN */ 1167 error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1168 if (error != 0) { 1169 ra.val[0].ptr = NULL; 1170 ra.val[1].int32 = 0; 1171 error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n"); 1172 if (ra.val[1].int32 > 0) { 1173 char *p; 1174 1175 buf = ra.val[0].ptr; 1176 if (strstr(buf[0], "+CME ERROR:") != NULL) { 1177 buf[0] += 12; 1178 errx(1, "%s", buf[0]); 1179 } 1180 freeresp(&ra); 1181 } else 1182 freeresp(&ra); 1183 1184 if (ctx->pin == NULL) { 1185 errx(1, "device requires PIN"); 1186 } 1187 1188 error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1189 ctx->pin); 1190 if (error != 0) { 1191 errx(1, "wrong PIN"); 1192 } 1193 } 1194 1195 /* 1196 * Check if a PDP context has been configured and configure one 1197 * if needed. 1198 */ 1199 ra.val[0].ptr = NULL; 1200 ra.val[1].int32 = 0; 1201 error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1202 buf = ra.val[0].ptr; 1203 needcfg = 1; 1204 for (i = 0; i < ra.val[1].int32; i++) { 1205 char apn[256]; 1206 int cid; 1207 error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1208 &cid, apn); 1209 if (error != 2) { 1210 free(buf[i]); 1211 continue; 1212 } 1213 1214 if (cid == ctx->pdp_ctx) { 1215 ctx->con_apn = strdup(apn); 1216 if (ctx->pdp_apn != NULL) { 1217 if (strcmp(apn, ctx->pdp_apn) == 0) 1218 needcfg = 0; 1219 } 1220 else { 1221 needcfg = 0; 1222 } 1223 } 1224 free(buf[i]); 1225 } 1226 free(buf); 1227 1228 if (needcfg) { 1229 if (ctx->pdp_apn == NULL) 1230 errx(1, "device is not configured and no APN given"); 1231 1232 error = at_cmd(ctx, NULL, NULL, NULL, 1233 "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1234 if (error != 0) { 1235 errx(1, "failed to configure device"); 1236 } 1237 ctx->con_apn = strdup(ctx->pdp_apn); 1238 } 1239 1240 if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1241 at_cmd(ctx, NULL, NULL, NULL, 1242 "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1243 (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1244 (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1245 } 1246 1247 error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1248 ctx->pdp_ctx); 1249 if (error != 0) 1250 return (-1); 1251 1252 at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1253 at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1254 1255 tmr_add(&timers, 1, 5, tmr_status, ctx); 1256 return (0); 1257} 1258 1259static void 1260do_disconnect(struct ctx *ctx) 1261{ 1262 struct sockaddr_in sin, mask; 1263 1264 /* Disconnect */ 1265 at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1266 ctx->pdp_ctx); 1267 close(ctx->fd); 1268 1269 /* Remove ip-address from interface */ 1270 if (ctx->flags & IPASSIGNED) { 1271 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1272 memset(&mask.sin_addr.s_addr, 0xff, 1273 sizeof(mask.sin_addr.s_addr)); 1274 sin.sin_family = mask.sin_family = AF_INET; 1275 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1276 sizeof(sin.sin_addr.s_addr)); 1277 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1278 (struct sockaddr *)&mask); 1279 1280 if_ifdown(ctx->ifnam); 1281 ctx->flags &= ~IPASSIGNED; 1282 } 1283 1284 /* Attempt to reset resolv.conf */ 1285 set_nameservers(ctx, ctx->resolv_path, 0); 1286} 1287 1288static void 1289daemonize(struct ctx *ctx) 1290{ 1291 struct pidfh *pfh; 1292 pid_t opid; 1293 1294 snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1295 1296 pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1297 if (pfh == NULL) { 1298 warn("Cannot create pidfile %s", ctx->pidfile); 1299 return; 1300 } 1301 1302 if (daemon(0, 0) == -1) { 1303 warn("Cannot daemonize"); 1304 pidfile_remove(pfh); 1305 return; 1306 } 1307 1308 pidfile_write(pfh); 1309 ctx->pfh = pfh; 1310 ctx->flags |= FLG_DAEMON; 1311 1312 snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1313 openlog(syslog_title, LOG_PID, LOG_USER); 1314 syslog_open = 1; 1315} 1316 1317static void 1318send_disconnect(const char *ifnam) 1319{ 1320 char pidfile[128]; 1321 FILE *fp; 1322 pid_t pid; 1323 int n; 1324 1325 snprintf(pidfile, 127, PIDFILE, ifnam); 1326 fp = fopen(pidfile, "r"); 1327 if (fp == NULL) { 1328 warn("Cannot open %s", pidfile); 1329 return; 1330 } 1331 1332 n = fscanf(fp, "%d", &pid); 1333 fclose(fp); 1334 if (n != 1) { 1335 warnx("unable to read daemon pid"); 1336 return; 1337 } 1338#ifdef DEBUG 1339 fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1340#endif 1341 kill(pid, SIGTERM); 1342} 1343 1344static void 1345usage(const char *exec) 1346{ 1347 1348 printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1349 "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1350 printf("usage %s -d interface\n", exec); 1351} 1352 1353enum { 1354 MODE_CONN, 1355 MODE_DISC 1356}; 1357 1358int 1359main(int argc, char *argv[]) 1360{ 1361 int ch, error, mode; 1362 const char *ifnam = NULL; 1363 char *tty = NULL; 1364 char **p, **tty_list; 1365 fd_set set; 1366 struct ctx ctx; 1367 struct itimerval it; 1368 1369 TAILQ_INIT(&timers.head); 1370 timers.res = 1; 1371 1372 ctx.pdp_ctx = 1; 1373 ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1374 ctx.pin = NULL; 1375 1376 ctx.con_status = 0; 1377 ctx.con_apn = NULL; 1378 ctx.con_oper = NULL; 1379 ctx.con_net_stat = 0; 1380 ctx.con_net_type = -1; 1381 ctx.flags = 0; 1382 ctx.resolv_path = RESOLV_PATH; 1383 ctx.resolv = NULL; 1384 ctx.ns = NULL; 1385 ctx.dbm = 0; 1386 1387 mode = MODE_CONN; 1388 ctx.flags |= FLG_DELAYED; 1389 1390 while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1391 switch (ch) { 1392 case 'a': 1393 ctx.pdp_apn = argv[optind - 1]; 1394 break; 1395 case 'c': 1396 ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1397 if (ctx.pdp_ctx < 1) { 1398 warnx("Invalid context ID, defaulting to 1"); 1399 ctx.pdp_ctx = 1; 1400 } 1401 break; 1402 case 'p': 1403 ctx.pin = argv[optind - 1]; 1404 break; 1405 case 'u': 1406 ctx.pdp_user = argv[optind - 1]; 1407 break; 1408 case 'k': 1409 ctx.pdp_pwd = argv[optind - 1]; 1410 break; 1411 case 'r': 1412 ctx.resolv_path = argv[optind - 1]; 1413 break; 1414 case 'd': 1415 mode = MODE_DISC; 1416 break; 1417 case 'b': 1418 ctx.flags &= ~FLG_DELAYED; 1419 break; 1420 case 'n': 1421 ctx.flags |= FLG_NODAEMON; 1422 break; 1423 case 'f': 1424 tty = argv[optind - 1]; 1425 break; 1426 case 'h': 1427 case '?': 1428 default: 1429 usage(argv[0]); 1430 exit(EXIT_SUCCESS); 1431 } 1432 } 1433 1434 argc -= optind; 1435 argv += optind; 1436 1437 if (argc < 1) 1438 errx(1, "no interface given"); 1439 1440 ifnam = argv[argc - 1]; 1441 ctx.ifnam = strdup(ifnam); 1442 1443 switch (mode) { 1444 case MODE_DISC: 1445 printf("Disconnecting %s\n", ifnam); 1446 send_disconnect(ifnam); 1447 exit(EXIT_SUCCESS); 1448 default: 1449 break; 1450 } 1451 1452 signal(SIGHUP, sig_handle); 1453 signal(SIGINT, sig_handle); 1454 signal(SIGQUIT, sig_handle); 1455 signal(SIGTERM, sig_handle); 1456 signal(SIGALRM, sig_handle); 1457 1458 it.it_interval.tv_sec = 1; 1459 it.it_interval.tv_usec = 0; 1460 it.it_value.tv_sec = 1; 1461 it.it_value.tv_usec = 0; 1462 error = setitimer(ITIMER_REAL, &it, NULL); 1463 if (error != 0) 1464 errx(1, "setitimer"); 1465 1466 tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1467 watchdog_reset(&ctx, 15); 1468 1469 if (tty != NULL) { 1470 error = do_connect(&ctx, tty); 1471 if (error != 0) 1472 errx(1, "Failed to open %s", tty); 1473 } 1474 else { 1475 tty_list = get_tty(&ctx); 1476 if (tty_list == NULL) 1477 errx(1, "%s does not appear to be a uhso device", ifnam); 1478#ifdef DEBUG 1479 if (tty_list == NULL) { 1480 fprintf(stderr, "get_tty returned empty list\n"); 1481 } else { 1482 fprintf(stderr, "tty list:\n"); 1483 for (p = tty_list; *p != NULL; p++) { 1484 fprintf(stderr, "\t %s\n", *p); 1485 } 1486 } 1487#endif 1488 for (p = tty_list; *p != NULL; p++) { 1489 error = do_connect(&ctx, *p); 1490 if (error == 0) { 1491 tty = *p; 1492 break; 1493 } 1494 } 1495 if (*p == NULL) 1496 errx(1, "Failed to obtain a control port, " 1497 "try specifying one manually"); 1498 } 1499 1500 if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1501 daemonize(&ctx); 1502 1503 1504 FD_ZERO(&set); 1505 FD_SET(ctx.fd, &set); 1506 for (;;) { 1507 1508 watchdog_disable(&ctx); 1509 error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1510 if (error <= 0) { 1511 if (running && errno == EINTR) 1512 continue; 1513 if (ctx.flags & FLG_WDEXP) { 1514 ctx.flags &= ~FLG_WDEXP; 1515 watchdog_reset(&ctx, 5); 1516 do_disconnect(&ctx); 1517 watchdog_reset(&ctx, 15); 1518 do_connect(&ctx, tty); 1519 running = 1; 1520 continue; 1521 } 1522 1523 break; 1524 } 1525 1526 if (FD_ISSET(ctx.fd, &set)) { 1527 watchdog_reset(&ctx, 15); 1528 error = at_async(&ctx, &ctx); 1529 if (error != 0) 1530 break; 1531 } 1532 FD_SET(ctx.fd, &set); 1533 1534 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1535 printf("Status: %s (%s)", 1536 ctx.con_status ? "connected" : "disconnected", 1537 network_access_type[ctx.con_net_type]); 1538 if (ctx.dbm < 0) 1539 printf(", signal: %d dBm", ctx.dbm); 1540 printf("\t\t\t\r"); 1541 fflush(stdout); 1542 } 1543 } 1544 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1545 printf("\n"); 1546 1547 signal(SIGHUP, SIG_DFL); 1548 signal(SIGINT, SIG_DFL); 1549 signal(SIGQUIT, SIG_DFL); 1550 signal(SIGTERM, SIG_DFL); 1551 signal(SIGALRM, SIG_IGN); 1552 1553 do_disconnect(&ctx); 1554 1555 if (ctx.flags & FLG_DAEMON) { 1556 pidfile_remove(ctx.pfh); 1557 if (syslog_open) 1558 closelog(); 1559 } 1560 1561 return (0); 1562} 1563