1202181Sthompsa/*- 2202181Sthompsa * Copyright (c) 2008-2009 Fredrik Lindberg 3202181Sthompsa * All rights reserved. 4202181Sthompsa * 5202181Sthompsa * Redistribution and use in source and binary forms, with or without 6202181Sthompsa * modification, are permitted provided that the following conditions 7202181Sthompsa * are met: 8202181Sthompsa * 1. Redistributions of source code must retain the above copyright 9202181Sthompsa * notice, this list of conditions and the following disclaimer. 10202181Sthompsa * 2. Redistributions in binary form must reproduce the above copyright 11202181Sthompsa * notice, this list of conditions and the following disclaimer in the 12202181Sthompsa * documentation and/or other materials provided with the distribution. 13202181Sthompsa * 14202181Sthompsa * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15202181Sthompsa * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16202181Sthompsa * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17202181Sthompsa * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18202181Sthompsa * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19202181Sthompsa * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20202181Sthompsa * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21202181Sthompsa * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22202181Sthompsa * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23202181Sthompsa * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24202181Sthompsa * 25202181Sthompsa * $FreeBSD$ 26202181Sthompsa */ 27202181Sthompsa 28202181Sthompsa#include <sys/types.h> 29202181Sthompsa#include <sys/param.h> 30202181Sthompsa#include <sys/socket.h> 31202181Sthompsa#include <sys/sockio.h> 32202181Sthompsa#include <sys/select.h> 33202181Sthompsa#include <sys/stat.h> 34202181Sthompsa#include <sys/sysctl.h> 35202181Sthompsa#include <sys/time.h> 36202181Sthompsa#include <sys/queue.h> 37202181Sthompsa 38202181Sthompsa#include <arpa/inet.h> 39202181Sthompsa#include <net/if.h> 40202181Sthompsa#include <net/if_var.h> 41202181Sthompsa#include <net/if_dl.h> 42202181Sthompsa#include <net/route.h> 43202181Sthompsa#include <netinet/in.h> 44202181Sthompsa#include <netinet/in_var.h> 45202181Sthompsa 46202181Sthompsa#include <err.h> 47202181Sthompsa#include <errno.h> 48202181Sthompsa#include <fcntl.h> 49202181Sthompsa#include <termios.h> 50202181Sthompsa#include <stdarg.h> 51202181Sthompsa#include <stdio.h> 52202181Sthompsa#include <stdlib.h> 53202181Sthompsa#include <stdint.h> 54202181Sthompsa#include <string.h> 55202181Sthompsa#include <signal.h> 56202181Sthompsa#include <syslog.h> 57202181Sthompsa#include <unistd.h> 58202181Sthompsa#include <ifaddrs.h> 59202181Sthompsa#include <libutil.h> 60202181Sthompsa#include <time.h> 61202181Sthompsa 62202181Sthompsa/* 63202181Sthompsa * Connection utility to ease connectivity using the raw IP packet interface 64202181Sthompsa * available on uhso(4) devices. 65202181Sthompsa */ 66202181Sthompsa 67202181Sthompsa#define TTY_NAME "/dev/%s" 68202181Sthompsa#define SYSCTL_TEST "dev.uhso.%d.%%driver" 69210276Sthompsa#define SYSCTL_LOCATION "dev.uhso.%d.%%location" 70202181Sthompsa#define SYSCTL_PORTS "dev.uhso.%d.ports" 71202181Sthompsa#define SYSCTL_NETIF "dev.uhso.%d.netif" 72202181Sthompsa#define SYSCTL_NAME_TTY "dev.uhso.%d.port.%s.tty" 73202181Sthompsa#define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc" 74202181Sthompsa#define RESOLV_PATH "/etc/resolv.conf" 75202181Sthompsa#define PIDFILE "/var/run/uhsoctl.%s.pid" 76202181Sthompsa 77202181Sthompsastatic const char *network_access_type[] = { 78202181Sthompsa "GSM", 79202181Sthompsa "Compact GSM", 80202181Sthompsa "UMTS", 81202181Sthompsa "GSM (EGPRS)", 82202181Sthompsa "HSDPA", 83202181Sthompsa "HSUPA", 84202181Sthompsa "HSDPA/HSUPA" 85202181Sthompsa}; 86202181Sthompsa 87202181Sthompsastatic const char *network_reg_status[] = { 88202181Sthompsa "Not registered", 89202181Sthompsa "Registered", 90202181Sthompsa "Searching for network", 91202181Sthompsa "Network registration denied", 92202181Sthompsa "Unknown", 93202181Sthompsa "Registered (roaming)" 94202181Sthompsa}; 95202181Sthompsa 96202181Sthompsastruct ctx { 97202181Sthompsa int fd; 98202181Sthompsa int flags; 99202181Sthompsa#define IPASSIGNED 0x01 100202181Sthompsa#define FLG_NODAEMON 0x02 /* Don't detach from terminal */ 101202181Sthompsa#define FLG_DAEMON 0x04 /* Running as daemon */ 102202181Sthompsa#define FLG_DELAYED 0x08 /* Fork into background after connect */ 103202181Sthompsa#define FLG_NEWDATA 0x10 104202181Sthompsa#define FLG_WATCHDOG 0x20 /* Watchdog enabled */ 105202181Sthompsa#define FLG_WDEXP 0x40 /* Watchdog expired */ 106202181Sthompsa const char *ifnam; 107202181Sthompsa const char *pin; /* device PIN */ 108202181Sthompsa 109202181Sthompsa char pidfile[128]; 110202181Sthompsa struct pidfh *pfh; 111202181Sthompsa 112202181Sthompsa time_t watchdog; 113202181Sthompsa 114202181Sthompsa /* PDP context settings */ 115202181Sthompsa int pdp_ctx; 116202181Sthompsa const char *pdp_apn; 117202181Sthompsa const char *pdp_user; 118202181Sthompsa const char *pdp_pwd; 119202181Sthompsa 120202181Sthompsa /* Connection status */ 121202181Sthompsa int con_status; /* Connected? */ 122202181Sthompsa char *con_apn; /* Connected APN */ 123202181Sthompsa char *con_oper; /* Operator name */ 124202181Sthompsa int con_net_stat; /* Network connection status */ 125202181Sthompsa int con_net_type; /* Network connection type */ 126202181Sthompsa 127202181Sthompsa /* Misc. status */ 128202181Sthompsa int dbm; 129202181Sthompsa 130202181Sthompsa /* IP and nameserver settings */ 131202181Sthompsa struct in_addr ip; 132202181Sthompsa char **ns; 133202181Sthompsa const char *resolv_path; 134202181Sthompsa char *resolv; /* Old resolv.conf */ 135202181Sthompsa size_t resolv_sz; 136202181Sthompsa}; 137202181Sthompsa 138202181Sthompsastatic int readline_buf(const char *, const char *, char *, size_t); 139202181Sthompsastatic int readline(int, char *, size_t); 140202181Sthompsastatic void daemonize(struct ctx *); 141202181Sthompsa 142202181Sthompsastatic int at_cmd_async(int, const char *, ...); 143202181Sthompsa 144202181Sthompsatypedef union { 145202181Sthompsa void *ptr; 146202181Sthompsa uint32_t int32; 147202181Sthompsa} resp_data; 148202181Sthompsatypedef struct { 149202181Sthompsa resp_data val[2]; 150202181Sthompsa} resp_arg; 151202181Sthompsatypedef void (*resp_cb)(resp_arg *, const char *, const char *); 152202181Sthompsa 153202181Sthompsatypedef void (*async_cb)(void *, const char *); 154202181Sthompsastruct async_handle { 155202181Sthompsa const char *cmd; 156202181Sthompsa async_cb func; 157202181Sthompsa}; 158202181Sthompsa 159202181Sthompsastatic void at_async_creg(void *, const char *); 160202181Sthompsastatic void at_async_cgreg(void *, const char *); 161202181Sthompsastatic void at_async_cops(void *, const char *); 162202181Sthompsastatic void at_async_owancall(void *, const char *); 163202181Sthompsastatic void at_async_owandata(void *, const char *); 164202181Sthompsastatic void at_async_csq(void *, const char *); 165202181Sthompsa 166202181Sthompsastatic struct async_handle async_cmd[] = { 167202181Sthompsa { "+CREG", at_async_creg }, 168202181Sthompsa { "+CGREG", at_async_cgreg }, 169202181Sthompsa { "+COPS", at_async_cops }, 170202181Sthompsa { "+CSQ", at_async_csq }, 171202181Sthompsa { "_OWANCALL", at_async_owancall }, 172202181Sthompsa { "_OWANDATA", at_async_owandata }, 173202181Sthompsa { NULL, NULL } 174202181Sthompsa}; 175202181Sthompsa 176202181Sthompsastruct timer_entry; 177202181Sthompsastruct timers { 178202181Sthompsa TAILQ_HEAD(, timer_entry) head; 179202181Sthompsa int res; 180202181Sthompsa}; 181202181Sthompsa 182202181Sthompsatypedef void (*tmr_cb)(int, void *); 183202181Sthompsastruct timer_entry { 184202181Sthompsa TAILQ_ENTRY(timer_entry) next; 185202181Sthompsa int id; 186202181Sthompsa int timeout; 187202181Sthompsa tmr_cb func; 188202181Sthompsa void *arg; 189202181Sthompsa}; 190202181Sthompsa 191202181Sthompsa 192202181Sthompsastatic struct timers timers; 193202181Sthompsastatic volatile int running = 1; 194202181Sthompsastatic int syslog_open = 0; 195202181Sthompsastatic char syslog_title[64]; 196202181Sthompsa 197202181Sthompsa/* Periodic timer, runs ready timer tasks every tick */ 198202181Sthompsastatic void 199202181Sthompsatmr_run(struct timers *tmrs) 200202181Sthompsa{ 201202181Sthompsa struct timer_entry *te, *te2; 202202181Sthompsa 203202181Sthompsa te = TAILQ_FIRST(&tmrs->head); 204202181Sthompsa if (te == NULL) 205202181Sthompsa return; 206202181Sthompsa 207202181Sthompsa te->timeout -= tmrs->res; 208202181Sthompsa while (te->timeout <= 0) { 209202181Sthompsa te2 = TAILQ_NEXT(te, next); 210202181Sthompsa TAILQ_REMOVE(&tmrs->head, te, next); 211202181Sthompsa te->func(te->id, te->arg); 212202181Sthompsa free(te); 213202181Sthompsa te = te2; 214202181Sthompsa if (te == NULL) 215202181Sthompsa break; 216202181Sthompsa } 217202181Sthompsa} 218202181Sthompsa 219202181Sthompsa/* Add a new timer */ 220202181Sthompsastatic void 221202181Sthompsatmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg) 222202181Sthompsa{ 223202181Sthompsa struct timer_entry *te, *te2, *te3; 224202181Sthompsa 225202181Sthompsa te = malloc(sizeof(struct timer_entry)); 226202181Sthompsa memset(te, 0, sizeof(struct timer_entry)); 227202181Sthompsa 228202181Sthompsa te->timeout = timeout; 229202181Sthompsa te->func = func; 230202181Sthompsa te->arg = arg; 231202181Sthompsa te->id = id; 232202181Sthompsa 233202181Sthompsa te2 = TAILQ_FIRST(&tmrs->head); 234202181Sthompsa 235202181Sthompsa if (TAILQ_EMPTY(&tmrs->head)) { 236202181Sthompsa TAILQ_INSERT_HEAD(&tmrs->head, te, next); 237202181Sthompsa } else if (te->timeout < te2->timeout) { 238202181Sthompsa te2->timeout -= te->timeout; 239202181Sthompsa TAILQ_INSERT_HEAD(&tmrs->head, te, next); 240202181Sthompsa } else { 241202181Sthompsa while (te->timeout >= te2->timeout) { 242202181Sthompsa te->timeout -= te2->timeout; 243202181Sthompsa te3 = TAILQ_NEXT(te2, next); 244202181Sthompsa if (te3 == NULL || te3->timeout > te->timeout) 245202181Sthompsa break; 246202181Sthompsa te2 = te3; 247202181Sthompsa } 248202181Sthompsa TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next); 249202181Sthompsa } 250202181Sthompsa} 251202181Sthompsa 252202181Sthompsa#define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG 253202181Sthompsa#define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG 254202181Sthompsa 255202181Sthompsastatic void 256202181Sthompsawatchdog_reset(struct ctx *ctx, int timeout) 257202181Sthompsa{ 258202181Sthompsa struct timespec tp; 259202181Sthompsa 260202181Sthompsa clock_gettime(CLOCK_MONOTONIC, &tp), 261202181Sthompsa ctx->watchdog = tp.tv_sec + timeout; 262202181Sthompsa 263202181Sthompsa watchdog_enable(ctx); 264202181Sthompsa} 265202181Sthompsa 266202181Sthompsastatic void 267202181Sthompsatmr_creg(int id, void *arg) 268202181Sthompsa{ 269202181Sthompsa struct ctx *ctx = arg; 270202181Sthompsa 271202181Sthompsa at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 272202181Sthompsa watchdog_reset(ctx, 10); 273202181Sthompsa} 274202181Sthompsa 275202181Sthompsastatic void 276202181Sthompsatmr_cgreg(int id, void *arg) 277202181Sthompsa{ 278202181Sthompsa struct ctx *ctx = arg; 279202181Sthompsa 280202181Sthompsa at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 281202181Sthompsa watchdog_reset(ctx, 10); 282202181Sthompsa} 283202181Sthompsa 284202181Sthompsastatic void 285202181Sthompsatmr_status(int id, void *arg) 286202181Sthompsa{ 287202181Sthompsa struct ctx *ctx = arg; 288202181Sthompsa 289202181Sthompsa at_cmd_async(ctx->fd, "AT+CSQ\r\n"); 290202181Sthompsa watchdog_reset(ctx, 10); 291202181Sthompsa} 292202181Sthompsa 293202181Sthompsastatic void 294202181Sthompsatmr_watchdog(int id, void *arg) 295202181Sthompsa{ 296202181Sthompsa struct ctx *ctx = arg; 297202181Sthompsa pid_t self; 298202181Sthompsa struct timespec tp; 299202181Sthompsa 300202181Sthompsa tmr_add(&timers, 1, 5, tmr_watchdog, ctx); 301202181Sthompsa 302202181Sthompsa if (!(ctx->flags & FLG_WATCHDOG)) 303202181Sthompsa return; 304202181Sthompsa 305202181Sthompsa clock_gettime(CLOCK_MONOTONIC, &tp); 306202181Sthompsa 307202181Sthompsa if (tp.tv_sec >= ctx->watchdog) { 308202181Sthompsa#ifdef DEBUG 309202181Sthompsa fprintf(stderr, "Watchdog expired\n"); 310202181Sthompsa#endif 311202181Sthompsa ctx->flags |= FLG_WDEXP; 312202181Sthompsa self = getpid(); 313202181Sthompsa kill(self, SIGHUP); 314202181Sthompsa } 315202181Sthompsa} 316202181Sthompsa 317202181Sthompsastatic void 318202181Sthompsasig_handle(int sig) 319202181Sthompsa{ 320202181Sthompsa 321202181Sthompsa switch (sig) { 322202181Sthompsa case SIGHUP: 323202181Sthompsa case SIGINT: 324202181Sthompsa case SIGQUIT: 325202181Sthompsa case SIGTERM: 326202181Sthompsa running = 0; 327202181Sthompsa break; 328202181Sthompsa case SIGALRM: 329202181Sthompsa tmr_run(&timers); 330202181Sthompsa break; 331202181Sthompsa } 332202181Sthompsa} 333202181Sthompsa 334202181Sthompsastatic void 335202181Sthompsalogger(int pri, const char *fmt, ...) 336202181Sthompsa{ 337202181Sthompsa char *buf; 338202181Sthompsa va_list ap; 339202181Sthompsa 340202181Sthompsa va_start(ap, fmt); 341202181Sthompsa vasprintf(&buf, fmt, ap); 342202181Sthompsa if (syslog_open) 343228721Sdim syslog(pri, "%s", buf); 344202181Sthompsa else { 345202181Sthompsa switch (pri) { 346202181Sthompsa case LOG_INFO: 347202181Sthompsa case LOG_NOTICE: 348202181Sthompsa printf("%s\n", buf); 349202181Sthompsa break; 350202181Sthompsa default: 351202181Sthompsa fprintf(stderr, "%s: %s\n", getprogname(), buf); 352202181Sthompsa break; 353202181Sthompsa } 354202181Sthompsa } 355202181Sthompsa 356202181Sthompsa free(buf); 357202181Sthompsa va_end(ap); 358202181Sthompsa} 359202181Sthompsa 360202181Sthompsa/* Add/remove IP address from an interface */ 361202181Sthompsastatic int 362202181Sthompsaifaddr_ad(int d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 363202181Sthompsa{ 364202181Sthompsa struct ifaliasreq req; 365202181Sthompsa int fd, error; 366202181Sthompsa 367202181Sthompsa fd = socket(AF_INET, SOCK_DGRAM, 0); 368202181Sthompsa if (fd < 0) 369202181Sthompsa return (-1); 370202181Sthompsa 371202181Sthompsa memset(&req, 0, sizeof(struct ifaliasreq)); 372202181Sthompsa strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name)); 373202181Sthompsa memcpy(&req.ifra_addr, sa, sa->sa_len); 374202181Sthompsa memcpy(&req.ifra_mask, mask, mask->sa_len); 375202181Sthompsa 376202181Sthompsa error = ioctl(fd, d, (char *)&req); 377202181Sthompsa close(fd); 378202181Sthompsa return (error); 379202181Sthompsa} 380202181Sthompsa 381202181Sthompsa#define if_ifup(ifnam) if_setflags(ifnam, IFF_UP) 382202181Sthompsa#define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP) 383202181Sthompsa 384202181Sthompsastatic int 385202181Sthompsaif_setflags(const char *ifnam, int flags) 386202181Sthompsa{ 387202181Sthompsa struct ifreq ifr; 388202181Sthompsa int fd, error; 389202181Sthompsa unsigned int oflags = 0; 390202181Sthompsa 391202181Sthompsa memset(&ifr, 0, sizeof(struct ifreq)); 392202181Sthompsa strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 393202181Sthompsa 394202181Sthompsa fd = socket(AF_INET, SOCK_DGRAM, 0); 395202181Sthompsa if (fd < 0) 396202181Sthompsa return (-1); 397202181Sthompsa 398202181Sthompsa error = ioctl(fd, SIOCGIFFLAGS, &ifr); 399202181Sthompsa if (error == 0) { 400202181Sthompsa oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 401202181Sthompsa } 402202181Sthompsa 403202181Sthompsa if (flags < 0) 404202181Sthompsa oflags &= ~(-flags); 405202181Sthompsa else 406202181Sthompsa oflags |= flags; 407202181Sthompsa 408202181Sthompsa ifr.ifr_flags = oflags & 0xffff; 409202181Sthompsa ifr.ifr_flagshigh = oflags >> 16; 410202181Sthompsa 411202181Sthompsa error = ioctl(fd, SIOCSIFFLAGS, &ifr); 412202181Sthompsa if (error != 0) 413202181Sthompsa warn("ioctl SIOCSIFFLAGS"); 414202181Sthompsa 415202181Sthompsa close(fd); 416202181Sthompsa return (error); 417202181Sthompsa} 418202181Sthompsa 419202181Sthompsastatic int 420202181Sthompsaifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 421202181Sthompsa{ 422202181Sthompsa int error; 423202181Sthompsa 424202181Sthompsa error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask); 425202181Sthompsa if (error != 0) 426202181Sthompsa warn("ioctl SIOCAIFADDR"); 427202181Sthompsa return (error); 428202181Sthompsa} 429202181Sthompsa 430202181Sthompsastatic int 431202181Sthompsaifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 432202181Sthompsa{ 433202181Sthompsa int error; 434202181Sthompsa 435202181Sthompsa error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask); 436202181Sthompsa if (error != 0) 437202181Sthompsa warn("ioctl SIOCDIFADDR"); 438202181Sthompsa return (error); 439202181Sthompsa} 440202181Sthompsa 441202181Sthompsastatic int 442202181Sthompsaset_nameservers(struct ctx *ctx, const char *respath, int ns, ...) 443202181Sthompsa{ 444202181Sthompsa int i, n, fd; 445202181Sthompsa FILE *fp; 446202181Sthompsa char *p; 447202181Sthompsa va_list ap; 448202181Sthompsa struct stat sb; 449202181Sthompsa char buf[512]; 450202181Sthompsa 451202181Sthompsa if (ctx->ns != NULL) { 452202181Sthompsa for (i = 0; ctx->ns[i] != NULL; i++) { 453202181Sthompsa free(ctx->ns[i]); 454202181Sthompsa } 455202181Sthompsa free(ctx->ns); 456202181Sthompsa } 457202181Sthompsa 458229467Spjd fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666); 459202181Sthompsa if (fd < 0) 460202181Sthompsa return (-1); 461202181Sthompsa 462202181Sthompsa if (ns == 0) { 463202181Sthompsa /* Attempt to restore old resolv.conf */ 464202181Sthompsa if (ctx->resolv != NULL) { 465202181Sthompsa ftruncate(fd, 0); 466202181Sthompsa lseek(fd, 0, SEEK_SET); 467202181Sthompsa write(fd, ctx->resolv, ctx->resolv_sz); 468202181Sthompsa free(ctx->resolv); 469202181Sthompsa ctx->resolv = NULL; 470202181Sthompsa ctx->resolv_sz = 0; 471202181Sthompsa } 472202181Sthompsa close(fd); 473202181Sthompsa return (0); 474202181Sthompsa } 475202181Sthompsa 476202181Sthompsa 477202181Sthompsa ctx->ns = malloc(sizeof(char *) * (ns + 1)); 478202181Sthompsa if (ctx->ns == NULL) { 479202181Sthompsa close(fd); 480202181Sthompsa return (-1); 481202181Sthompsa } 482202181Sthompsa 483202181Sthompsa va_start(ap, ns); 484202181Sthompsa for (i = 0; i < ns; i++) { 485202181Sthompsa p = va_arg(ap, char *); 486202181Sthompsa ctx->ns[i] = strdup(p); 487202181Sthompsa } 488202181Sthompsa ctx->ns[i] = NULL; 489202181Sthompsa va_end(ap); 490202181Sthompsa 491202181Sthompsa /* Attempt to backup the old resolv.conf */ 492202181Sthompsa if (ctx->resolv == NULL) { 493202181Sthompsa i = fstat(fd, &sb); 494202181Sthompsa if (i == 0 && sb.st_size != 0) { 495202181Sthompsa ctx->resolv_sz = sb.st_size; 496202181Sthompsa ctx->resolv = malloc(sb.st_size); 497202181Sthompsa if (ctx->resolv != NULL) { 498202181Sthompsa n = read(fd, ctx->resolv, sb.st_size); 499202181Sthompsa if (n != sb.st_size) { 500202181Sthompsa free(ctx->resolv); 501202181Sthompsa ctx->resolv = NULL; 502202181Sthompsa } 503202181Sthompsa } 504202181Sthompsa } 505202181Sthompsa } 506202181Sthompsa 507202181Sthompsa 508202181Sthompsa ftruncate(fd, 0); 509202181Sthompsa lseek(fd, 0, SEEK_SET); 510202181Sthompsa fp = fdopen(fd, "w"); 511202181Sthompsa 512202181Sthompsa /* 513202181Sthompsa * Write back everything other than nameserver entries to the 514202181Sthompsa * new resolv.conf 515202181Sthompsa */ 516202181Sthompsa if (ctx->resolv != NULL) { 517202181Sthompsa p = ctx->resolv; 518202181Sthompsa while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 519202181Sthompsa sizeof(buf))) > 0) { 520202181Sthompsa p += i; 521202181Sthompsa if (strncasecmp(buf, "nameserver", 10) == 0) 522202181Sthompsa continue; 523202181Sthompsa fprintf(fp, "%s", buf); 524202181Sthompsa } 525202181Sthompsa } 526202181Sthompsa 527202181Sthompsa for (i = 0; ctx->ns[i] != NULL; i++) { 528202181Sthompsa fprintf(fp, "nameserver %s\n", ctx->ns[i]); 529202181Sthompsa } 530202181Sthompsa fclose(fp); 531202181Sthompsa return (0); 532202181Sthompsa} 533202181Sthompsa 534202181Sthompsa/* Read a \n-terminated line from buffer */ 535202181Sthompsastatic int 536202181Sthompsareadline_buf(const char *s, const char *e, char *buf, size_t bufsz) 537202181Sthompsa{ 538202181Sthompsa int pos = 0; 539202181Sthompsa char *p = buf; 540202181Sthompsa 541202181Sthompsa for (; s < e; s++) { 542202181Sthompsa *p = *s; 543202181Sthompsa pos++; 544202181Sthompsa if (pos >= (bufsz - 1)) 545202181Sthompsa break; 546202181Sthompsa if (*p++ == '\n') 547202181Sthompsa break; 548202181Sthompsa } 549202181Sthompsa *p = '\0'; 550202181Sthompsa return (pos); 551202181Sthompsa} 552202181Sthompsa 553202181Sthompsa/* Read a \n-terminated line from file */ 554202181Sthompsastatic int 555202181Sthompsareadline(int fd, char *buf, size_t bufsz) 556202181Sthompsa{ 557202181Sthompsa int n = 0, pos = 0; 558202181Sthompsa char *p = buf; 559202181Sthompsa 560202181Sthompsa for (;;) { 561202181Sthompsa n = read(fd, p, 1); 562202181Sthompsa if (n <= 0) 563202181Sthompsa break; 564202181Sthompsa pos++; 565202181Sthompsa if (pos >= (bufsz - 1)) 566202181Sthompsa break; 567202181Sthompsa if (*p++ == '\n') 568202181Sthompsa break; 569202181Sthompsa } 570202181Sthompsa *p = '\0'; 571202181Sthompsa return (n <= 0 ? n : pos); 572202181Sthompsa} 573202181Sthompsa 574202181Sthompsa/* 575202181Sthompsa * Synchronous AT command 576202181Sthompsa */ 577202181Sthompsastatic int 578202181Sthompsaat_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 579202181Sthompsa{ 580210276Sthompsa char buf[512]; 581210276Sthompsa char cmd[64]; 582202181Sthompsa size_t l; 583202181Sthompsa int n, error, retval = 0; 584202181Sthompsa va_list ap; 585202181Sthompsa fd_set set; 586210276Sthompsa char *p; 587202181Sthompsa 588202181Sthompsa va_start(ap, cf); 589202181Sthompsa vsnprintf(cmd, sizeof(cmd), cf, ap); 590202181Sthompsa va_end(ap); 591202181Sthompsa 592202181Sthompsa#ifdef DEBUG 593202181Sthompsa fprintf(stderr, "SYNC_CMD: %s", cmd); 594202181Sthompsa#endif 595202181Sthompsa 596202181Sthompsa l = strlen(cmd); 597202181Sthompsa n = write(ctx->fd, cmd, l); 598202181Sthompsa if (n <= 0) 599202181Sthompsa return (-1); 600202181Sthompsa 601202181Sthompsa if (resp != NULL) { 602202181Sthompsa l = strlen(resp); 603202181Sthompsa#ifdef DEBUG 604202181Sthompsa fprintf(stderr, "SYNC_EXP: %s (%d)\n", resp, l); 605202181Sthompsa#endif 606202181Sthompsa } 607202181Sthompsa 608202181Sthompsa for (;;) { 609202181Sthompsa bzero(buf, sizeof(buf)); 610202181Sthompsa 611202181Sthompsa FD_ZERO(&set); 612202181Sthompsa watchdog_reset(ctx, 5); 613202181Sthompsa do { 614202181Sthompsa FD_SET(ctx->fd, &set); 615202181Sthompsa error = select(ctx->fd + 1, &set, NULL, NULL, NULL); 616210276Sthompsa if (ctx->flags & FLG_WDEXP) { 617202181Sthompsa watchdog_disable(ctx); 618210276Sthompsa return (-2); 619202181Sthompsa } 620202181Sthompsa } while (error <= 0 && errno == EINTR); 621210276Sthompsa watchdog_disable(ctx); 622202181Sthompsa 623202181Sthompsa if (error <= 0) { 624202181Sthompsa retval = -2; 625202181Sthompsa break; 626202181Sthompsa } 627202181Sthompsa 628202181Sthompsa n = readline(ctx->fd, buf, sizeof(buf)); 629202181Sthompsa if (n <= 0) { 630202181Sthompsa retval = -2; 631202181Sthompsa break; 632202181Sthompsa } 633202181Sthompsa 634202181Sthompsa if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) 635202181Sthompsa continue; 636202181Sthompsa 637210276Sthompsa if ((p = strchr(buf, '\r')) != NULL) 638210276Sthompsa *p = '\0'; 639210276Sthompsa else if ((p = strchr(buf, '\n')) != NULL) 640210276Sthompsa *p = '\0'; 641202181Sthompsa#ifdef DEBUG 642210276Sthompsa fprintf(stderr, "SYNC_RESP: %s\n", buf); 643202181Sthompsa#endif 644202181Sthompsa 645210276Sthompsa /* Skip local echo */ 646210276Sthompsa if (strncasecmp(cmd, buf, strlen(buf)) == 0) 647210276Sthompsa continue; 648210276Sthompsa 649210276Sthompsa if (cb != NULL) 650210276Sthompsa cb(ra, cmd, buf); 651210276Sthompsa 652202181Sthompsa if (strncmp(buf, "OK", 2) == 0) { 653210276Sthompsa retval = retval ? retval : 0; 654202181Sthompsa break; 655210276Sthompsa } else if (strstr(buf, "ERROR") != NULL) { 656202181Sthompsa retval = -1; 657202181Sthompsa break; 658202181Sthompsa } 659210276Sthompsa if (resp != NULL) 660210276Sthompsa retval = strncmp(buf, resp, l); 661202181Sthompsa } 662202181Sthompsa#ifdef DEBUG 663202181Sthompsa fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 664202181Sthompsa#endif 665202181Sthompsa return (retval); 666202181Sthompsa} 667202181Sthompsa 668202181Sthompsastatic int 669202181Sthompsaat_cmd_async(int fd, const char *cf, ...) 670202181Sthompsa{ 671202181Sthompsa size_t l; 672202181Sthompsa va_list ap; 673202181Sthompsa char cmd[64]; 674202181Sthompsa 675202181Sthompsa va_start(ap, cf); 676202181Sthompsa vsnprintf(cmd, sizeof(cmd), cf, ap); 677202181Sthompsa va_end(ap); 678202181Sthompsa 679202181Sthompsa#ifdef DEBUG 680202181Sthompsa fprintf(stderr, "CMD: %s", cmd); 681202181Sthompsa#endif 682202181Sthompsa l = strlen(cmd); 683202181Sthompsa return (write(fd, cmd, l)); 684202181Sthompsa} 685202181Sthompsa 686202181Sthompsastatic void 687202181Sthompsasaveresp(resp_arg *ra, const char *cmd, const char *resp) 688202181Sthompsa{ 689202181Sthompsa char **buf; 690202181Sthompsa int i = ra->val[1].int32; 691202181Sthompsa 692210276Sthompsa#ifdef DEBUG 693210276Sthompsa fprintf(stderr, "Save '%s'\n", resp); 694210276Sthompsa#endif 695210276Sthompsa 696202181Sthompsa buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 697202181Sthompsa if (buf == NULL) 698202181Sthompsa return; 699202181Sthompsa 700202181Sthompsa buf[i] = strdup(resp); 701202181Sthompsa 702202181Sthompsa ra->val[0].ptr = buf; 703202181Sthompsa ra->val[1].int32 = i + 1; 704202181Sthompsa} 705202181Sthompsa 706202181Sthompsastatic void 707210276Sthompsafreeresp(resp_arg *ra) 708210276Sthompsa{ 709210276Sthompsa char **buf; 710210276Sthompsa int i; 711210276Sthompsa 712210276Sthompsa buf = ra->val[0].ptr; 713210276Sthompsa for (i = 0; i < ra->val[1].int32; i++) { 714210276Sthompsa free(buf[i]); 715210276Sthompsa } 716210276Sthompsa free(buf); 717210276Sthompsa} 718210276Sthompsa 719210276Sthompsastatic void 720202181Sthompsaat_async_creg(void *arg, const char *resp) 721202181Sthompsa{ 722202181Sthompsa struct ctx *ctx = arg; 723202181Sthompsa int n, reg; 724202181Sthompsa 725202181Sthompsa n = sscanf(resp, "+CREG: %*d,%d", ®); 726202181Sthompsa if (n != 1) { 727202181Sthompsa n = sscanf(resp, "+CREG: %d", ®); 728202181Sthompsa if (n != 1) 729202181Sthompsa return; 730202181Sthompsa } 731202181Sthompsa 732202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 733202181Sthompsa tmr_add(&timers, 1, 1, tmr_creg, ctx); 734202181Sthompsa } 735202181Sthompsa else { 736202181Sthompsa tmr_add(&timers, 1, 30, tmr_creg, ctx); 737202181Sthompsa } 738202181Sthompsa 739202181Sthompsa if (ctx->con_net_stat == reg) 740202181Sthompsa return; 741202181Sthompsa 742202181Sthompsa ctx->con_net_stat = reg; 743202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 744202181Sthompsa} 745202181Sthompsa 746202181Sthompsastatic void 747202181Sthompsaat_async_cgreg(void *arg, const char *resp) 748202181Sthompsa{ 749202181Sthompsa struct ctx *ctx = arg; 750202181Sthompsa int n, reg; 751202181Sthompsa 752202181Sthompsa n = sscanf(resp, "+CGREG: %*d,%d", ®); 753202181Sthompsa if (n != 1) { 754202181Sthompsa n = sscanf(resp, "+CGREG: %d", ®); 755202181Sthompsa if (n != 1) 756202181Sthompsa return; 757202181Sthompsa } 758202181Sthompsa 759202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 760202181Sthompsa tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 761202181Sthompsa } 762202181Sthompsa else { 763202181Sthompsa tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 764202181Sthompsa } 765202181Sthompsa 766202181Sthompsa if (ctx->con_net_stat == reg) 767202181Sthompsa return; 768202181Sthompsa 769202181Sthompsa ctx->con_net_stat = reg; 770202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 771202181Sthompsa} 772202181Sthompsa 773202181Sthompsa 774202181Sthompsastatic void 775202181Sthompsaat_async_cops(void *arg, const char *resp) 776202181Sthompsa{ 777202181Sthompsa struct ctx *ctx = arg; 778202181Sthompsa int n, at; 779202181Sthompsa char opr[64]; 780202181Sthompsa 781202181Sthompsa n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 782202181Sthompsa opr, &at); 783202181Sthompsa if (n != 2) 784202181Sthompsa return; 785202181Sthompsa 786202181Sthompsa if (ctx->con_oper != NULL) { 787202181Sthompsa if (ctx->con_net_type == at && 788202181Sthompsa strcasecmp(opr, ctx->con_oper) == 0) 789202181Sthompsa return; 790202181Sthompsa free(ctx->con_oper); 791202181Sthompsa } 792202181Sthompsa 793202181Sthompsa ctx->con_oper = strdup(opr); 794202181Sthompsa ctx->con_net_type = at; 795202181Sthompsa 796202181Sthompsa if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 797202181Sthompsa logger(LOG_NOTICE, "%s to \"%s\" (%s)", 798202181Sthompsa network_reg_status[ctx->con_net_stat], 799202181Sthompsa ctx->con_oper, network_access_type[ctx->con_net_type]); 800202181Sthompsa if (ctx->con_status != 1) { 801202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 802202181Sthompsa ctx->pdp_ctx); 803202181Sthompsa } 804202181Sthompsa } 805202181Sthompsa else { 806202181Sthompsa logger(LOG_NOTICE, "%s (%s)", 807202181Sthompsa network_reg_status[ctx->con_net_stat], 808202181Sthompsa network_access_type[ctx->con_net_type]); 809202181Sthompsa } 810202181Sthompsa} 811202181Sthompsa 812202181Sthompsa/* 813202181Sthompsa * Signal strength for pretty console output 814202181Sthompsa * 815202181Sthompsa * From 3GPP TS 27.007 V8.3.0, Section 8.5 816202181Sthompsa * 0 = -113 dBm or less 817202181Sthompsa * 1 = -111 dBm 818202181Sthompsa * 2...30 = -109...-53 dBm 819202181Sthompsa * 31 = -51 dBm or greater 820202181Sthompsa * 821202181Sthompsa * So, dbm = (rssi * 2) - 113 822202181Sthompsa*/ 823202181Sthompsastatic void 824202181Sthompsaat_async_csq(void *arg, const char *resp) 825202181Sthompsa{ 826202181Sthompsa struct ctx *ctx = arg; 827202181Sthompsa int n, rssi; 828202181Sthompsa 829202181Sthompsa n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 830202181Sthompsa if (n != 1) 831202181Sthompsa return; 832202181Sthompsa if (rssi == 99) 833202181Sthompsa ctx->dbm = 0; 834202181Sthompsa else { 835202181Sthompsa ctx->dbm = (rssi * 2) - 113; 836202181Sthompsa tmr_add(&timers, 1, 15, tmr_status, ctx); 837202181Sthompsa } 838202181Sthompsa 839202181Sthompsa ctx->flags |= FLG_NEWDATA; 840202181Sthompsa} 841202181Sthompsa 842202181Sthompsastatic void 843202181Sthompsaat_async_owancall(void *arg, const char *resp) 844202181Sthompsa{ 845202181Sthompsa struct ctx *ctx = arg; 846202181Sthompsa int n, i; 847202181Sthompsa 848202181Sthompsa n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 849202181Sthompsa if (n != 1) 850202181Sthompsa return; 851202181Sthompsa 852202181Sthompsa if (i == ctx->con_status) 853202181Sthompsa return; 854202181Sthompsa 855202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 856202181Sthompsa 857202181Sthompsa ctx->con_status = i; 858202181Sthompsa if (ctx->con_status == 1) { 859202181Sthompsa logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 860202181Sthompsa ctx->con_oper, ctx->con_apn, 861202181Sthompsa network_access_type[ctx->con_net_type]); 862202181Sthompsa } 863202181Sthompsa else { 864202181Sthompsa logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 865202181Sthompsa ctx->con_oper, ctx->con_apn); 866202181Sthompsa } 867202181Sthompsa} 868202181Sthompsa 869202181Sthompsastatic void 870202181Sthompsaat_async_owandata(void *arg, const char *resp) 871202181Sthompsa{ 872202181Sthompsa struct ctx *ctx = arg; 873202181Sthompsa char ip[40], ns1[40], ns2[40]; 874202181Sthompsa int n, error, rs; 875202181Sthompsa struct ifaddrs *ifap, *ifa; 876202181Sthompsa struct sockaddr_in sin, mask; 877202181Sthompsa struct sockaddr_dl sdl; 878202181Sthompsa struct { 879202181Sthompsa struct rt_msghdr rtm; 880202181Sthompsa char buf[512]; 881202181Sthompsa } r; 882202181Sthompsa char *cp = r.buf; 883202181Sthompsa 884202181Sthompsa n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 885202181Sthompsa ip, ns1, ns2); 886202181Sthompsa if (n != 3) 887202181Sthompsa return; 888202181Sthompsa 889202181Sthompsa /* XXX: AF_INET assumption */ 890202181Sthompsa 891202181Sthompsa logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 892202181Sthompsa 893202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 894202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 895202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 896202181Sthompsa 897202181Sthompsa if (ctx->flags & IPASSIGNED) { 898202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 899202181Sthompsa sizeof(sin.sin_addr.s_addr)); 900202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 901202181Sthompsa (struct sockaddr *)&mask); 902202181Sthompsa } 903202181Sthompsa inet_pton(AF_INET, ip, &ctx->ip.s_addr); 904202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 905202181Sthompsa sizeof(sin.sin_addr.s_addr)); 906202181Sthompsa 907202181Sthompsa error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 908202181Sthompsa (struct sockaddr *)&mask); 909202181Sthompsa if (error != 0) { 910202181Sthompsa logger(LOG_ERR, "failed to set ip-address"); 911202181Sthompsa return; 912202181Sthompsa } 913202181Sthompsa 914202181Sthompsa if_ifup(ctx->ifnam); 915202181Sthompsa 916202181Sthompsa ctx->flags |= IPASSIGNED; 917202181Sthompsa 918202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 919202181Sthompsa error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 920202181Sthompsa if (error != 0) { 921202181Sthompsa logger(LOG_ERR, "failed to set nameservers"); 922202181Sthompsa } 923202181Sthompsa 924202181Sthompsa error = getifaddrs(&ifap); 925202181Sthompsa if (error != 0) { 926202181Sthompsa logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 927202181Sthompsa return; 928202181Sthompsa } 929202181Sthompsa 930202181Sthompsa for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 931202181Sthompsa if (ifa->ifa_addr->sa_family != AF_LINK) 932202181Sthompsa continue; 933202181Sthompsa if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 934202181Sthompsa memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 935202181Sthompsa sizeof(struct sockaddr_dl)); 936202181Sthompsa break; 937202181Sthompsa } 938202181Sthompsa } 939202181Sthompsa if (ifa == NULL) 940202181Sthompsa return; 941202181Sthompsa 942202181Sthompsa rs = socket(PF_ROUTE, SOCK_RAW, 0); 943202181Sthompsa if (rs < 0) { 944202181Sthompsa logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 945202181Sthompsa return; 946202181Sthompsa } 947202181Sthompsa 948202181Sthompsa memset(&r, 0, sizeof(r)); 949202181Sthompsa 950202181Sthompsa r.rtm.rtm_version = RTM_VERSION; 951202181Sthompsa r.rtm.rtm_type = RTM_ADD; 952202181Sthompsa r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 953202181Sthompsa r.rtm.rtm_pid = getpid(); 954202181Sthompsa memset(&sin, 0, sizeof(struct sockaddr_in)); 955202181Sthompsa sin.sin_family = AF_INET; 956202181Sthompsa sin.sin_len = sizeof(struct sockaddr_in); 957202181Sthompsa 958202181Sthompsa memcpy(cp, &sin, sin.sin_len); 959202181Sthompsa cp += SA_SIZE(&sin); 960202181Sthompsa memcpy(cp, &sdl, sdl.sdl_len); 961202181Sthompsa cp += SA_SIZE(&sdl); 962202181Sthompsa memcpy(cp, &sin, sin.sin_len); 963202181Sthompsa r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 964202181Sthompsa r.rtm.rtm_msglen = sizeof(r); 965202181Sthompsa 966202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 967202181Sthompsa if (n != r.rtm.rtm_msglen) { 968202181Sthompsa r.rtm.rtm_type = RTM_DELETE; 969202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 970202181Sthompsa r.rtm.rtm_type = RTM_ADD; 971202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 972202181Sthompsa } 973202181Sthompsa 974202181Sthompsa if (n != r.rtm.rtm_msglen) { 975202181Sthompsa logger(LOG_ERR, "failed to set default route: %s", 976202181Sthompsa strerror(errno)); 977202181Sthompsa } 978202181Sthompsa close(rs); 979202181Sthompsa 980202181Sthompsa /* Delayed daemonization */ 981202181Sthompsa if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 982202181Sthompsa daemonize(ctx); 983202181Sthompsa} 984202181Sthompsa 985202181Sthompsastatic int 986202181Sthompsaat_async(struct ctx *ctx, void *arg) 987202181Sthompsa{ 988202181Sthompsa int n, i; 989202181Sthompsa size_t l; 990202181Sthompsa char buf[512]; 991202181Sthompsa 992202181Sthompsa watchdog_reset(ctx, 15); 993202181Sthompsa 994202181Sthompsa bzero(buf, sizeof(buf)); 995202181Sthompsa n = readline(ctx->fd, buf, sizeof(buf)); 996202181Sthompsa if (n <= 0) 997202181Sthompsa return (n <= 0 ? -1 : 0); 998202181Sthompsa 999202181Sthompsa#ifdef DEBUG 1000202181Sthompsa fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 1001202181Sthompsa#endif 1002202181Sthompsa for (i = 0; async_cmd[i].cmd != NULL; i++) { 1003202181Sthompsa l = strlen(async_cmd[i].cmd); 1004202181Sthompsa if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 1005202181Sthompsa async_cmd[i].func(arg, buf); 1006202181Sthompsa } 1007202181Sthompsa } 1008202181Sthompsa return (0); 1009202181Sthompsa} 1010202181Sthompsa 1011202181Sthompsastatic const char *port_type_list[] = { 1012202181Sthompsa "control", "application", "application2", NULL 1013202181Sthompsa}; 1014202181Sthompsa 1015202181Sthompsa/* 1016202181Sthompsa * Attempts to find a list of control tty for the interface 1017210276Sthompsa * FreeBSD attaches USB devices per interface so we have to go through 1018202181Sthompsa * hoops to find which ttys that belong to our network interface. 1019202181Sthompsa */ 1020202181Sthompsastatic char ** 1021202181Sthompsaget_tty(struct ctx *ctx) 1022202181Sthompsa{ 1023210276Sthompsa char buf[64], data[128]; 1024210276Sthompsa int error, i, usbport, usbport0, list_size = 0; 1025210276Sthompsa char **list = NULL; 1026202181Sthompsa size_t len; 1027210276Sthompsa const char **p, *q; 1028202181Sthompsa 1029210276Sthompsa /* 1030210276Sthompsa * Look for the network interface first 1031210276Sthompsa */ 1032202181Sthompsa for (i = 0; ; i++) { 1033210276Sthompsa /* Check if we still have uhso nodes to check */ 1034202181Sthompsa snprintf(buf, 64, SYSCTL_TEST, i); 1035202181Sthompsa len = 127; 1036202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1037210276Sthompsa data[len] = '\0'; 1038202181Sthompsa#ifdef DEBUG 1039202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1040202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1041202181Sthompsa#endif 1042210276Sthompsa if (error < 0 || strcasecmp(data, "uhso") != 0) 1043202181Sthompsa return NULL; 1044202181Sthompsa 1045210276Sthompsa /* Check if this node contains the network interface we want */ 1046202181Sthompsa snprintf(buf, 64, SYSCTL_NETIF, i); 1047202181Sthompsa len = 127; 1048202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1049210276Sthompsa data[len] = '\0'; 1050202181Sthompsa#ifdef DEBUG 1051202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1052202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1053202181Sthompsa#endif 1054210276Sthompsa if (error == 0 && strcasecmp(data, ctx->ifnam) == 0) 1055210276Sthompsa break; 1056210276Sthompsa } 1057202181Sthompsa 1058210276Sthompsa /* Figure out the USB port location */ 1059210276Sthompsa snprintf(buf, 64, SYSCTL_LOCATION, i); 1060210276Sthompsa len = 127; 1061210276Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1062210276Sthompsa data[len] = '\0'; 1063202181Sthompsa#ifdef DEBUG 1064210276Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1065210276Sthompsa buf, error, error == 0 ? data : "FAILED"); 1066202181Sthompsa#endif 1067210276Sthompsa if (error != 0) 1068210276Sthompsa return (NULL); 1069202181Sthompsa 1070210276Sthompsa q = strstr(data, "port="); 1071210276Sthompsa if (q != NULL) { 1072210276Sthompsa error = sscanf(q, " port=%d", &usbport); 1073210276Sthompsa if (error != 1) { 1074202181Sthompsa#ifdef DEBUG 1075210276Sthompsa fprintf(stderr, "failed to read usb port location from '%s'\n", data); 1076202181Sthompsa#endif 1077210276Sthompsa return (NULL); 1078202181Sthompsa } 1079210276Sthompsa } else { 1080210276Sthompsa#ifdef DEBUG 1081210276Sthompsa fprintf(stderr, "failed to parse location '%s'\n", data); 1082210276Sthompsa#endif 1083210276Sthompsa return (NULL); 1084202181Sthompsa } 1085210276Sthompsa#ifdef DEBUG 1086210276Sthompsa fprintf(stderr, "USB port location=%d\n", usbport); 1087210276Sthompsa#endif 1088202181Sthompsa 1089202181Sthompsa /* 1090210276Sthompsa * Now go through it all again but only look at those matching the 1091210276Sthompsa * usb port location we found. 1092202181Sthompsa */ 1093210276Sthompsa for (i = 0; ; i++) { 1094210276Sthompsa snprintf(buf, 64, SYSCTL_LOCATION, i); 1095202181Sthompsa len = 127; 1096210276Sthompsa memset(&data, 0, sizeof(data)); 1097202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1098210276Sthompsa if (error != 0) 1099202181Sthompsa break; 1100210276Sthompsa data[len] = '\0'; 1101210276Sthompsa q = strstr(data, "port="); 1102210276Sthompsa if (q == NULL) 1103210276Sthompsa continue; 1104210276Sthompsa sscanf(q, " port=%d", &usbport0); 1105210276Sthompsa if (usbport != usbport0) 1106210276Sthompsa continue; 1107202181Sthompsa 1108210276Sthompsa /* Try to add ports */ 1109202181Sthompsa for (p = port_type_list; *p != NULL; p++) { 1110210276Sthompsa snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1111202181Sthompsa len = 127; 1112210276Sthompsa memset(&data, 0, sizeof(data)); 1113202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1114210276Sthompsa data[len] = '\0'; 1115210276Sthompsa#ifdef DEBUG 1116210276Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1117210276Sthompsa buf, error, error == 0 ? data : "FAILED"); 1118210276Sthompsa#endif 1119202181Sthompsa if (error == 0) { 1120202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1121202181Sthompsa list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1122210276Sthompsa sprintf(list[list_size], TTY_NAME, data); 1123210276Sthompsa list_size++; 1124202181Sthompsa } 1125202181Sthompsa } 1126202181Sthompsa } 1127202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1128202181Sthompsa list[list_size] = NULL; 1129210276Sthompsa return (list); 1130202181Sthompsa} 1131202181Sthompsa 1132202181Sthompsastatic int 1133202181Sthompsado_connect(struct ctx *ctx, const char *tty) 1134202181Sthompsa{ 1135202181Sthompsa int i, error, needcfg; 1136202181Sthompsa resp_arg ra; 1137202181Sthompsa struct termios t; 1138202181Sthompsa char **buf; 1139202181Sthompsa 1140202181Sthompsa#ifdef DEBUG 1141202181Sthompsa fprintf(stderr, "Attempting to open %s\n", tty); 1142202181Sthompsa#endif 1143202181Sthompsa 1144202181Sthompsa ctx->fd = open(tty, O_RDWR); 1145202181Sthompsa if (ctx->fd < 0) { 1146202181Sthompsa#ifdef DEBUG 1147202181Sthompsa fprintf(stderr, "Failed to open %s\n", tty); 1148202181Sthompsa#endif 1149202181Sthompsa return (-1); 1150202181Sthompsa } 1151202181Sthompsa 1152202181Sthompsa tcgetattr(ctx->fd, &t); 1153202181Sthompsa t.c_oflag = 0; 1154202181Sthompsa t.c_iflag = 0; 1155202181Sthompsa t.c_cflag = CLOCAL | CREAD; 1156202181Sthompsa t.c_lflag = 0; 1157202181Sthompsa tcsetattr(ctx->fd, TCSAFLUSH, &t); 1158202181Sthompsa 1159202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1160202181Sthompsa if (error == -2) { 1161210276Sthompsa warnx("failed to read from device %s", tty); 1162202181Sthompsa return (-1); 1163202181Sthompsa } 1164202181Sthompsa 1165202181Sthompsa /* Check for PIN */ 1166202181Sthompsa error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1167202181Sthompsa if (error != 0) { 1168210276Sthompsa ra.val[0].ptr = NULL; 1169210276Sthompsa ra.val[1].int32 = 0; 1170210276Sthompsa error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n"); 1171210276Sthompsa if (ra.val[1].int32 > 0) { 1172210276Sthompsa char *p; 1173210276Sthompsa 1174210276Sthompsa buf = ra.val[0].ptr; 1175210276Sthompsa if (strstr(buf[0], "+CME ERROR:") != NULL) { 1176210276Sthompsa buf[0] += 12; 1177228721Sdim errx(1, "%s", buf[0]); 1178210276Sthompsa } 1179210276Sthompsa freeresp(&ra); 1180210276Sthompsa } else 1181210276Sthompsa freeresp(&ra); 1182210276Sthompsa 1183202181Sthompsa if (ctx->pin == NULL) { 1184202181Sthompsa errx(1, "device requires PIN"); 1185202181Sthompsa } 1186202181Sthompsa 1187202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1188202181Sthompsa ctx->pin); 1189202181Sthompsa if (error != 0) { 1190202181Sthompsa errx(1, "wrong PIN"); 1191202181Sthompsa } 1192202181Sthompsa } 1193202181Sthompsa 1194202181Sthompsa /* 1195202181Sthompsa * Check if a PDP context has been configured and configure one 1196202181Sthompsa * if needed. 1197202181Sthompsa */ 1198202181Sthompsa ra.val[0].ptr = NULL; 1199202181Sthompsa ra.val[1].int32 = 0; 1200202181Sthompsa error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1201202181Sthompsa buf = ra.val[0].ptr; 1202202181Sthompsa needcfg = 1; 1203202181Sthompsa for (i = 0; i < ra.val[1].int32; i++) { 1204202181Sthompsa char apn[256]; 1205202181Sthompsa int cid; 1206202181Sthompsa error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1207202181Sthompsa &cid, apn); 1208202181Sthompsa if (error != 2) { 1209202181Sthompsa free(buf[i]); 1210202181Sthompsa continue; 1211202181Sthompsa } 1212202181Sthompsa 1213202181Sthompsa if (cid == ctx->pdp_ctx) { 1214202181Sthompsa ctx->con_apn = strdup(apn); 1215202181Sthompsa if (ctx->pdp_apn != NULL) { 1216202181Sthompsa if (strcmp(apn, ctx->pdp_apn) == 0) 1217202181Sthompsa needcfg = 0; 1218202181Sthompsa } 1219202181Sthompsa else { 1220202181Sthompsa needcfg = 0; 1221202181Sthompsa } 1222202181Sthompsa } 1223202181Sthompsa free(buf[i]); 1224202181Sthompsa } 1225202181Sthompsa free(buf); 1226202181Sthompsa 1227202181Sthompsa if (needcfg) { 1228202181Sthompsa if (ctx->pdp_apn == NULL) 1229202181Sthompsa errx(1, "device is not configured and no APN given"); 1230202181Sthompsa 1231202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, 1232202181Sthompsa "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1233202181Sthompsa if (error != 0) { 1234202181Sthompsa errx(1, "failed to configure device"); 1235202181Sthompsa } 1236202181Sthompsa ctx->con_apn = strdup(ctx->pdp_apn); 1237202181Sthompsa } 1238202181Sthompsa 1239202181Sthompsa if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1240202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, 1241202181Sthompsa "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1242202181Sthompsa (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1243202181Sthompsa (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1244202181Sthompsa } 1245202181Sthompsa 1246202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1247202181Sthompsa ctx->pdp_ctx); 1248202181Sthompsa if (error != 0) 1249202181Sthompsa return (-1); 1250202181Sthompsa 1251202181Sthompsa at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1252202181Sthompsa at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1253202181Sthompsa 1254202181Sthompsa tmr_add(&timers, 1, 5, tmr_status, ctx); 1255202181Sthompsa return (0); 1256202181Sthompsa} 1257202181Sthompsa 1258202181Sthompsastatic void 1259202181Sthompsado_disconnect(struct ctx *ctx) 1260202181Sthompsa{ 1261202181Sthompsa struct sockaddr_in sin, mask; 1262202181Sthompsa 1263202181Sthompsa /* Disconnect */ 1264202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1265202181Sthompsa ctx->pdp_ctx); 1266202181Sthompsa close(ctx->fd); 1267202181Sthompsa 1268202181Sthompsa /* Remove ip-address from interface */ 1269202181Sthompsa if (ctx->flags & IPASSIGNED) { 1270202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1271202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, 1272202181Sthompsa sizeof(mask.sin_addr.s_addr)); 1273202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 1274202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1275202181Sthompsa sizeof(sin.sin_addr.s_addr)); 1276202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1277202181Sthompsa (struct sockaddr *)&mask); 1278202181Sthompsa 1279202181Sthompsa if_ifdown(ctx->ifnam); 1280202181Sthompsa ctx->flags &= ~IPASSIGNED; 1281202181Sthompsa } 1282202181Sthompsa 1283202181Sthompsa /* Attempt to reset resolv.conf */ 1284202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 1285202181Sthompsa} 1286202181Sthompsa 1287202181Sthompsastatic void 1288202181Sthompsadaemonize(struct ctx *ctx) 1289202181Sthompsa{ 1290202181Sthompsa struct pidfh *pfh; 1291202181Sthompsa pid_t opid; 1292202181Sthompsa 1293202181Sthompsa snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1294202181Sthompsa 1295202181Sthompsa pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1296202181Sthompsa if (pfh == NULL) { 1297202181Sthompsa warn("Cannot create pidfile %s", ctx->pidfile); 1298202181Sthompsa return; 1299202181Sthompsa } 1300202181Sthompsa 1301202181Sthompsa if (daemon(0, 0) == -1) { 1302202181Sthompsa warn("Cannot daemonize"); 1303202181Sthompsa pidfile_remove(pfh); 1304202181Sthompsa return; 1305202181Sthompsa } 1306202181Sthompsa 1307202181Sthompsa pidfile_write(pfh); 1308202181Sthompsa ctx->pfh = pfh; 1309202181Sthompsa ctx->flags |= FLG_DAEMON; 1310202181Sthompsa 1311202181Sthompsa snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1312202181Sthompsa openlog(syslog_title, LOG_PID, LOG_USER); 1313202181Sthompsa syslog_open = 1; 1314202181Sthompsa} 1315202181Sthompsa 1316202181Sthompsastatic void 1317202181Sthompsasend_disconnect(const char *ifnam) 1318202181Sthompsa{ 1319202181Sthompsa char pidfile[128]; 1320202181Sthompsa FILE *fp; 1321202181Sthompsa pid_t pid; 1322202181Sthompsa int n; 1323202181Sthompsa 1324202181Sthompsa snprintf(pidfile, 127, PIDFILE, ifnam); 1325202181Sthompsa fp = fopen(pidfile, "r"); 1326202181Sthompsa if (fp == NULL) { 1327202181Sthompsa warn("Cannot open %s", pidfile); 1328202181Sthompsa return; 1329202181Sthompsa } 1330202181Sthompsa 1331202181Sthompsa n = fscanf(fp, "%d", &pid); 1332202181Sthompsa fclose(fp); 1333202181Sthompsa if (n != 1) { 1334202181Sthompsa warnx("unable to read daemon pid"); 1335202181Sthompsa return; 1336202181Sthompsa } 1337202181Sthompsa#ifdef DEBUG 1338202181Sthompsa fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1339202181Sthompsa#endif 1340202181Sthompsa kill(pid, SIGTERM); 1341202181Sthompsa} 1342202181Sthompsa 1343202181Sthompsastatic void 1344202181Sthompsausage(const char *exec) 1345202181Sthompsa{ 1346202181Sthompsa 1347202181Sthompsa printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1348202181Sthompsa "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1349202181Sthompsa printf("usage %s -d interface\n", exec); 1350202181Sthompsa} 1351202181Sthompsa 1352202181Sthompsaenum { 1353202181Sthompsa MODE_CONN, 1354202181Sthompsa MODE_DISC 1355202181Sthompsa}; 1356202181Sthompsa 1357202181Sthompsaint 1358202181Sthompsamain(int argc, char *argv[]) 1359202181Sthompsa{ 1360202181Sthompsa int ch, error, mode; 1361202181Sthompsa const char *ifnam = NULL; 1362202181Sthompsa char *tty = NULL; 1363202181Sthompsa char **p, **tty_list; 1364202181Sthompsa fd_set set; 1365202181Sthompsa struct ctx ctx; 1366202181Sthompsa struct itimerval it; 1367202181Sthompsa 1368202181Sthompsa TAILQ_INIT(&timers.head); 1369202181Sthompsa timers.res = 1; 1370202181Sthompsa 1371202181Sthompsa ctx.pdp_ctx = 1; 1372202181Sthompsa ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1373202181Sthompsa ctx.pin = NULL; 1374202181Sthompsa 1375202181Sthompsa ctx.con_status = 0; 1376202181Sthompsa ctx.con_apn = NULL; 1377202181Sthompsa ctx.con_oper = NULL; 1378202181Sthompsa ctx.con_net_stat = 0; 1379202181Sthompsa ctx.con_net_type = -1; 1380202181Sthompsa ctx.flags = 0; 1381202181Sthompsa ctx.resolv_path = RESOLV_PATH; 1382202181Sthompsa ctx.resolv = NULL; 1383202181Sthompsa ctx.ns = NULL; 1384202181Sthompsa ctx.dbm = 0; 1385202181Sthompsa 1386202181Sthompsa mode = MODE_CONN; 1387202181Sthompsa ctx.flags |= FLG_DELAYED; 1388202181Sthompsa 1389202181Sthompsa while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1390202181Sthompsa switch (ch) { 1391202181Sthompsa case 'a': 1392202181Sthompsa ctx.pdp_apn = argv[optind - 1]; 1393202181Sthompsa break; 1394202181Sthompsa case 'c': 1395202181Sthompsa ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1396202181Sthompsa if (ctx.pdp_ctx < 1) { 1397202181Sthompsa warnx("Invalid context ID, defaulting to 1"); 1398202181Sthompsa ctx.pdp_ctx = 1; 1399202181Sthompsa } 1400202181Sthompsa break; 1401202181Sthompsa case 'p': 1402202181Sthompsa ctx.pin = argv[optind - 1]; 1403202181Sthompsa break; 1404202181Sthompsa case 'u': 1405202181Sthompsa ctx.pdp_user = argv[optind - 1]; 1406202181Sthompsa break; 1407202181Sthompsa case 'k': 1408202181Sthompsa ctx.pdp_pwd = argv[optind - 1]; 1409202181Sthompsa break; 1410202181Sthompsa case 'r': 1411202181Sthompsa ctx.resolv_path = argv[optind - 1]; 1412202181Sthompsa break; 1413202181Sthompsa case 'd': 1414202181Sthompsa mode = MODE_DISC; 1415202181Sthompsa break; 1416202181Sthompsa case 'b': 1417202181Sthompsa ctx.flags &= ~FLG_DELAYED; 1418202181Sthompsa break; 1419202181Sthompsa case 'n': 1420202181Sthompsa ctx.flags |= FLG_NODAEMON; 1421202181Sthompsa break; 1422202181Sthompsa case 'f': 1423202181Sthompsa tty = argv[optind - 1]; 1424202181Sthompsa break; 1425202181Sthompsa case 'h': 1426202181Sthompsa case '?': 1427202181Sthompsa default: 1428202181Sthompsa usage(argv[0]); 1429202181Sthompsa exit(EXIT_SUCCESS); 1430202181Sthompsa } 1431202181Sthompsa } 1432202181Sthompsa 1433202181Sthompsa argc -= optind; 1434202181Sthompsa argv += optind; 1435202181Sthompsa 1436202181Sthompsa if (argc < 1) 1437202181Sthompsa errx(1, "no interface given"); 1438202181Sthompsa 1439202181Sthompsa ifnam = argv[argc - 1]; 1440202181Sthompsa ctx.ifnam = strdup(ifnam); 1441202181Sthompsa 1442202181Sthompsa switch (mode) { 1443202181Sthompsa case MODE_DISC: 1444202181Sthompsa printf("Disconnecting %s\n", ifnam); 1445202181Sthompsa send_disconnect(ifnam); 1446202181Sthompsa exit(EXIT_SUCCESS); 1447202181Sthompsa default: 1448202181Sthompsa break; 1449202181Sthompsa } 1450202181Sthompsa 1451202181Sthompsa signal(SIGHUP, sig_handle); 1452202181Sthompsa signal(SIGINT, sig_handle); 1453202181Sthompsa signal(SIGQUIT, sig_handle); 1454202181Sthompsa signal(SIGTERM, sig_handle); 1455202181Sthompsa signal(SIGALRM, sig_handle); 1456202181Sthompsa 1457202181Sthompsa it.it_interval.tv_sec = 1; 1458202181Sthompsa it.it_interval.tv_usec = 0; 1459202181Sthompsa it.it_value.tv_sec = 1; 1460202181Sthompsa it.it_value.tv_usec = 0; 1461202181Sthompsa error = setitimer(ITIMER_REAL, &it, NULL); 1462202181Sthompsa if (error != 0) 1463202181Sthompsa errx(1, "setitimer"); 1464202181Sthompsa 1465202181Sthompsa tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1466202181Sthompsa watchdog_reset(&ctx, 15); 1467202181Sthompsa 1468202181Sthompsa if (tty != NULL) { 1469202181Sthompsa error = do_connect(&ctx, tty); 1470202181Sthompsa if (error != 0) 1471202181Sthompsa errx(1, "Failed to open %s", tty); 1472202181Sthompsa } 1473202181Sthompsa else { 1474202181Sthompsa tty_list = get_tty(&ctx); 1475210276Sthompsa if (tty_list == NULL) 1476210276Sthompsa errx(1, "%s does not appear to be a uhso device", ifnam); 1477202181Sthompsa#ifdef DEBUG 1478202181Sthompsa if (tty_list == NULL) { 1479202181Sthompsa fprintf(stderr, "get_tty returned empty list\n"); 1480202181Sthompsa } else { 1481202181Sthompsa fprintf(stderr, "tty list:\n"); 1482202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1483202181Sthompsa fprintf(stderr, "\t %s\n", *p); 1484202181Sthompsa } 1485202181Sthompsa } 1486202181Sthompsa#endif 1487202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1488202181Sthompsa error = do_connect(&ctx, *p); 1489202181Sthompsa if (error == 0) { 1490202181Sthompsa tty = *p; 1491202181Sthompsa break; 1492202181Sthompsa } 1493202181Sthompsa } 1494202181Sthompsa if (*p == NULL) 1495202181Sthompsa errx(1, "Failed to obtain a control port, " 1496202181Sthompsa "try specifying one manually"); 1497202181Sthompsa } 1498202181Sthompsa 1499202181Sthompsa if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1500202181Sthompsa daemonize(&ctx); 1501202181Sthompsa 1502202181Sthompsa 1503202181Sthompsa FD_ZERO(&set); 1504202181Sthompsa FD_SET(ctx.fd, &set); 1505202181Sthompsa for (;;) { 1506202181Sthompsa 1507202181Sthompsa watchdog_disable(&ctx); 1508202181Sthompsa error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1509202181Sthompsa if (error <= 0) { 1510202181Sthompsa if (running && errno == EINTR) 1511202181Sthompsa continue; 1512202181Sthompsa if (ctx.flags & FLG_WDEXP) { 1513202181Sthompsa ctx.flags &= ~FLG_WDEXP; 1514202181Sthompsa watchdog_reset(&ctx, 5); 1515202181Sthompsa do_disconnect(&ctx); 1516202181Sthompsa watchdog_reset(&ctx, 15); 1517202181Sthompsa do_connect(&ctx, tty); 1518202181Sthompsa running = 1; 1519202181Sthompsa continue; 1520202181Sthompsa } 1521202181Sthompsa 1522202181Sthompsa break; 1523202181Sthompsa } 1524202181Sthompsa 1525202181Sthompsa if (FD_ISSET(ctx.fd, &set)) { 1526202181Sthompsa watchdog_reset(&ctx, 15); 1527202181Sthompsa error = at_async(&ctx, &ctx); 1528202181Sthompsa if (error != 0) 1529202181Sthompsa break; 1530202181Sthompsa } 1531202181Sthompsa FD_SET(ctx.fd, &set); 1532202181Sthompsa 1533202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1534202181Sthompsa printf("Status: %s (%s)", 1535202181Sthompsa ctx.con_status ? "connected" : "disconnected", 1536202181Sthompsa network_access_type[ctx.con_net_type]); 1537202181Sthompsa if (ctx.dbm < 0) 1538202181Sthompsa printf(", signal: %d dBm", ctx.dbm); 1539210276Sthompsa printf("\t\t\t\r"); 1540202181Sthompsa fflush(stdout); 1541202181Sthompsa } 1542202181Sthompsa } 1543202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1544202181Sthompsa printf("\n"); 1545202181Sthompsa 1546202181Sthompsa signal(SIGHUP, SIG_DFL); 1547202181Sthompsa signal(SIGINT, SIG_DFL); 1548202181Sthompsa signal(SIGQUIT, SIG_DFL); 1549202181Sthompsa signal(SIGTERM, SIG_DFL); 1550202181Sthompsa signal(SIGALRM, SIG_IGN); 1551202181Sthompsa 1552202181Sthompsa do_disconnect(&ctx); 1553202181Sthompsa 1554202181Sthompsa if (ctx.flags & FLG_DAEMON) { 1555202181Sthompsa pidfile_remove(ctx.pfh); 1556202181Sthompsa if (syslog_open) 1557202181Sthompsa closelog(); 1558202181Sthompsa } 1559202181Sthompsa 1560202181Sthompsa return (0); 1561202181Sthompsa} 1562