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: stable/10/usr.sbin/uhsoctl/uhsoctl.c 340281 2018-11-09 08:47:54Z hselasky $ 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 362340281Shselaskyifaddr_ad(unsigned long 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); 456292855Sngie ctx->ns = NULL; 457202181Sthompsa } 458202181Sthompsa 459229467Spjd fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666); 460202181Sthompsa if (fd < 0) 461202181Sthompsa return (-1); 462202181Sthompsa 463202181Sthompsa if (ns == 0) { 464202181Sthompsa /* Attempt to restore old resolv.conf */ 465202181Sthompsa if (ctx->resolv != NULL) { 466202181Sthompsa ftruncate(fd, 0); 467202181Sthompsa lseek(fd, 0, SEEK_SET); 468202181Sthompsa write(fd, ctx->resolv, ctx->resolv_sz); 469202181Sthompsa free(ctx->resolv); 470202181Sthompsa ctx->resolv = NULL; 471202181Sthompsa ctx->resolv_sz = 0; 472202181Sthompsa } 473202181Sthompsa close(fd); 474202181Sthompsa return (0); 475202181Sthompsa } 476202181Sthompsa 477202181Sthompsa 478202181Sthompsa ctx->ns = malloc(sizeof(char *) * (ns + 1)); 479202181Sthompsa if (ctx->ns == NULL) { 480202181Sthompsa close(fd); 481202181Sthompsa return (-1); 482202181Sthompsa } 483202181Sthompsa 484202181Sthompsa va_start(ap, ns); 485202181Sthompsa for (i = 0; i < ns; i++) { 486202181Sthompsa p = va_arg(ap, char *); 487202181Sthompsa ctx->ns[i] = strdup(p); 488202181Sthompsa } 489202181Sthompsa ctx->ns[i] = NULL; 490202181Sthompsa va_end(ap); 491202181Sthompsa 492202181Sthompsa /* Attempt to backup the old resolv.conf */ 493202181Sthompsa if (ctx->resolv == NULL) { 494202181Sthompsa i = fstat(fd, &sb); 495202181Sthompsa if (i == 0 && sb.st_size != 0) { 496202181Sthompsa ctx->resolv_sz = sb.st_size; 497202181Sthompsa ctx->resolv = malloc(sb.st_size); 498202181Sthompsa if (ctx->resolv != NULL) { 499202181Sthompsa n = read(fd, ctx->resolv, sb.st_size); 500202181Sthompsa if (n != sb.st_size) { 501202181Sthompsa free(ctx->resolv); 502202181Sthompsa ctx->resolv = NULL; 503202181Sthompsa } 504202181Sthompsa } 505202181Sthompsa } 506202181Sthompsa } 507202181Sthompsa 508202181Sthompsa 509202181Sthompsa ftruncate(fd, 0); 510202181Sthompsa lseek(fd, 0, SEEK_SET); 511202181Sthompsa fp = fdopen(fd, "w"); 512202181Sthompsa 513202181Sthompsa /* 514202181Sthompsa * Write back everything other than nameserver entries to the 515202181Sthompsa * new resolv.conf 516202181Sthompsa */ 517202181Sthompsa if (ctx->resolv != NULL) { 518202181Sthompsa p = ctx->resolv; 519202181Sthompsa while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 520202181Sthompsa sizeof(buf))) > 0) { 521202181Sthompsa p += i; 522202181Sthompsa if (strncasecmp(buf, "nameserver", 10) == 0) 523202181Sthompsa continue; 524202181Sthompsa fprintf(fp, "%s", buf); 525202181Sthompsa } 526202181Sthompsa } 527202181Sthompsa 528202181Sthompsa for (i = 0; ctx->ns[i] != NULL; i++) { 529202181Sthompsa fprintf(fp, "nameserver %s\n", ctx->ns[i]); 530202181Sthompsa } 531202181Sthompsa fclose(fp); 532202181Sthompsa return (0); 533202181Sthompsa} 534202181Sthompsa 535202181Sthompsa/* Read a \n-terminated line from buffer */ 536202181Sthompsastatic int 537202181Sthompsareadline_buf(const char *s, const char *e, char *buf, size_t bufsz) 538202181Sthompsa{ 539202181Sthompsa int pos = 0; 540202181Sthompsa char *p = buf; 541202181Sthompsa 542202181Sthompsa for (; s < e; s++) { 543202181Sthompsa *p = *s; 544202181Sthompsa pos++; 545202181Sthompsa if (pos >= (bufsz - 1)) 546202181Sthompsa break; 547202181Sthompsa if (*p++ == '\n') 548202181Sthompsa break; 549202181Sthompsa } 550202181Sthompsa *p = '\0'; 551202181Sthompsa return (pos); 552202181Sthompsa} 553202181Sthompsa 554202181Sthompsa/* Read a \n-terminated line from file */ 555202181Sthompsastatic int 556202181Sthompsareadline(int fd, char *buf, size_t bufsz) 557202181Sthompsa{ 558202181Sthompsa int n = 0, pos = 0; 559202181Sthompsa char *p = buf; 560202181Sthompsa 561202181Sthompsa for (;;) { 562202181Sthompsa n = read(fd, p, 1); 563202181Sthompsa if (n <= 0) 564202181Sthompsa break; 565202181Sthompsa pos++; 566202181Sthompsa if (pos >= (bufsz - 1)) 567202181Sthompsa break; 568202181Sthompsa if (*p++ == '\n') 569202181Sthompsa break; 570202181Sthompsa } 571202181Sthompsa *p = '\0'; 572202181Sthompsa return (n <= 0 ? n : pos); 573202181Sthompsa} 574202181Sthompsa 575202181Sthompsa/* 576202181Sthompsa * Synchronous AT command 577202181Sthompsa */ 578202181Sthompsastatic int 579202181Sthompsaat_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 580202181Sthompsa{ 581210276Sthompsa char buf[512]; 582210276Sthompsa char cmd[64]; 583202181Sthompsa size_t l; 584202181Sthompsa int n, error, retval = 0; 585202181Sthompsa va_list ap; 586202181Sthompsa fd_set set; 587210276Sthompsa char *p; 588202181Sthompsa 589202181Sthompsa va_start(ap, cf); 590202181Sthompsa vsnprintf(cmd, sizeof(cmd), cf, ap); 591202181Sthompsa va_end(ap); 592202181Sthompsa 593202181Sthompsa#ifdef DEBUG 594202181Sthompsa fprintf(stderr, "SYNC_CMD: %s", cmd); 595202181Sthompsa#endif 596202181Sthompsa 597202181Sthompsa l = strlen(cmd); 598202181Sthompsa n = write(ctx->fd, cmd, l); 599202181Sthompsa if (n <= 0) 600202181Sthompsa return (-1); 601202181Sthompsa 602202181Sthompsa if (resp != NULL) { 603202181Sthompsa l = strlen(resp); 604202181Sthompsa#ifdef DEBUG 605268762Sgavin fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l); 606202181Sthompsa#endif 607202181Sthompsa } 608202181Sthompsa 609202181Sthompsa for (;;) { 610202181Sthompsa bzero(buf, sizeof(buf)); 611202181Sthompsa 612202181Sthompsa FD_ZERO(&set); 613202181Sthompsa watchdog_reset(ctx, 5); 614202181Sthompsa do { 615202181Sthompsa FD_SET(ctx->fd, &set); 616202181Sthompsa error = select(ctx->fd + 1, &set, NULL, NULL, NULL); 617210276Sthompsa if (ctx->flags & FLG_WDEXP) { 618202181Sthompsa watchdog_disable(ctx); 619210276Sthompsa return (-2); 620202181Sthompsa } 621202181Sthompsa } while (error <= 0 && errno == EINTR); 622210276Sthompsa watchdog_disable(ctx); 623202181Sthompsa 624202181Sthompsa if (error <= 0) { 625202181Sthompsa retval = -2; 626202181Sthompsa break; 627202181Sthompsa } 628202181Sthompsa 629202181Sthompsa n = readline(ctx->fd, buf, sizeof(buf)); 630202181Sthompsa if (n <= 0) { 631202181Sthompsa retval = -2; 632202181Sthompsa break; 633202181Sthompsa } 634202181Sthompsa 635202181Sthompsa if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0) 636202181Sthompsa continue; 637202181Sthompsa 638210276Sthompsa if ((p = strchr(buf, '\r')) != NULL) 639210276Sthompsa *p = '\0'; 640210276Sthompsa else if ((p = strchr(buf, '\n')) != NULL) 641210276Sthompsa *p = '\0'; 642202181Sthompsa#ifdef DEBUG 643210276Sthompsa fprintf(stderr, "SYNC_RESP: %s\n", buf); 644202181Sthompsa#endif 645202181Sthompsa 646210276Sthompsa /* Skip local echo */ 647210276Sthompsa if (strncasecmp(cmd, buf, strlen(buf)) == 0) 648210276Sthompsa continue; 649210276Sthompsa 650210276Sthompsa if (cb != NULL) 651210276Sthompsa cb(ra, cmd, buf); 652210276Sthompsa 653202181Sthompsa if (strncmp(buf, "OK", 2) == 0) { 654210276Sthompsa retval = retval ? retval : 0; 655202181Sthompsa break; 656210276Sthompsa } else if (strstr(buf, "ERROR") != NULL) { 657202181Sthompsa retval = -1; 658202181Sthompsa break; 659202181Sthompsa } 660210276Sthompsa if (resp != NULL) 661210276Sthompsa retval = strncmp(buf, resp, l); 662202181Sthompsa } 663202181Sthompsa#ifdef DEBUG 664202181Sthompsa fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 665202181Sthompsa#endif 666202181Sthompsa return (retval); 667202181Sthompsa} 668202181Sthompsa 669202181Sthompsastatic int 670202181Sthompsaat_cmd_async(int fd, const char *cf, ...) 671202181Sthompsa{ 672202181Sthompsa size_t l; 673202181Sthompsa va_list ap; 674202181Sthompsa char cmd[64]; 675202181Sthompsa 676202181Sthompsa va_start(ap, cf); 677202181Sthompsa vsnprintf(cmd, sizeof(cmd), cf, ap); 678202181Sthompsa va_end(ap); 679202181Sthompsa 680202181Sthompsa#ifdef DEBUG 681202181Sthompsa fprintf(stderr, "CMD: %s", cmd); 682202181Sthompsa#endif 683202181Sthompsa l = strlen(cmd); 684202181Sthompsa return (write(fd, cmd, l)); 685202181Sthompsa} 686202181Sthompsa 687202181Sthompsastatic void 688202181Sthompsasaveresp(resp_arg *ra, const char *cmd, const char *resp) 689202181Sthompsa{ 690202181Sthompsa char **buf; 691202181Sthompsa int i = ra->val[1].int32; 692202181Sthompsa 693210276Sthompsa#ifdef DEBUG 694210276Sthompsa fprintf(stderr, "Save '%s'\n", resp); 695210276Sthompsa#endif 696210276Sthompsa 697202181Sthompsa buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 698202181Sthompsa if (buf == NULL) 699202181Sthompsa return; 700202181Sthompsa 701202181Sthompsa buf[i] = strdup(resp); 702202181Sthompsa 703202181Sthompsa ra->val[0].ptr = buf; 704202181Sthompsa ra->val[1].int32 = i + 1; 705202181Sthompsa} 706202181Sthompsa 707202181Sthompsastatic void 708210276Sthompsafreeresp(resp_arg *ra) 709210276Sthompsa{ 710210276Sthompsa char **buf; 711210276Sthompsa int i; 712210276Sthompsa 713210276Sthompsa buf = ra->val[0].ptr; 714210276Sthompsa for (i = 0; i < ra->val[1].int32; i++) { 715210276Sthompsa free(buf[i]); 716210276Sthompsa } 717210276Sthompsa free(buf); 718210276Sthompsa} 719210276Sthompsa 720210276Sthompsastatic void 721202181Sthompsaat_async_creg(void *arg, const char *resp) 722202181Sthompsa{ 723202181Sthompsa struct ctx *ctx = arg; 724202181Sthompsa int n, reg; 725202181Sthompsa 726202181Sthompsa n = sscanf(resp, "+CREG: %*d,%d", ®); 727202181Sthompsa if (n != 1) { 728202181Sthompsa n = sscanf(resp, "+CREG: %d", ®); 729202181Sthompsa if (n != 1) 730202181Sthompsa return; 731202181Sthompsa } 732202181Sthompsa 733202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 734202181Sthompsa tmr_add(&timers, 1, 1, tmr_creg, ctx); 735202181Sthompsa } 736202181Sthompsa else { 737202181Sthompsa tmr_add(&timers, 1, 30, tmr_creg, ctx); 738202181Sthompsa } 739202181Sthompsa 740202181Sthompsa if (ctx->con_net_stat == reg) 741202181Sthompsa return; 742202181Sthompsa 743202181Sthompsa ctx->con_net_stat = reg; 744202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 745202181Sthompsa} 746202181Sthompsa 747202181Sthompsastatic void 748202181Sthompsaat_async_cgreg(void *arg, const char *resp) 749202181Sthompsa{ 750202181Sthompsa struct ctx *ctx = arg; 751202181Sthompsa int n, reg; 752202181Sthompsa 753202181Sthompsa n = sscanf(resp, "+CGREG: %*d,%d", ®); 754202181Sthompsa if (n != 1) { 755202181Sthompsa n = sscanf(resp, "+CGREG: %d", ®); 756202181Sthompsa if (n != 1) 757202181Sthompsa return; 758202181Sthompsa } 759202181Sthompsa 760202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 761202181Sthompsa tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 762202181Sthompsa } 763202181Sthompsa else { 764202181Sthompsa tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 765202181Sthompsa } 766202181Sthompsa 767202181Sthompsa if (ctx->con_net_stat == reg) 768202181Sthompsa return; 769202181Sthompsa 770202181Sthompsa ctx->con_net_stat = reg; 771202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 772202181Sthompsa} 773202181Sthompsa 774202181Sthompsa 775202181Sthompsastatic void 776202181Sthompsaat_async_cops(void *arg, const char *resp) 777202181Sthompsa{ 778202181Sthompsa struct ctx *ctx = arg; 779202181Sthompsa int n, at; 780202181Sthompsa char opr[64]; 781202181Sthompsa 782202181Sthompsa n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 783202181Sthompsa opr, &at); 784202181Sthompsa if (n != 2) 785202181Sthompsa return; 786202181Sthompsa 787202181Sthompsa if (ctx->con_oper != NULL) { 788202181Sthompsa if (ctx->con_net_type == at && 789202181Sthompsa strcasecmp(opr, ctx->con_oper) == 0) 790202181Sthompsa return; 791202181Sthompsa free(ctx->con_oper); 792202181Sthompsa } 793202181Sthompsa 794202181Sthompsa ctx->con_oper = strdup(opr); 795202181Sthompsa ctx->con_net_type = at; 796202181Sthompsa 797202181Sthompsa if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 798202181Sthompsa logger(LOG_NOTICE, "%s to \"%s\" (%s)", 799202181Sthompsa network_reg_status[ctx->con_net_stat], 800202181Sthompsa ctx->con_oper, network_access_type[ctx->con_net_type]); 801202181Sthompsa if (ctx->con_status != 1) { 802202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 803202181Sthompsa ctx->pdp_ctx); 804202181Sthompsa } 805202181Sthompsa } 806202181Sthompsa else { 807202181Sthompsa logger(LOG_NOTICE, "%s (%s)", 808202181Sthompsa network_reg_status[ctx->con_net_stat], 809202181Sthompsa network_access_type[ctx->con_net_type]); 810202181Sthompsa } 811202181Sthompsa} 812202181Sthompsa 813202181Sthompsa/* 814202181Sthompsa * Signal strength for pretty console output 815202181Sthompsa * 816202181Sthompsa * From 3GPP TS 27.007 V8.3.0, Section 8.5 817202181Sthompsa * 0 = -113 dBm or less 818202181Sthompsa * 1 = -111 dBm 819202181Sthompsa * 2...30 = -109...-53 dBm 820202181Sthompsa * 31 = -51 dBm or greater 821202181Sthompsa * 822202181Sthompsa * So, dbm = (rssi * 2) - 113 823202181Sthompsa*/ 824202181Sthompsastatic void 825202181Sthompsaat_async_csq(void *arg, const char *resp) 826202181Sthompsa{ 827202181Sthompsa struct ctx *ctx = arg; 828202181Sthompsa int n, rssi; 829202181Sthompsa 830202181Sthompsa n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 831202181Sthompsa if (n != 1) 832202181Sthompsa return; 833202181Sthompsa if (rssi == 99) 834202181Sthompsa ctx->dbm = 0; 835202181Sthompsa else { 836202181Sthompsa ctx->dbm = (rssi * 2) - 113; 837202181Sthompsa tmr_add(&timers, 1, 15, tmr_status, ctx); 838202181Sthompsa } 839202181Sthompsa 840202181Sthompsa ctx->flags |= FLG_NEWDATA; 841202181Sthompsa} 842202181Sthompsa 843202181Sthompsastatic void 844202181Sthompsaat_async_owancall(void *arg, const char *resp) 845202181Sthompsa{ 846202181Sthompsa struct ctx *ctx = arg; 847202181Sthompsa int n, i; 848202181Sthompsa 849202181Sthompsa n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 850202181Sthompsa if (n != 1) 851202181Sthompsa return; 852202181Sthompsa 853202181Sthompsa if (i == ctx->con_status) 854202181Sthompsa return; 855202181Sthompsa 856202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 857202181Sthompsa 858202181Sthompsa ctx->con_status = i; 859202181Sthompsa if (ctx->con_status == 1) { 860202181Sthompsa logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 861202181Sthompsa ctx->con_oper, ctx->con_apn, 862202181Sthompsa network_access_type[ctx->con_net_type]); 863202181Sthompsa } 864202181Sthompsa else { 865202181Sthompsa logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 866202181Sthompsa ctx->con_oper, ctx->con_apn); 867202181Sthompsa } 868202181Sthompsa} 869202181Sthompsa 870202181Sthompsastatic void 871202181Sthompsaat_async_owandata(void *arg, const char *resp) 872202181Sthompsa{ 873202181Sthompsa struct ctx *ctx = arg; 874202181Sthompsa char ip[40], ns1[40], ns2[40]; 875202181Sthompsa int n, error, rs; 876202181Sthompsa struct ifaddrs *ifap, *ifa; 877202181Sthompsa struct sockaddr_in sin, mask; 878202181Sthompsa struct sockaddr_dl sdl; 879202181Sthompsa struct { 880202181Sthompsa struct rt_msghdr rtm; 881202181Sthompsa char buf[512]; 882202181Sthompsa } r; 883202181Sthompsa char *cp = r.buf; 884202181Sthompsa 885202181Sthompsa n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 886202181Sthompsa ip, ns1, ns2); 887202181Sthompsa if (n != 3) 888202181Sthompsa return; 889202181Sthompsa 890202181Sthompsa /* XXX: AF_INET assumption */ 891202181Sthompsa 892202181Sthompsa logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 893202181Sthompsa 894202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 895202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 896202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 897202181Sthompsa 898202181Sthompsa if (ctx->flags & IPASSIGNED) { 899202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 900202181Sthompsa sizeof(sin.sin_addr.s_addr)); 901202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 902202181Sthompsa (struct sockaddr *)&mask); 903202181Sthompsa } 904202181Sthompsa inet_pton(AF_INET, ip, &ctx->ip.s_addr); 905202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 906202181Sthompsa sizeof(sin.sin_addr.s_addr)); 907202181Sthompsa 908202181Sthompsa error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 909202181Sthompsa (struct sockaddr *)&mask); 910202181Sthompsa if (error != 0) { 911202181Sthompsa logger(LOG_ERR, "failed to set ip-address"); 912202181Sthompsa return; 913202181Sthompsa } 914202181Sthompsa 915202181Sthompsa if_ifup(ctx->ifnam); 916202181Sthompsa 917202181Sthompsa ctx->flags |= IPASSIGNED; 918202181Sthompsa 919202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 920202181Sthompsa error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 921202181Sthompsa if (error != 0) { 922202181Sthompsa logger(LOG_ERR, "failed to set nameservers"); 923202181Sthompsa } 924202181Sthompsa 925202181Sthompsa error = getifaddrs(&ifap); 926202181Sthompsa if (error != 0) { 927202181Sthompsa logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 928202181Sthompsa return; 929202181Sthompsa } 930202181Sthompsa 931202181Sthompsa for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 932202181Sthompsa if (ifa->ifa_addr->sa_family != AF_LINK) 933202181Sthompsa continue; 934202181Sthompsa if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 935202181Sthompsa memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 936202181Sthompsa sizeof(struct sockaddr_dl)); 937202181Sthompsa break; 938202181Sthompsa } 939202181Sthompsa } 940202181Sthompsa if (ifa == NULL) 941202181Sthompsa return; 942202181Sthompsa 943202181Sthompsa rs = socket(PF_ROUTE, SOCK_RAW, 0); 944202181Sthompsa if (rs < 0) { 945202181Sthompsa logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 946202181Sthompsa return; 947202181Sthompsa } 948202181Sthompsa 949202181Sthompsa memset(&r, 0, sizeof(r)); 950202181Sthompsa 951202181Sthompsa r.rtm.rtm_version = RTM_VERSION; 952202181Sthompsa r.rtm.rtm_type = RTM_ADD; 953202181Sthompsa r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 954202181Sthompsa r.rtm.rtm_pid = getpid(); 955202181Sthompsa memset(&sin, 0, sizeof(struct sockaddr_in)); 956202181Sthompsa sin.sin_family = AF_INET; 957202181Sthompsa sin.sin_len = sizeof(struct sockaddr_in); 958202181Sthompsa 959202181Sthompsa memcpy(cp, &sin, sin.sin_len); 960202181Sthompsa cp += SA_SIZE(&sin); 961202181Sthompsa memcpy(cp, &sdl, sdl.sdl_len); 962202181Sthompsa cp += SA_SIZE(&sdl); 963202181Sthompsa memcpy(cp, &sin, sin.sin_len); 964202181Sthompsa r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 965202181Sthompsa r.rtm.rtm_msglen = sizeof(r); 966202181Sthompsa 967202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 968202181Sthompsa if (n != r.rtm.rtm_msglen) { 969202181Sthompsa r.rtm.rtm_type = RTM_DELETE; 970202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 971202181Sthompsa r.rtm.rtm_type = RTM_ADD; 972202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 973202181Sthompsa } 974202181Sthompsa 975202181Sthompsa if (n != r.rtm.rtm_msglen) { 976202181Sthompsa logger(LOG_ERR, "failed to set default route: %s", 977202181Sthompsa strerror(errno)); 978202181Sthompsa } 979202181Sthompsa close(rs); 980202181Sthompsa 981202181Sthompsa /* Delayed daemonization */ 982202181Sthompsa if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 983202181Sthompsa daemonize(ctx); 984202181Sthompsa} 985202181Sthompsa 986202181Sthompsastatic int 987202181Sthompsaat_async(struct ctx *ctx, void *arg) 988202181Sthompsa{ 989202181Sthompsa int n, i; 990202181Sthompsa size_t l; 991202181Sthompsa char buf[512]; 992202181Sthompsa 993202181Sthompsa watchdog_reset(ctx, 15); 994202181Sthompsa 995202181Sthompsa bzero(buf, sizeof(buf)); 996202181Sthompsa n = readline(ctx->fd, buf, sizeof(buf)); 997202181Sthompsa if (n <= 0) 998202181Sthompsa return (n <= 0 ? -1 : 0); 999202181Sthompsa 1000202181Sthompsa#ifdef DEBUG 1001202181Sthompsa fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 1002202181Sthompsa#endif 1003202181Sthompsa for (i = 0; async_cmd[i].cmd != NULL; i++) { 1004202181Sthompsa l = strlen(async_cmd[i].cmd); 1005202181Sthompsa if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 1006202181Sthompsa async_cmd[i].func(arg, buf); 1007202181Sthompsa } 1008202181Sthompsa } 1009202181Sthompsa return (0); 1010202181Sthompsa} 1011202181Sthompsa 1012202181Sthompsastatic const char *port_type_list[] = { 1013202181Sthompsa "control", "application", "application2", NULL 1014202181Sthompsa}; 1015202181Sthompsa 1016202181Sthompsa/* 1017202181Sthompsa * Attempts to find a list of control tty for the interface 1018210276Sthompsa * FreeBSD attaches USB devices per interface so we have to go through 1019202181Sthompsa * hoops to find which ttys that belong to our network interface. 1020202181Sthompsa */ 1021202181Sthompsastatic char ** 1022202181Sthompsaget_tty(struct ctx *ctx) 1023202181Sthompsa{ 1024210276Sthompsa char buf[64], data[128]; 1025210276Sthompsa int error, i, usbport, usbport0, list_size = 0; 1026210276Sthompsa char **list = NULL; 1027202181Sthompsa size_t len; 1028210276Sthompsa const char **p, *q; 1029202181Sthompsa 1030210276Sthompsa /* 1031210276Sthompsa * Look for the network interface first 1032210276Sthompsa */ 1033202181Sthompsa for (i = 0; ; i++) { 1034210276Sthompsa /* Check if we still have uhso nodes to check */ 1035202181Sthompsa snprintf(buf, 64, SYSCTL_TEST, i); 1036202181Sthompsa len = 127; 1037202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1038210276Sthompsa data[len] = '\0'; 1039202181Sthompsa#ifdef DEBUG 1040202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1041202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1042202181Sthompsa#endif 1043210276Sthompsa if (error < 0 || strcasecmp(data, "uhso") != 0) 1044202181Sthompsa return NULL; 1045202181Sthompsa 1046210276Sthompsa /* Check if this node contains the network interface we want */ 1047202181Sthompsa snprintf(buf, 64, SYSCTL_NETIF, i); 1048202181Sthompsa len = 127; 1049202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1050210276Sthompsa data[len] = '\0'; 1051202181Sthompsa#ifdef DEBUG 1052202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1053202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1054202181Sthompsa#endif 1055210276Sthompsa if (error == 0 && strcasecmp(data, ctx->ifnam) == 0) 1056210276Sthompsa break; 1057210276Sthompsa } 1058202181Sthompsa 1059210276Sthompsa /* Figure out the USB port location */ 1060210276Sthompsa snprintf(buf, 64, SYSCTL_LOCATION, i); 1061210276Sthompsa len = 127; 1062210276Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1063210276Sthompsa data[len] = '\0'; 1064202181Sthompsa#ifdef DEBUG 1065210276Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1066210276Sthompsa buf, error, error == 0 ? data : "FAILED"); 1067202181Sthompsa#endif 1068210276Sthompsa if (error != 0) 1069210276Sthompsa return (NULL); 1070202181Sthompsa 1071210276Sthompsa q = strstr(data, "port="); 1072210276Sthompsa if (q != NULL) { 1073210276Sthompsa error = sscanf(q, " port=%d", &usbport); 1074210276Sthompsa if (error != 1) { 1075202181Sthompsa#ifdef DEBUG 1076210276Sthompsa fprintf(stderr, "failed to read usb port location from '%s'\n", data); 1077202181Sthompsa#endif 1078210276Sthompsa return (NULL); 1079202181Sthompsa } 1080210276Sthompsa } else { 1081210276Sthompsa#ifdef DEBUG 1082210276Sthompsa fprintf(stderr, "failed to parse location '%s'\n", data); 1083210276Sthompsa#endif 1084210276Sthompsa return (NULL); 1085202181Sthompsa } 1086210276Sthompsa#ifdef DEBUG 1087210276Sthompsa fprintf(stderr, "USB port location=%d\n", usbport); 1088210276Sthompsa#endif 1089202181Sthompsa 1090202181Sthompsa /* 1091210276Sthompsa * Now go through it all again but only look at those matching the 1092210276Sthompsa * usb port location we found. 1093202181Sthompsa */ 1094210276Sthompsa for (i = 0; ; i++) { 1095210276Sthompsa snprintf(buf, 64, SYSCTL_LOCATION, i); 1096202181Sthompsa len = 127; 1097210276Sthompsa memset(&data, 0, sizeof(data)); 1098202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1099210276Sthompsa if (error != 0) 1100202181Sthompsa break; 1101210276Sthompsa data[len] = '\0'; 1102210276Sthompsa q = strstr(data, "port="); 1103210276Sthompsa if (q == NULL) 1104210276Sthompsa continue; 1105210276Sthompsa sscanf(q, " port=%d", &usbport0); 1106210276Sthompsa if (usbport != usbport0) 1107210276Sthompsa continue; 1108202181Sthompsa 1109210276Sthompsa /* Try to add ports */ 1110202181Sthompsa for (p = port_type_list; *p != NULL; p++) { 1111210276Sthompsa snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1112202181Sthompsa len = 127; 1113210276Sthompsa memset(&data, 0, sizeof(data)); 1114202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1115210276Sthompsa data[len] = '\0'; 1116210276Sthompsa#ifdef DEBUG 1117210276Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1118210276Sthompsa buf, error, error == 0 ? data : "FAILED"); 1119210276Sthompsa#endif 1120202181Sthompsa if (error == 0) { 1121202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1122202181Sthompsa list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1123210276Sthompsa sprintf(list[list_size], TTY_NAME, data); 1124210276Sthompsa list_size++; 1125202181Sthompsa } 1126202181Sthompsa } 1127202181Sthompsa } 1128202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1129202181Sthompsa list[list_size] = NULL; 1130210276Sthompsa return (list); 1131202181Sthompsa} 1132202181Sthompsa 1133202181Sthompsastatic int 1134202181Sthompsado_connect(struct ctx *ctx, const char *tty) 1135202181Sthompsa{ 1136202181Sthompsa int i, error, needcfg; 1137202181Sthompsa resp_arg ra; 1138202181Sthompsa struct termios t; 1139202181Sthompsa char **buf; 1140202181Sthompsa 1141202181Sthompsa#ifdef DEBUG 1142202181Sthompsa fprintf(stderr, "Attempting to open %s\n", tty); 1143202181Sthompsa#endif 1144202181Sthompsa 1145202181Sthompsa ctx->fd = open(tty, O_RDWR); 1146202181Sthompsa if (ctx->fd < 0) { 1147202181Sthompsa#ifdef DEBUG 1148202181Sthompsa fprintf(stderr, "Failed to open %s\n", tty); 1149202181Sthompsa#endif 1150202181Sthompsa return (-1); 1151202181Sthompsa } 1152202181Sthompsa 1153202181Sthompsa tcgetattr(ctx->fd, &t); 1154202181Sthompsa t.c_oflag = 0; 1155202181Sthompsa t.c_iflag = 0; 1156202181Sthompsa t.c_cflag = CLOCAL | CREAD; 1157202181Sthompsa t.c_lflag = 0; 1158202181Sthompsa tcsetattr(ctx->fd, TCSAFLUSH, &t); 1159202181Sthompsa 1160202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1161202181Sthompsa if (error == -2) { 1162210276Sthompsa warnx("failed to read from device %s", tty); 1163202181Sthompsa return (-1); 1164202181Sthompsa } 1165202181Sthompsa 1166202181Sthompsa /* Check for PIN */ 1167202181Sthompsa error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1168202181Sthompsa if (error != 0) { 1169210276Sthompsa ra.val[0].ptr = NULL; 1170210276Sthompsa ra.val[1].int32 = 0; 1171210276Sthompsa error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n"); 1172210276Sthompsa if (ra.val[1].int32 > 0) { 1173210276Sthompsa char *p; 1174210276Sthompsa 1175210276Sthompsa buf = ra.val[0].ptr; 1176210276Sthompsa if (strstr(buf[0], "+CME ERROR:") != NULL) { 1177210276Sthompsa buf[0] += 12; 1178228721Sdim errx(1, "%s", buf[0]); 1179210276Sthompsa } 1180210276Sthompsa freeresp(&ra); 1181210276Sthompsa } else 1182210276Sthompsa freeresp(&ra); 1183210276Sthompsa 1184202181Sthompsa if (ctx->pin == NULL) { 1185202181Sthompsa errx(1, "device requires PIN"); 1186202181Sthompsa } 1187202181Sthompsa 1188202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1189202181Sthompsa ctx->pin); 1190202181Sthompsa if (error != 0) { 1191202181Sthompsa errx(1, "wrong PIN"); 1192202181Sthompsa } 1193202181Sthompsa } 1194202181Sthompsa 1195202181Sthompsa /* 1196202181Sthompsa * Check if a PDP context has been configured and configure one 1197202181Sthompsa * if needed. 1198202181Sthompsa */ 1199202181Sthompsa ra.val[0].ptr = NULL; 1200202181Sthompsa ra.val[1].int32 = 0; 1201202181Sthompsa error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1202202181Sthompsa buf = ra.val[0].ptr; 1203202181Sthompsa needcfg = 1; 1204202181Sthompsa for (i = 0; i < ra.val[1].int32; i++) { 1205202181Sthompsa char apn[256]; 1206202181Sthompsa int cid; 1207202181Sthompsa error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1208202181Sthompsa &cid, apn); 1209202181Sthompsa if (error != 2) { 1210202181Sthompsa free(buf[i]); 1211202181Sthompsa continue; 1212202181Sthompsa } 1213202181Sthompsa 1214202181Sthompsa if (cid == ctx->pdp_ctx) { 1215202181Sthompsa ctx->con_apn = strdup(apn); 1216202181Sthompsa if (ctx->pdp_apn != NULL) { 1217202181Sthompsa if (strcmp(apn, ctx->pdp_apn) == 0) 1218202181Sthompsa needcfg = 0; 1219202181Sthompsa } 1220202181Sthompsa else { 1221202181Sthompsa needcfg = 0; 1222202181Sthompsa } 1223202181Sthompsa } 1224202181Sthompsa free(buf[i]); 1225202181Sthompsa } 1226202181Sthompsa free(buf); 1227202181Sthompsa 1228202181Sthompsa if (needcfg) { 1229202181Sthompsa if (ctx->pdp_apn == NULL) 1230202181Sthompsa errx(1, "device is not configured and no APN given"); 1231202181Sthompsa 1232202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, 1233202181Sthompsa "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1234202181Sthompsa if (error != 0) { 1235202181Sthompsa errx(1, "failed to configure device"); 1236202181Sthompsa } 1237202181Sthompsa ctx->con_apn = strdup(ctx->pdp_apn); 1238202181Sthompsa } 1239202181Sthompsa 1240202181Sthompsa if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1241202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, 1242202181Sthompsa "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1243202181Sthompsa (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1244202181Sthompsa (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1245202181Sthompsa } 1246202181Sthompsa 1247202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1248202181Sthompsa ctx->pdp_ctx); 1249202181Sthompsa if (error != 0) 1250202181Sthompsa return (-1); 1251202181Sthompsa 1252202181Sthompsa at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1253202181Sthompsa at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1254202181Sthompsa 1255202181Sthompsa tmr_add(&timers, 1, 5, tmr_status, ctx); 1256202181Sthompsa return (0); 1257202181Sthompsa} 1258202181Sthompsa 1259202181Sthompsastatic void 1260202181Sthompsado_disconnect(struct ctx *ctx) 1261202181Sthompsa{ 1262202181Sthompsa struct sockaddr_in sin, mask; 1263202181Sthompsa 1264202181Sthompsa /* Disconnect */ 1265202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1266202181Sthompsa ctx->pdp_ctx); 1267202181Sthompsa close(ctx->fd); 1268202181Sthompsa 1269202181Sthompsa /* Remove ip-address from interface */ 1270202181Sthompsa if (ctx->flags & IPASSIGNED) { 1271202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1272202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, 1273202181Sthompsa sizeof(mask.sin_addr.s_addr)); 1274202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 1275202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1276202181Sthompsa sizeof(sin.sin_addr.s_addr)); 1277202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1278202181Sthompsa (struct sockaddr *)&mask); 1279202181Sthompsa 1280202181Sthompsa if_ifdown(ctx->ifnam); 1281202181Sthompsa ctx->flags &= ~IPASSIGNED; 1282202181Sthompsa } 1283202181Sthompsa 1284202181Sthompsa /* Attempt to reset resolv.conf */ 1285202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 1286202181Sthompsa} 1287202181Sthompsa 1288202181Sthompsastatic void 1289202181Sthompsadaemonize(struct ctx *ctx) 1290202181Sthompsa{ 1291202181Sthompsa struct pidfh *pfh; 1292202181Sthompsa pid_t opid; 1293202181Sthompsa 1294202181Sthompsa snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1295202181Sthompsa 1296202181Sthompsa pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1297202181Sthompsa if (pfh == NULL) { 1298202181Sthompsa warn("Cannot create pidfile %s", ctx->pidfile); 1299202181Sthompsa return; 1300202181Sthompsa } 1301202181Sthompsa 1302202181Sthompsa if (daemon(0, 0) == -1) { 1303202181Sthompsa warn("Cannot daemonize"); 1304202181Sthompsa pidfile_remove(pfh); 1305202181Sthompsa return; 1306202181Sthompsa } 1307202181Sthompsa 1308202181Sthompsa pidfile_write(pfh); 1309202181Sthompsa ctx->pfh = pfh; 1310202181Sthompsa ctx->flags |= FLG_DAEMON; 1311202181Sthompsa 1312202181Sthompsa snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1313202181Sthompsa openlog(syslog_title, LOG_PID, LOG_USER); 1314202181Sthompsa syslog_open = 1; 1315202181Sthompsa} 1316202181Sthompsa 1317202181Sthompsastatic void 1318202181Sthompsasend_disconnect(const char *ifnam) 1319202181Sthompsa{ 1320202181Sthompsa char pidfile[128]; 1321202181Sthompsa FILE *fp; 1322202181Sthompsa pid_t pid; 1323202181Sthompsa int n; 1324202181Sthompsa 1325202181Sthompsa snprintf(pidfile, 127, PIDFILE, ifnam); 1326202181Sthompsa fp = fopen(pidfile, "r"); 1327202181Sthompsa if (fp == NULL) { 1328202181Sthompsa warn("Cannot open %s", pidfile); 1329202181Sthompsa return; 1330202181Sthompsa } 1331202181Sthompsa 1332202181Sthompsa n = fscanf(fp, "%d", &pid); 1333202181Sthompsa fclose(fp); 1334202181Sthompsa if (n != 1) { 1335202181Sthompsa warnx("unable to read daemon pid"); 1336202181Sthompsa return; 1337202181Sthompsa } 1338202181Sthompsa#ifdef DEBUG 1339202181Sthompsa fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1340202181Sthompsa#endif 1341202181Sthompsa kill(pid, SIGTERM); 1342202181Sthompsa} 1343202181Sthompsa 1344202181Sthompsastatic void 1345202181Sthompsausage(const char *exec) 1346202181Sthompsa{ 1347202181Sthompsa 1348202181Sthompsa printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1349202181Sthompsa "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1350202181Sthompsa printf("usage %s -d interface\n", exec); 1351202181Sthompsa} 1352202181Sthompsa 1353202181Sthompsaenum { 1354202181Sthompsa MODE_CONN, 1355202181Sthompsa MODE_DISC 1356202181Sthompsa}; 1357202181Sthompsa 1358202181Sthompsaint 1359202181Sthompsamain(int argc, char *argv[]) 1360202181Sthompsa{ 1361202181Sthompsa int ch, error, mode; 1362202181Sthompsa const char *ifnam = NULL; 1363202181Sthompsa char *tty = NULL; 1364202181Sthompsa char **p, **tty_list; 1365202181Sthompsa fd_set set; 1366202181Sthompsa struct ctx ctx; 1367202181Sthompsa struct itimerval it; 1368202181Sthompsa 1369202181Sthompsa TAILQ_INIT(&timers.head); 1370202181Sthompsa timers.res = 1; 1371202181Sthompsa 1372202181Sthompsa ctx.pdp_ctx = 1; 1373202181Sthompsa ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1374202181Sthompsa ctx.pin = NULL; 1375202181Sthompsa 1376202181Sthompsa ctx.con_status = 0; 1377202181Sthompsa ctx.con_apn = NULL; 1378202181Sthompsa ctx.con_oper = NULL; 1379202181Sthompsa ctx.con_net_stat = 0; 1380202181Sthompsa ctx.con_net_type = -1; 1381202181Sthompsa ctx.flags = 0; 1382202181Sthompsa ctx.resolv_path = RESOLV_PATH; 1383202181Sthompsa ctx.resolv = NULL; 1384202181Sthompsa ctx.ns = NULL; 1385202181Sthompsa ctx.dbm = 0; 1386202181Sthompsa 1387202181Sthompsa mode = MODE_CONN; 1388202181Sthompsa ctx.flags |= FLG_DELAYED; 1389202181Sthompsa 1390202181Sthompsa while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1391202181Sthompsa switch (ch) { 1392202181Sthompsa case 'a': 1393202181Sthompsa ctx.pdp_apn = argv[optind - 1]; 1394202181Sthompsa break; 1395202181Sthompsa case 'c': 1396202181Sthompsa ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1397202181Sthompsa if (ctx.pdp_ctx < 1) { 1398202181Sthompsa warnx("Invalid context ID, defaulting to 1"); 1399202181Sthompsa ctx.pdp_ctx = 1; 1400202181Sthompsa } 1401202181Sthompsa break; 1402202181Sthompsa case 'p': 1403202181Sthompsa ctx.pin = argv[optind - 1]; 1404202181Sthompsa break; 1405202181Sthompsa case 'u': 1406202181Sthompsa ctx.pdp_user = argv[optind - 1]; 1407202181Sthompsa break; 1408202181Sthompsa case 'k': 1409202181Sthompsa ctx.pdp_pwd = argv[optind - 1]; 1410202181Sthompsa break; 1411202181Sthompsa case 'r': 1412202181Sthompsa ctx.resolv_path = argv[optind - 1]; 1413202181Sthompsa break; 1414202181Sthompsa case 'd': 1415202181Sthompsa mode = MODE_DISC; 1416202181Sthompsa break; 1417202181Sthompsa case 'b': 1418202181Sthompsa ctx.flags &= ~FLG_DELAYED; 1419202181Sthompsa break; 1420202181Sthompsa case 'n': 1421202181Sthompsa ctx.flags |= FLG_NODAEMON; 1422202181Sthompsa break; 1423202181Sthompsa case 'f': 1424202181Sthompsa tty = argv[optind - 1]; 1425202181Sthompsa break; 1426202181Sthompsa case 'h': 1427202181Sthompsa case '?': 1428202181Sthompsa default: 1429202181Sthompsa usage(argv[0]); 1430202181Sthompsa exit(EXIT_SUCCESS); 1431202181Sthompsa } 1432202181Sthompsa } 1433202181Sthompsa 1434202181Sthompsa argc -= optind; 1435202181Sthompsa argv += optind; 1436202181Sthompsa 1437202181Sthompsa if (argc < 1) 1438202181Sthompsa errx(1, "no interface given"); 1439202181Sthompsa 1440202181Sthompsa ifnam = argv[argc - 1]; 1441202181Sthompsa ctx.ifnam = strdup(ifnam); 1442202181Sthompsa 1443202181Sthompsa switch (mode) { 1444202181Sthompsa case MODE_DISC: 1445202181Sthompsa printf("Disconnecting %s\n", ifnam); 1446202181Sthompsa send_disconnect(ifnam); 1447202181Sthompsa exit(EXIT_SUCCESS); 1448202181Sthompsa default: 1449202181Sthompsa break; 1450202181Sthompsa } 1451202181Sthompsa 1452202181Sthompsa signal(SIGHUP, sig_handle); 1453202181Sthompsa signal(SIGINT, sig_handle); 1454202181Sthompsa signal(SIGQUIT, sig_handle); 1455202181Sthompsa signal(SIGTERM, sig_handle); 1456202181Sthompsa signal(SIGALRM, sig_handle); 1457202181Sthompsa 1458202181Sthompsa it.it_interval.tv_sec = 1; 1459202181Sthompsa it.it_interval.tv_usec = 0; 1460202181Sthompsa it.it_value.tv_sec = 1; 1461202181Sthompsa it.it_value.tv_usec = 0; 1462202181Sthompsa error = setitimer(ITIMER_REAL, &it, NULL); 1463202181Sthompsa if (error != 0) 1464202181Sthompsa errx(1, "setitimer"); 1465202181Sthompsa 1466202181Sthompsa tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1467202181Sthompsa watchdog_reset(&ctx, 15); 1468202181Sthompsa 1469202181Sthompsa if (tty != NULL) { 1470202181Sthompsa error = do_connect(&ctx, tty); 1471202181Sthompsa if (error != 0) 1472202181Sthompsa errx(1, "Failed to open %s", tty); 1473202181Sthompsa } 1474202181Sthompsa else { 1475202181Sthompsa tty_list = get_tty(&ctx); 1476210276Sthompsa if (tty_list == NULL) 1477210276Sthompsa errx(1, "%s does not appear to be a uhso device", ifnam); 1478202181Sthompsa#ifdef DEBUG 1479202181Sthompsa if (tty_list == NULL) { 1480202181Sthompsa fprintf(stderr, "get_tty returned empty list\n"); 1481202181Sthompsa } else { 1482202181Sthompsa fprintf(stderr, "tty list:\n"); 1483202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1484202181Sthompsa fprintf(stderr, "\t %s\n", *p); 1485202181Sthompsa } 1486202181Sthompsa } 1487202181Sthompsa#endif 1488202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1489202181Sthompsa error = do_connect(&ctx, *p); 1490202181Sthompsa if (error == 0) { 1491202181Sthompsa tty = *p; 1492202181Sthompsa break; 1493202181Sthompsa } 1494202181Sthompsa } 1495202181Sthompsa if (*p == NULL) 1496202181Sthompsa errx(1, "Failed to obtain a control port, " 1497202181Sthompsa "try specifying one manually"); 1498202181Sthompsa } 1499202181Sthompsa 1500202181Sthompsa if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1501202181Sthompsa daemonize(&ctx); 1502202181Sthompsa 1503202181Sthompsa 1504202181Sthompsa FD_ZERO(&set); 1505202181Sthompsa FD_SET(ctx.fd, &set); 1506202181Sthompsa for (;;) { 1507202181Sthompsa 1508202181Sthompsa watchdog_disable(&ctx); 1509202181Sthompsa error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1510202181Sthompsa if (error <= 0) { 1511202181Sthompsa if (running && errno == EINTR) 1512202181Sthompsa continue; 1513202181Sthompsa if (ctx.flags & FLG_WDEXP) { 1514202181Sthompsa ctx.flags &= ~FLG_WDEXP; 1515202181Sthompsa watchdog_reset(&ctx, 5); 1516202181Sthompsa do_disconnect(&ctx); 1517202181Sthompsa watchdog_reset(&ctx, 15); 1518202181Sthompsa do_connect(&ctx, tty); 1519202181Sthompsa running = 1; 1520202181Sthompsa continue; 1521202181Sthompsa } 1522202181Sthompsa 1523202181Sthompsa break; 1524202181Sthompsa } 1525202181Sthompsa 1526202181Sthompsa if (FD_ISSET(ctx.fd, &set)) { 1527202181Sthompsa watchdog_reset(&ctx, 15); 1528202181Sthompsa error = at_async(&ctx, &ctx); 1529202181Sthompsa if (error != 0) 1530202181Sthompsa break; 1531202181Sthompsa } 1532202181Sthompsa FD_SET(ctx.fd, &set); 1533202181Sthompsa 1534202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1535202181Sthompsa printf("Status: %s (%s)", 1536202181Sthompsa ctx.con_status ? "connected" : "disconnected", 1537202181Sthompsa network_access_type[ctx.con_net_type]); 1538202181Sthompsa if (ctx.dbm < 0) 1539202181Sthompsa printf(", signal: %d dBm", ctx.dbm); 1540210276Sthompsa printf("\t\t\t\r"); 1541202181Sthompsa fflush(stdout); 1542202181Sthompsa } 1543202181Sthompsa } 1544202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1545202181Sthompsa printf("\n"); 1546202181Sthompsa 1547202181Sthompsa signal(SIGHUP, SIG_DFL); 1548202181Sthompsa signal(SIGINT, SIG_DFL); 1549202181Sthompsa signal(SIGQUIT, SIG_DFL); 1550202181Sthompsa signal(SIGTERM, SIG_DFL); 1551202181Sthompsa signal(SIGALRM, SIG_IGN); 1552202181Sthompsa 1553202181Sthompsa do_disconnect(&ctx); 1554202181Sthompsa 1555202181Sthompsa if (ctx.flags & FLG_DAEMON) { 1556202181Sthompsa pidfile_remove(ctx.pfh); 1557202181Sthompsa if (syslog_open) 1558202181Sthompsa closelog(); 1559202181Sthompsa } 1560202181Sthompsa 1561202181Sthompsa return (0); 1562202181Sthompsa} 1563