uhsoctl.c revision 202181
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: head/usr.sbin/uhsoctl/uhsoctl.c 202181 2010-01-13 03:16:31Z thompsa $ 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" 69202181Sthompsa#define SYSCTL_PORTS "dev.uhso.%d.ports" 70202181Sthompsa#define SYSCTL_NETIF "dev.uhso.%d.netif" 71202181Sthompsa#define SYSCTL_NAME_TTY "dev.uhso.%d.port.%s.tty" 72202181Sthompsa#define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc" 73202181Sthompsa#define RESOLV_PATH "/etc/resolv.conf" 74202181Sthompsa#define PIDFILE "/var/run/uhsoctl.%s.pid" 75202181Sthompsa 76202181Sthompsastatic const char *network_access_type[] = { 77202181Sthompsa "GSM", 78202181Sthompsa "Compact GSM", 79202181Sthompsa "UMTS", 80202181Sthompsa "GSM (EGPRS)", 81202181Sthompsa "HSDPA", 82202181Sthompsa "HSUPA", 83202181Sthompsa "HSDPA/HSUPA" 84202181Sthompsa}; 85202181Sthompsa 86202181Sthompsastatic const char *network_reg_status[] = { 87202181Sthompsa "Not registered", 88202181Sthompsa "Registered", 89202181Sthompsa "Searching for network", 90202181Sthompsa "Network registration denied", 91202181Sthompsa "Unknown", 92202181Sthompsa "Registered (roaming)" 93202181Sthompsa}; 94202181Sthompsa 95202181Sthompsastruct ctx { 96202181Sthompsa int fd; 97202181Sthompsa int flags; 98202181Sthompsa#define IPASSIGNED 0x01 99202181Sthompsa#define FLG_NODAEMON 0x02 /* Don't detach from terminal */ 100202181Sthompsa#define FLG_DAEMON 0x04 /* Running as daemon */ 101202181Sthompsa#define FLG_DELAYED 0x08 /* Fork into background after connect */ 102202181Sthompsa#define FLG_NEWDATA 0x10 103202181Sthompsa#define FLG_WATCHDOG 0x20 /* Watchdog enabled */ 104202181Sthompsa#define FLG_WDEXP 0x40 /* Watchdog expired */ 105202181Sthompsa const char *ifnam; 106202181Sthompsa const char *pin; /* device PIN */ 107202181Sthompsa 108202181Sthompsa char pidfile[128]; 109202181Sthompsa struct pidfh *pfh; 110202181Sthompsa 111202181Sthompsa time_t watchdog; 112202181Sthompsa 113202181Sthompsa /* PDP context settings */ 114202181Sthompsa int pdp_ctx; 115202181Sthompsa const char *pdp_apn; 116202181Sthompsa const char *pdp_user; 117202181Sthompsa const char *pdp_pwd; 118202181Sthompsa 119202181Sthompsa /* Connection status */ 120202181Sthompsa int con_status; /* Connected? */ 121202181Sthompsa char *con_apn; /* Connected APN */ 122202181Sthompsa char *con_oper; /* Operator name */ 123202181Sthompsa int con_net_stat; /* Network connection status */ 124202181Sthompsa int con_net_type; /* Network connection type */ 125202181Sthompsa 126202181Sthompsa /* Misc. status */ 127202181Sthompsa int dbm; 128202181Sthompsa 129202181Sthompsa /* IP and nameserver settings */ 130202181Sthompsa struct in_addr ip; 131202181Sthompsa char **ns; 132202181Sthompsa const char *resolv_path; 133202181Sthompsa char *resolv; /* Old resolv.conf */ 134202181Sthompsa size_t resolv_sz; 135202181Sthompsa}; 136202181Sthompsa 137202181Sthompsastatic int readline_buf(const char *, const char *, char *, size_t); 138202181Sthompsastatic int readline(int, char *, size_t); 139202181Sthompsastatic void daemonize(struct ctx *); 140202181Sthompsa 141202181Sthompsastatic int at_cmd_async(int, const char *, ...); 142202181Sthompsa 143202181Sthompsatypedef union { 144202181Sthompsa void *ptr; 145202181Sthompsa uint32_t int32; 146202181Sthompsa} resp_data; 147202181Sthompsatypedef struct { 148202181Sthompsa resp_data val[2]; 149202181Sthompsa} resp_arg; 150202181Sthompsatypedef void (*resp_cb)(resp_arg *, const char *, const char *); 151202181Sthompsa 152202181Sthompsatypedef void (*async_cb)(void *, const char *); 153202181Sthompsastruct async_handle { 154202181Sthompsa const char *cmd; 155202181Sthompsa async_cb func; 156202181Sthompsa}; 157202181Sthompsa 158202181Sthompsastatic void at_async_creg(void *, const char *); 159202181Sthompsastatic void at_async_cgreg(void *, const char *); 160202181Sthompsastatic void at_async_cops(void *, const char *); 161202181Sthompsastatic void at_async_owancall(void *, const char *); 162202181Sthompsastatic void at_async_owandata(void *, const char *); 163202181Sthompsastatic void at_async_csq(void *, const char *); 164202181Sthompsa 165202181Sthompsastatic struct async_handle async_cmd[] = { 166202181Sthompsa { "+CREG", at_async_creg }, 167202181Sthompsa { "+CGREG", at_async_cgreg }, 168202181Sthompsa { "+COPS", at_async_cops }, 169202181Sthompsa { "+CSQ", at_async_csq }, 170202181Sthompsa { "_OWANCALL", at_async_owancall }, 171202181Sthompsa { "_OWANDATA", at_async_owandata }, 172202181Sthompsa { NULL, NULL } 173202181Sthompsa}; 174202181Sthompsa 175202181Sthompsastruct timer_entry; 176202181Sthompsastruct timers { 177202181Sthompsa TAILQ_HEAD(, timer_entry) head; 178202181Sthompsa int res; 179202181Sthompsa}; 180202181Sthompsa 181202181Sthompsatypedef void (*tmr_cb)(int, void *); 182202181Sthompsastruct timer_entry { 183202181Sthompsa TAILQ_ENTRY(timer_entry) next; 184202181Sthompsa int id; 185202181Sthompsa int timeout; 186202181Sthompsa tmr_cb func; 187202181Sthompsa void *arg; 188202181Sthompsa}; 189202181Sthompsa 190202181Sthompsa 191202181Sthompsastatic struct timers timers; 192202181Sthompsastatic volatile int running = 1; 193202181Sthompsastatic int syslog_open = 0; 194202181Sthompsastatic char syslog_title[64]; 195202181Sthompsa 196202181Sthompsa/* Periodic timer, runs ready timer tasks every tick */ 197202181Sthompsastatic void 198202181Sthompsatmr_run(struct timers *tmrs) 199202181Sthompsa{ 200202181Sthompsa struct timer_entry *te, *te2; 201202181Sthompsa 202202181Sthompsa te = TAILQ_FIRST(&tmrs->head); 203202181Sthompsa if (te == NULL) 204202181Sthompsa return; 205202181Sthompsa 206202181Sthompsa te->timeout -= tmrs->res; 207202181Sthompsa while (te->timeout <= 0) { 208202181Sthompsa te2 = TAILQ_NEXT(te, next); 209202181Sthompsa TAILQ_REMOVE(&tmrs->head, te, next); 210202181Sthompsa if (te2 != NULL) 211202181Sthompsa te2->timeout -= tmrs->res; 212202181Sthompsa 213202181Sthompsa te->func(te->id, te->arg); 214202181Sthompsa free(te); 215202181Sthompsa te = te2; 216202181Sthompsa if (te == NULL) 217202181Sthompsa break; 218202181Sthompsa } 219202181Sthompsa} 220202181Sthompsa 221202181Sthompsa/* Add a new timer */ 222202181Sthompsastatic void 223202181Sthompsatmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg) 224202181Sthompsa{ 225202181Sthompsa struct timer_entry *te, *te2, *te3; 226202181Sthompsa 227202181Sthompsa te = malloc(sizeof(struct timer_entry)); 228202181Sthompsa memset(te, 0, sizeof(struct timer_entry)); 229202181Sthompsa 230202181Sthompsa te->timeout = timeout; 231202181Sthompsa te->func = func; 232202181Sthompsa te->arg = arg; 233202181Sthompsa te->id = id; 234202181Sthompsa 235202181Sthompsa te2 = TAILQ_FIRST(&tmrs->head); 236202181Sthompsa 237202181Sthompsa if (TAILQ_EMPTY(&tmrs->head)) { 238202181Sthompsa TAILQ_INSERT_HEAD(&tmrs->head, te, next); 239202181Sthompsa } else if (te->timeout < te2->timeout) { 240202181Sthompsa te2->timeout -= te->timeout; 241202181Sthompsa TAILQ_INSERT_HEAD(&tmrs->head, te, next); 242202181Sthompsa } else { 243202181Sthompsa while (te->timeout >= te2->timeout) { 244202181Sthompsa te->timeout -= te2->timeout; 245202181Sthompsa te3 = TAILQ_NEXT(te2, next); 246202181Sthompsa if (te3 == NULL || te3->timeout > te->timeout) 247202181Sthompsa break; 248202181Sthompsa te2 = te3; 249202181Sthompsa } 250202181Sthompsa TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next); 251202181Sthompsa } 252202181Sthompsa} 253202181Sthompsa 254202181Sthompsa#define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG 255202181Sthompsa#define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG 256202181Sthompsa 257202181Sthompsastatic void 258202181Sthompsawatchdog_reset(struct ctx *ctx, int timeout) 259202181Sthompsa{ 260202181Sthompsa struct timespec tp; 261202181Sthompsa 262202181Sthompsa clock_gettime(CLOCK_MONOTONIC, &tp), 263202181Sthompsa ctx->watchdog = tp.tv_sec + timeout; 264202181Sthompsa 265202181Sthompsa watchdog_enable(ctx); 266202181Sthompsa} 267202181Sthompsa 268202181Sthompsastatic void 269202181Sthompsatmr_creg(int id, void *arg) 270202181Sthompsa{ 271202181Sthompsa struct ctx *ctx = arg; 272202181Sthompsa 273202181Sthompsa at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 274202181Sthompsa watchdog_reset(ctx, 10); 275202181Sthompsa} 276202181Sthompsa 277202181Sthompsastatic void 278202181Sthompsatmr_cgreg(int id, void *arg) 279202181Sthompsa{ 280202181Sthompsa struct ctx *ctx = arg; 281202181Sthompsa 282202181Sthompsa at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 283202181Sthompsa watchdog_reset(ctx, 10); 284202181Sthompsa} 285202181Sthompsa 286202181Sthompsastatic void 287202181Sthompsatmr_status(int id, void *arg) 288202181Sthompsa{ 289202181Sthompsa struct ctx *ctx = arg; 290202181Sthompsa 291202181Sthompsa at_cmd_async(ctx->fd, "AT+CSQ\r\n"); 292202181Sthompsa watchdog_reset(ctx, 10); 293202181Sthompsa} 294202181Sthompsa 295202181Sthompsastatic void 296202181Sthompsatmr_watchdog(int id, void *arg) 297202181Sthompsa{ 298202181Sthompsa struct ctx *ctx = arg; 299202181Sthompsa pid_t self; 300202181Sthompsa struct timespec tp; 301202181Sthompsa 302202181Sthompsa tmr_add(&timers, 1, 5, tmr_watchdog, ctx); 303202181Sthompsa 304202181Sthompsa if (!(ctx->flags & FLG_WATCHDOG)) 305202181Sthompsa return; 306202181Sthompsa 307202181Sthompsa clock_gettime(CLOCK_MONOTONIC, &tp); 308202181Sthompsa 309202181Sthompsa if (tp.tv_sec >= ctx->watchdog) { 310202181Sthompsa#ifdef DEBUG 311202181Sthompsa fprintf(stderr, "Watchdog expired\n"); 312202181Sthompsa#endif 313202181Sthompsa ctx->flags |= FLG_WDEXP; 314202181Sthompsa self = getpid(); 315202181Sthompsa kill(self, SIGHUP); 316202181Sthompsa } 317202181Sthompsa} 318202181Sthompsa 319202181Sthompsastatic void 320202181Sthompsasig_handle(int sig) 321202181Sthompsa{ 322202181Sthompsa 323202181Sthompsa switch (sig) { 324202181Sthompsa case SIGHUP: 325202181Sthompsa case SIGINT: 326202181Sthompsa case SIGQUIT: 327202181Sthompsa case SIGTERM: 328202181Sthompsa running = 0; 329202181Sthompsa break; 330202181Sthompsa case SIGALRM: 331202181Sthompsa tmr_run(&timers); 332202181Sthompsa break; 333202181Sthompsa } 334202181Sthompsa} 335202181Sthompsa 336202181Sthompsastatic void 337202181Sthompsalogger(int pri, const char *fmt, ...) 338202181Sthompsa{ 339202181Sthompsa char *buf; 340202181Sthompsa va_list ap; 341202181Sthompsa 342202181Sthompsa va_start(ap, fmt); 343202181Sthompsa vasprintf(&buf, fmt, ap); 344202181Sthompsa if (syslog_open) 345202181Sthompsa syslog(pri, buf); 346202181Sthompsa else { 347202181Sthompsa switch (pri) { 348202181Sthompsa case LOG_INFO: 349202181Sthompsa case LOG_NOTICE: 350202181Sthompsa printf("%s\n", buf); 351202181Sthompsa break; 352202181Sthompsa default: 353202181Sthompsa fprintf(stderr, "%s: %s\n", getprogname(), buf); 354202181Sthompsa break; 355202181Sthompsa } 356202181Sthompsa } 357202181Sthompsa 358202181Sthompsa free(buf); 359202181Sthompsa va_end(ap); 360202181Sthompsa} 361202181Sthompsa 362202181Sthompsa/* Add/remove IP address from an interface */ 363202181Sthompsastatic int 364202181Sthompsaifaddr_ad(int d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 365202181Sthompsa{ 366202181Sthompsa struct ifaliasreq req; 367202181Sthompsa int fd, error; 368202181Sthompsa 369202181Sthompsa fd = socket(AF_INET, SOCK_DGRAM, 0); 370202181Sthompsa if (fd < 0) 371202181Sthompsa return (-1); 372202181Sthompsa 373202181Sthompsa memset(&req, 0, sizeof(struct ifaliasreq)); 374202181Sthompsa strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name)); 375202181Sthompsa memcpy(&req.ifra_addr, sa, sa->sa_len); 376202181Sthompsa memcpy(&req.ifra_mask, mask, mask->sa_len); 377202181Sthompsa 378202181Sthompsa error = ioctl(fd, d, (char *)&req); 379202181Sthompsa close(fd); 380202181Sthompsa return (error); 381202181Sthompsa} 382202181Sthompsa 383202181Sthompsa#define if_ifup(ifnam) if_setflags(ifnam, IFF_UP) 384202181Sthompsa#define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP) 385202181Sthompsa 386202181Sthompsastatic int 387202181Sthompsaif_setflags(const char *ifnam, int flags) 388202181Sthompsa{ 389202181Sthompsa struct ifreq ifr; 390202181Sthompsa int fd, error; 391202181Sthompsa unsigned int oflags = 0; 392202181Sthompsa 393202181Sthompsa memset(&ifr, 0, sizeof(struct ifreq)); 394202181Sthompsa strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name)); 395202181Sthompsa 396202181Sthompsa fd = socket(AF_INET, SOCK_DGRAM, 0); 397202181Sthompsa if (fd < 0) 398202181Sthompsa return (-1); 399202181Sthompsa 400202181Sthompsa error = ioctl(fd, SIOCGIFFLAGS, &ifr); 401202181Sthompsa if (error == 0) { 402202181Sthompsa oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16); 403202181Sthompsa } 404202181Sthompsa 405202181Sthompsa if (flags < 0) 406202181Sthompsa oflags &= ~(-flags); 407202181Sthompsa else 408202181Sthompsa oflags |= flags; 409202181Sthompsa 410202181Sthompsa ifr.ifr_flags = oflags & 0xffff; 411202181Sthompsa ifr.ifr_flagshigh = oflags >> 16; 412202181Sthompsa 413202181Sthompsa error = ioctl(fd, SIOCSIFFLAGS, &ifr); 414202181Sthompsa if (error != 0) 415202181Sthompsa warn("ioctl SIOCSIFFLAGS"); 416202181Sthompsa 417202181Sthompsa close(fd); 418202181Sthompsa return (error); 419202181Sthompsa} 420202181Sthompsa 421202181Sthompsastatic int 422202181Sthompsaifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 423202181Sthompsa{ 424202181Sthompsa int error; 425202181Sthompsa 426202181Sthompsa error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask); 427202181Sthompsa if (error != 0) 428202181Sthompsa warn("ioctl SIOCAIFADDR"); 429202181Sthompsa return (error); 430202181Sthompsa} 431202181Sthompsa 432202181Sthompsastatic int 433202181Sthompsaifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask) 434202181Sthompsa{ 435202181Sthompsa int error; 436202181Sthompsa 437202181Sthompsa error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask); 438202181Sthompsa if (error != 0) 439202181Sthompsa warn("ioctl SIOCDIFADDR"); 440202181Sthompsa return (error); 441202181Sthompsa} 442202181Sthompsa 443202181Sthompsastatic int 444202181Sthompsaset_nameservers(struct ctx *ctx, const char *respath, int ns, ...) 445202181Sthompsa{ 446202181Sthompsa int i, n, fd; 447202181Sthompsa FILE *fp; 448202181Sthompsa char *p; 449202181Sthompsa va_list ap; 450202181Sthompsa struct stat sb; 451202181Sthompsa char buf[512]; 452202181Sthompsa 453202181Sthompsa if (ctx->ns != NULL) { 454202181Sthompsa for (i = 0; ctx->ns[i] != NULL; i++) { 455202181Sthompsa free(ctx->ns[i]); 456202181Sthompsa } 457202181Sthompsa free(ctx->ns); 458202181Sthompsa } 459202181Sthompsa 460202181Sthompsa fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW); 461202181Sthompsa if (fd < 0) 462202181Sthompsa return (-1); 463202181Sthompsa 464202181Sthompsa if (ns == 0) { 465202181Sthompsa /* Attempt to restore old resolv.conf */ 466202181Sthompsa if (ctx->resolv != NULL) { 467202181Sthompsa ftruncate(fd, 0); 468202181Sthompsa lseek(fd, 0, SEEK_SET); 469202181Sthompsa write(fd, ctx->resolv, ctx->resolv_sz); 470202181Sthompsa free(ctx->resolv); 471202181Sthompsa ctx->resolv = NULL; 472202181Sthompsa ctx->resolv_sz = 0; 473202181Sthompsa } 474202181Sthompsa close(fd); 475202181Sthompsa return (0); 476202181Sthompsa } 477202181Sthompsa 478202181Sthompsa 479202181Sthompsa ctx->ns = malloc(sizeof(char *) * (ns + 1)); 480202181Sthompsa if (ctx->ns == NULL) { 481202181Sthompsa close(fd); 482202181Sthompsa return (-1); 483202181Sthompsa } 484202181Sthompsa 485202181Sthompsa va_start(ap, ns); 486202181Sthompsa for (i = 0; i < ns; i++) { 487202181Sthompsa p = va_arg(ap, char *); 488202181Sthompsa ctx->ns[i] = strdup(p); 489202181Sthompsa } 490202181Sthompsa ctx->ns[i] = NULL; 491202181Sthompsa va_end(ap); 492202181Sthompsa 493202181Sthompsa /* Attempt to backup the old resolv.conf */ 494202181Sthompsa if (ctx->resolv == NULL) { 495202181Sthompsa i = fstat(fd, &sb); 496202181Sthompsa if (i == 0 && sb.st_size != 0) { 497202181Sthompsa ctx->resolv_sz = sb.st_size; 498202181Sthompsa ctx->resolv = malloc(sb.st_size); 499202181Sthompsa if (ctx->resolv != NULL) { 500202181Sthompsa n = read(fd, ctx->resolv, sb.st_size); 501202181Sthompsa if (n != sb.st_size) { 502202181Sthompsa free(ctx->resolv); 503202181Sthompsa ctx->resolv = NULL; 504202181Sthompsa } 505202181Sthompsa } 506202181Sthompsa } 507202181Sthompsa } 508202181Sthompsa 509202181Sthompsa 510202181Sthompsa ftruncate(fd, 0); 511202181Sthompsa lseek(fd, 0, SEEK_SET); 512202181Sthompsa fp = fdopen(fd, "w"); 513202181Sthompsa 514202181Sthompsa /* 515202181Sthompsa * Write back everything other than nameserver entries to the 516202181Sthompsa * new resolv.conf 517202181Sthompsa */ 518202181Sthompsa if (ctx->resolv != NULL) { 519202181Sthompsa p = ctx->resolv; 520202181Sthompsa while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf, 521202181Sthompsa sizeof(buf))) > 0) { 522202181Sthompsa p += i; 523202181Sthompsa if (strncasecmp(buf, "nameserver", 10) == 0) 524202181Sthompsa continue; 525202181Sthompsa fprintf(fp, "%s", buf); 526202181Sthompsa } 527202181Sthompsa } 528202181Sthompsa 529202181Sthompsa for (i = 0; ctx->ns[i] != NULL; i++) { 530202181Sthompsa fprintf(fp, "nameserver %s\n", ctx->ns[i]); 531202181Sthompsa } 532202181Sthompsa fclose(fp); 533202181Sthompsa return (0); 534202181Sthompsa} 535202181Sthompsa 536202181Sthompsa/* Read a \n-terminated line from buffer */ 537202181Sthompsastatic int 538202181Sthompsareadline_buf(const char *s, const char *e, char *buf, size_t bufsz) 539202181Sthompsa{ 540202181Sthompsa int pos = 0; 541202181Sthompsa char *p = buf; 542202181Sthompsa 543202181Sthompsa for (; s < e; s++) { 544202181Sthompsa *p = *s; 545202181Sthompsa pos++; 546202181Sthompsa if (pos >= (bufsz - 1)) 547202181Sthompsa break; 548202181Sthompsa if (*p++ == '\n') 549202181Sthompsa break; 550202181Sthompsa } 551202181Sthompsa *p = '\0'; 552202181Sthompsa return (pos); 553202181Sthompsa} 554202181Sthompsa 555202181Sthompsa/* Read a \n-terminated line from file */ 556202181Sthompsastatic int 557202181Sthompsareadline(int fd, char *buf, size_t bufsz) 558202181Sthompsa{ 559202181Sthompsa int n = 0, pos = 0; 560202181Sthompsa char *p = buf; 561202181Sthompsa 562202181Sthompsa for (;;) { 563202181Sthompsa n = read(fd, p, 1); 564202181Sthompsa if (n <= 0) 565202181Sthompsa break; 566202181Sthompsa pos++; 567202181Sthompsa if (pos >= (bufsz - 1)) 568202181Sthompsa break; 569202181Sthompsa if (*p++ == '\n') 570202181Sthompsa break; 571202181Sthompsa } 572202181Sthompsa *p = '\0'; 573202181Sthompsa return (n <= 0 ? n : pos); 574202181Sthompsa} 575202181Sthompsa 576202181Sthompsa/* 577202181Sthompsa * Synchronous AT command 578202181Sthompsa */ 579202181Sthompsastatic int 580202181Sthompsaat_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...) 581202181Sthompsa{ 582202181Sthompsa size_t l; 583202181Sthompsa int n, error, retval = 0; 584202181Sthompsa va_list ap; 585202181Sthompsa fd_set set; 586202181Sthompsa char buf[512]; 587202181Sthompsa char cmd[64]; 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 605202181Sthompsa fprintf(stderr, "SYNC_EXP: %s (%d)\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); 617202181Sthompsa if (error < 0 && errno == EINTR && ctx->flags & FLG_WDEXP) { 618202181Sthompsa watchdog_disable(ctx); 619202181Sthompsa retval = -2; 620202181Sthompsa break; 621202181Sthompsa } 622202181Sthompsa } while (error <= 0 && errno == EINTR); 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 638202181Sthompsa#ifdef DEBUG 639202181Sthompsa fprintf(stderr, "SYNC_RESP: %s", buf); 640202181Sthompsa#endif 641202181Sthompsa 642202181Sthompsa if (strncmp(buf, "OK", 2) == 0) { 643202181Sthompsa break; 644202181Sthompsa } 645202181Sthompsa else if (strncmp(buf, "ERROR", 5) == 0) { 646202181Sthompsa retval = -1; 647202181Sthompsa break; 648202181Sthompsa } 649202181Sthompsa 650202181Sthompsa if (resp != NULL) { 651202181Sthompsa retval = strncmp(resp, buf, l); 652202181Sthompsa if (retval == 0 && cb != NULL) { 653202181Sthompsa cb(ra, cmd, buf); 654202181Sthompsa } 655202181Sthompsa } 656202181Sthompsa } 657202181Sthompsa#ifdef DEBUG 658202181Sthompsa fprintf(stderr, "SYNC_RETVAL=%d\n", retval); 659202181Sthompsa#endif 660202181Sthompsa return (retval); 661202181Sthompsa} 662202181Sthompsa 663202181Sthompsastatic int 664202181Sthompsaat_cmd_async(int fd, const char *cf, ...) 665202181Sthompsa{ 666202181Sthompsa size_t l; 667202181Sthompsa va_list ap; 668202181Sthompsa char cmd[64]; 669202181Sthompsa 670202181Sthompsa va_start(ap, cf); 671202181Sthompsa vsnprintf(cmd, sizeof(cmd), cf, ap); 672202181Sthompsa va_end(ap); 673202181Sthompsa 674202181Sthompsa#ifdef DEBUG 675202181Sthompsa fprintf(stderr, "CMD: %s", cmd); 676202181Sthompsa#endif 677202181Sthompsa l = strlen(cmd); 678202181Sthompsa return (write(fd, cmd, l)); 679202181Sthompsa} 680202181Sthompsa 681202181Sthompsastatic void 682202181Sthompsasaveresp(resp_arg *ra, const char *cmd, const char *resp) 683202181Sthompsa{ 684202181Sthompsa char **buf; 685202181Sthompsa int i = ra->val[1].int32; 686202181Sthompsa 687202181Sthompsa buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1)); 688202181Sthompsa if (buf == NULL) 689202181Sthompsa return; 690202181Sthompsa 691202181Sthompsa buf[i] = strdup(resp); 692202181Sthompsa 693202181Sthompsa ra->val[0].ptr = buf; 694202181Sthompsa ra->val[1].int32 = i + 1; 695202181Sthompsa} 696202181Sthompsa 697202181Sthompsastatic void 698202181Sthompsaat_async_creg(void *arg, const char *resp) 699202181Sthompsa{ 700202181Sthompsa struct ctx *ctx = arg; 701202181Sthompsa int n, reg; 702202181Sthompsa 703202181Sthompsa n = sscanf(resp, "+CREG: %*d,%d", ®); 704202181Sthompsa if (n != 1) { 705202181Sthompsa n = sscanf(resp, "+CREG: %d", ®); 706202181Sthompsa if (n != 1) 707202181Sthompsa return; 708202181Sthompsa } 709202181Sthompsa 710202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 711202181Sthompsa tmr_add(&timers, 1, 1, tmr_creg, ctx); 712202181Sthompsa } 713202181Sthompsa else { 714202181Sthompsa tmr_add(&timers, 1, 30, tmr_creg, ctx); 715202181Sthompsa } 716202181Sthompsa 717202181Sthompsa if (ctx->con_net_stat == reg) 718202181Sthompsa return; 719202181Sthompsa 720202181Sthompsa ctx->con_net_stat = reg; 721202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 722202181Sthompsa} 723202181Sthompsa 724202181Sthompsastatic void 725202181Sthompsaat_async_cgreg(void *arg, const char *resp) 726202181Sthompsa{ 727202181Sthompsa struct ctx *ctx = arg; 728202181Sthompsa int n, reg; 729202181Sthompsa 730202181Sthompsa n = sscanf(resp, "+CGREG: %*d,%d", ®); 731202181Sthompsa if (n != 1) { 732202181Sthompsa n = sscanf(resp, "+CGREG: %d", ®); 733202181Sthompsa if (n != 1) 734202181Sthompsa return; 735202181Sthompsa } 736202181Sthompsa 737202181Sthompsa if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) { 738202181Sthompsa tmr_add(&timers, 1, 1, tmr_cgreg, ctx); 739202181Sthompsa } 740202181Sthompsa else { 741202181Sthompsa tmr_add(&timers, 1, 30, tmr_cgreg, ctx); 742202181Sthompsa } 743202181Sthompsa 744202181Sthompsa if (ctx->con_net_stat == reg) 745202181Sthompsa return; 746202181Sthompsa 747202181Sthompsa ctx->con_net_stat = reg; 748202181Sthompsa at_cmd_async(ctx->fd, "AT+COPS?\r\n"); 749202181Sthompsa} 750202181Sthompsa 751202181Sthompsa 752202181Sthompsastatic void 753202181Sthompsaat_async_cops(void *arg, const char *resp) 754202181Sthompsa{ 755202181Sthompsa struct ctx *ctx = arg; 756202181Sthompsa int n, at; 757202181Sthompsa char opr[64]; 758202181Sthompsa 759202181Sthompsa n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d", 760202181Sthompsa opr, &at); 761202181Sthompsa if (n != 2) 762202181Sthompsa return; 763202181Sthompsa 764202181Sthompsa if (ctx->con_oper != NULL) { 765202181Sthompsa if (ctx->con_net_type == at && 766202181Sthompsa strcasecmp(opr, ctx->con_oper) == 0) 767202181Sthompsa return; 768202181Sthompsa free(ctx->con_oper); 769202181Sthompsa } 770202181Sthompsa 771202181Sthompsa ctx->con_oper = strdup(opr); 772202181Sthompsa ctx->con_net_type = at; 773202181Sthompsa 774202181Sthompsa if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) { 775202181Sthompsa logger(LOG_NOTICE, "%s to \"%s\" (%s)", 776202181Sthompsa network_reg_status[ctx->con_net_stat], 777202181Sthompsa ctx->con_oper, network_access_type[ctx->con_net_type]); 778202181Sthompsa if (ctx->con_status != 1) { 779202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n", 780202181Sthompsa ctx->pdp_ctx); 781202181Sthompsa } 782202181Sthompsa } 783202181Sthompsa else { 784202181Sthompsa logger(LOG_NOTICE, "%s (%s)", 785202181Sthompsa network_reg_status[ctx->con_net_stat], 786202181Sthompsa network_access_type[ctx->con_net_type]); 787202181Sthompsa } 788202181Sthompsa} 789202181Sthompsa 790202181Sthompsa/* 791202181Sthompsa * Signal strength for pretty console output 792202181Sthompsa * 793202181Sthompsa * From 3GPP TS 27.007 V8.3.0, Section 8.5 794202181Sthompsa * 0 = -113 dBm or less 795202181Sthompsa * 1 = -111 dBm 796202181Sthompsa * 2...30 = -109...-53 dBm 797202181Sthompsa * 31 = -51 dBm or greater 798202181Sthompsa * 799202181Sthompsa * So, dbm = (rssi * 2) - 113 800202181Sthompsa*/ 801202181Sthompsastatic void 802202181Sthompsaat_async_csq(void *arg, const char *resp) 803202181Sthompsa{ 804202181Sthompsa struct ctx *ctx = arg; 805202181Sthompsa int n, rssi; 806202181Sthompsa 807202181Sthompsa n = sscanf(resp, "+CSQ: %d,%*d", &rssi); 808202181Sthompsa if (n != 1) 809202181Sthompsa return; 810202181Sthompsa if (rssi == 99) 811202181Sthompsa ctx->dbm = 0; 812202181Sthompsa else { 813202181Sthompsa ctx->dbm = (rssi * 2) - 113; 814202181Sthompsa tmr_add(&timers, 1, 15, tmr_status, ctx); 815202181Sthompsa } 816202181Sthompsa 817202181Sthompsa ctx->flags |= FLG_NEWDATA; 818202181Sthompsa} 819202181Sthompsa 820202181Sthompsastatic void 821202181Sthompsaat_async_owancall(void *arg, const char *resp) 822202181Sthompsa{ 823202181Sthompsa struct ctx *ctx = arg; 824202181Sthompsa int n, i; 825202181Sthompsa 826202181Sthompsa n = sscanf(resp, "_OWANCALL: %*d,%d", &i); 827202181Sthompsa if (n != 1) 828202181Sthompsa return; 829202181Sthompsa 830202181Sthompsa if (i == ctx->con_status) 831202181Sthompsa return; 832202181Sthompsa 833202181Sthompsa at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx); 834202181Sthompsa 835202181Sthompsa ctx->con_status = i; 836202181Sthompsa if (ctx->con_status == 1) { 837202181Sthompsa logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s", 838202181Sthompsa ctx->con_oper, ctx->con_apn, 839202181Sthompsa network_access_type[ctx->con_net_type]); 840202181Sthompsa } 841202181Sthompsa else { 842202181Sthompsa logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)", 843202181Sthompsa ctx->con_oper, ctx->con_apn); 844202181Sthompsa } 845202181Sthompsa} 846202181Sthompsa 847202181Sthompsastatic void 848202181Sthompsaat_async_owandata(void *arg, const char *resp) 849202181Sthompsa{ 850202181Sthompsa struct ctx *ctx = arg; 851202181Sthompsa char ip[40], ns1[40], ns2[40]; 852202181Sthompsa int n, error, rs; 853202181Sthompsa struct ifaddrs *ifap, *ifa; 854202181Sthompsa struct sockaddr_in sin, mask; 855202181Sthompsa struct sockaddr_dl sdl; 856202181Sthompsa struct { 857202181Sthompsa struct rt_msghdr rtm; 858202181Sthompsa char buf[512]; 859202181Sthompsa } r; 860202181Sthompsa char *cp = r.buf; 861202181Sthompsa 862202181Sthompsa n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]", 863202181Sthompsa ip, ns1, ns2); 864202181Sthompsa if (n != 3) 865202181Sthompsa return; 866202181Sthompsa 867202181Sthompsa /* XXX: AF_INET assumption */ 868202181Sthompsa 869202181Sthompsa logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2); 870202181Sthompsa 871202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 872202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr)); 873202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 874202181Sthompsa 875202181Sthompsa if (ctx->flags & IPASSIGNED) { 876202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 877202181Sthompsa sizeof(sin.sin_addr.s_addr)); 878202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 879202181Sthompsa (struct sockaddr *)&mask); 880202181Sthompsa } 881202181Sthompsa inet_pton(AF_INET, ip, &ctx->ip.s_addr); 882202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 883202181Sthompsa sizeof(sin.sin_addr.s_addr)); 884202181Sthompsa 885202181Sthompsa error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin, 886202181Sthompsa (struct sockaddr *)&mask); 887202181Sthompsa if (error != 0) { 888202181Sthompsa logger(LOG_ERR, "failed to set ip-address"); 889202181Sthompsa return; 890202181Sthompsa } 891202181Sthompsa 892202181Sthompsa if_ifup(ctx->ifnam); 893202181Sthompsa 894202181Sthompsa ctx->flags |= IPASSIGNED; 895202181Sthompsa 896202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 897202181Sthompsa error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2); 898202181Sthompsa if (error != 0) { 899202181Sthompsa logger(LOG_ERR, "failed to set nameservers"); 900202181Sthompsa } 901202181Sthompsa 902202181Sthompsa error = getifaddrs(&ifap); 903202181Sthompsa if (error != 0) { 904202181Sthompsa logger(LOG_ERR, "getifaddrs: %s", strerror(errno)); 905202181Sthompsa return; 906202181Sthompsa } 907202181Sthompsa 908202181Sthompsa for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 909202181Sthompsa if (ifa->ifa_addr->sa_family != AF_LINK) 910202181Sthompsa continue; 911202181Sthompsa if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) { 912202181Sthompsa memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr, 913202181Sthompsa sizeof(struct sockaddr_dl)); 914202181Sthompsa break; 915202181Sthompsa } 916202181Sthompsa } 917202181Sthompsa if (ifa == NULL) 918202181Sthompsa return; 919202181Sthompsa 920202181Sthompsa rs = socket(PF_ROUTE, SOCK_RAW, 0); 921202181Sthompsa if (rs < 0) { 922202181Sthompsa logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno)); 923202181Sthompsa return; 924202181Sthompsa } 925202181Sthompsa 926202181Sthompsa memset(&r, 0, sizeof(r)); 927202181Sthompsa 928202181Sthompsa r.rtm.rtm_version = RTM_VERSION; 929202181Sthompsa r.rtm.rtm_type = RTM_ADD; 930202181Sthompsa r.rtm.rtm_flags = RTF_UP | RTF_STATIC; 931202181Sthompsa r.rtm.rtm_pid = getpid(); 932202181Sthompsa memset(&sin, 0, sizeof(struct sockaddr_in)); 933202181Sthompsa sin.sin_family = AF_INET; 934202181Sthompsa sin.sin_len = sizeof(struct sockaddr_in); 935202181Sthompsa 936202181Sthompsa memcpy(cp, &sin, sin.sin_len); 937202181Sthompsa cp += SA_SIZE(&sin); 938202181Sthompsa memcpy(cp, &sdl, sdl.sdl_len); 939202181Sthompsa cp += SA_SIZE(&sdl); 940202181Sthompsa memcpy(cp, &sin, sin.sin_len); 941202181Sthompsa r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK; 942202181Sthompsa r.rtm.rtm_msglen = sizeof(r); 943202181Sthompsa 944202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 945202181Sthompsa if (n != r.rtm.rtm_msglen) { 946202181Sthompsa r.rtm.rtm_type = RTM_DELETE; 947202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 948202181Sthompsa r.rtm.rtm_type = RTM_ADD; 949202181Sthompsa n = write(rs, &r, r.rtm.rtm_msglen); 950202181Sthompsa } 951202181Sthompsa 952202181Sthompsa if (n != r.rtm.rtm_msglen) { 953202181Sthompsa logger(LOG_ERR, "failed to set default route: %s", 954202181Sthompsa strerror(errno)); 955202181Sthompsa } 956202181Sthompsa close(rs); 957202181Sthompsa 958202181Sthompsa /* Delayed daemonization */ 959202181Sthompsa if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON)) 960202181Sthompsa daemonize(ctx); 961202181Sthompsa} 962202181Sthompsa 963202181Sthompsastatic int 964202181Sthompsaat_async(struct ctx *ctx, void *arg) 965202181Sthompsa{ 966202181Sthompsa int n, i; 967202181Sthompsa size_t l; 968202181Sthompsa char buf[512]; 969202181Sthompsa 970202181Sthompsa watchdog_reset(ctx, 15); 971202181Sthompsa 972202181Sthompsa bzero(buf, sizeof(buf)); 973202181Sthompsa n = readline(ctx->fd, buf, sizeof(buf)); 974202181Sthompsa if (n <= 0) 975202181Sthompsa return (n <= 0 ? -1 : 0); 976202181Sthompsa 977202181Sthompsa#ifdef DEBUG 978202181Sthompsa fprintf(stderr, "AT_ASYNC_RESP: %s", buf); 979202181Sthompsa#endif 980202181Sthompsa for (i = 0; async_cmd[i].cmd != NULL; i++) { 981202181Sthompsa l = strlen(async_cmd[i].cmd); 982202181Sthompsa if (strncmp(buf, async_cmd[i].cmd, l) == 0) { 983202181Sthompsa async_cmd[i].func(arg, buf); 984202181Sthompsa } 985202181Sthompsa } 986202181Sthompsa return (0); 987202181Sthompsa} 988202181Sthompsa 989202181Sthompsastatic const char *port_type_list[] = { 990202181Sthompsa "control", "application", "application2", NULL 991202181Sthompsa}; 992202181Sthompsa 993202181Sthompsa/* 994202181Sthompsa * Attempts to find a list of control tty for the interface 995202181Sthompsa * FreeBSD attaches USb devices per interface so we have to go through 996202181Sthompsa * hoops to find which ttys that belong to our network interface. 997202181Sthompsa */ 998202181Sthompsastatic char ** 999202181Sthompsaget_tty(struct ctx *ctx) 1000202181Sthompsa{ 1001202181Sthompsa char buf[64]; 1002202181Sthompsa char data[128]; 1003202181Sthompsa size_t len; 1004202181Sthompsa int error; 1005202181Sthompsa unsigned int i; 1006202181Sthompsa char **list = NULL; 1007202181Sthompsa int list_size = 0; 1008202181Sthompsa const char **p; 1009202181Sthompsa 1010202181Sthompsa for (i = 0; ; i++) { 1011202181Sthompsa /* Basic test to check if we're even in the right ballpark */ 1012202181Sthompsa snprintf(buf, 64, SYSCTL_TEST, i); 1013202181Sthompsa len = 127; 1014202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1015202181Sthompsa#ifdef DEBUG 1016202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1017202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1018202181Sthompsa#endif 1019202181Sthompsa if (error < 0) 1020202181Sthompsa return NULL; 1021202181Sthompsa if (strcasecmp(data, "uhso") != 0) 1022202181Sthompsa return NULL; 1023202181Sthompsa 1024202181Sthompsa /* Check for interface */ 1025202181Sthompsa snprintf(buf, 64, SYSCTL_NETIF, i); 1026202181Sthompsa len = 127; 1027202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1028202181Sthompsa#ifdef DEBUG 1029202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1030202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1031202181Sthompsa#endif 1032202181Sthompsa if (error < 0) 1033202181Sthompsa continue; 1034202181Sthompsa 1035202181Sthompsa if (strcasecmp(data, ctx->ifnam) != 0) 1036202181Sthompsa continue; 1037202181Sthompsa#ifdef DEBUG 1038202181Sthompsa fprintf(stderr, "Found %s at %s\n", ctx->ifnam, buf); 1039202181Sthompsa#endif 1040202181Sthompsa break; 1041202181Sthompsa } 1042202181Sthompsa 1043202181Sthompsa /* Add multiplexed ports */ 1044202181Sthompsa for (p = port_type_list; *p != NULL; p++) { 1045202181Sthompsa snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p); 1046202181Sthompsa len = 127; 1047202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1048202181Sthompsa#ifdef DEBUG 1049202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1050202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1051202181Sthompsa#endif 1052202181Sthompsa if (error == 0) { 1053202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1054202181Sthompsa list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1055202181Sthompsa sprintf(list[list_size], TTY_NAME, data); 1056202181Sthompsa list_size++; 1057202181Sthompsa } 1058202181Sthompsa } 1059202181Sthompsa 1060202181Sthompsa /* 1061202181Sthompsa * We can return directly if we found multiplexed serial ports because 1062202181Sthompsa * devices with these ports only have additional diagnostic ports (useless) 1063202181Sthompsa * and modem ports (for used with pppd). 1064202181Sthompsa */ 1065202181Sthompsa if (list_size > 0) { 1066202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1067202181Sthompsa list[list_size] = NULL; 1068202181Sthompsa return list; 1069202181Sthompsa } 1070202181Sthompsa 1071202181Sthompsa /* 1072202181Sthompsa * The network port is on a high numbered interface so we walk backwards until 1073202181Sthompsa * we hit anything other than application/control. 1074202181Sthompsa */ 1075202181Sthompsa 1076202181Sthompsa for (--i; i >= 0; i--) { 1077202181Sthompsa /* Basic test to check if we're even in the right ballpark */ 1078202181Sthompsa snprintf(buf, 64, SYSCTL_TEST, i); 1079202181Sthompsa len = 127; 1080202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1081202181Sthompsa#ifdef DEBUG 1082202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1083202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1084202181Sthompsa#endif 1085202181Sthompsa if (error < 0) 1086202181Sthompsa break; 1087202181Sthompsa if (strcasecmp(data, "uhso") != 0) 1088202181Sthompsa break; 1089202181Sthompsa 1090202181Sthompsa /* Test for useable ports */ 1091202181Sthompsa for (p = port_type_list; *p != NULL; p++) { 1092202181Sthompsa snprintf(buf, 64, SYSCTL_NAME_TTY, i, p); 1093202181Sthompsa len = 127; 1094202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1095202181Sthompsa if (error == 0) { 1096202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1097202181Sthompsa list[list_size] = malloc(strlen(data) + strlen(TTY_NAME)); 1098202181Sthompsa sprintf(list[list_size], TTY_NAME, data); 1099202181Sthompsa list_size++; 1100202181Sthompsa } 1101202181Sthompsa } 1102202181Sthompsa 1103202181Sthompsa /* HACK! first port is a diagnostic port, we abort here */ 1104202181Sthompsa snprintf(buf, 64, SYSCTL_NAME_TTY, i, "diagnostic"); 1105202181Sthompsa len = 127; 1106202181Sthompsa error = sysctlbyname(buf, data, &len, NULL, 0); 1107202181Sthompsa#ifdef DEBUG 1108202181Sthompsa fprintf(stderr, "sysctl %s returned(%d): %s\n", 1109202181Sthompsa buf, error, error == 0 ? data : "FAILED"); 1110202181Sthompsa#endif 1111202181Sthompsa if (error == 0) 1112202181Sthompsa break; 1113202181Sthompsa } 1114202181Sthompsa 1115202181Sthompsa list = realloc(list, (list_size + 1) * sizeof(char *)); 1116202181Sthompsa list[list_size] = NULL; 1117202181Sthompsa return list; 1118202181Sthompsa} 1119202181Sthompsa 1120202181Sthompsastatic int 1121202181Sthompsado_connect(struct ctx *ctx, const char *tty) 1122202181Sthompsa{ 1123202181Sthompsa int i, error, needcfg; 1124202181Sthompsa resp_arg ra; 1125202181Sthompsa struct termios t; 1126202181Sthompsa char **buf; 1127202181Sthompsa 1128202181Sthompsa#ifdef DEBUG 1129202181Sthompsa fprintf(stderr, "Attempting to open %s\n", tty); 1130202181Sthompsa#endif 1131202181Sthompsa 1132202181Sthompsa ctx->fd = open(tty, O_RDWR); 1133202181Sthompsa if (ctx->fd < 0) { 1134202181Sthompsa#ifdef DEBUG 1135202181Sthompsa fprintf(stderr, "Failed to open %s\n", tty); 1136202181Sthompsa#endif 1137202181Sthompsa return (-1); 1138202181Sthompsa } 1139202181Sthompsa 1140202181Sthompsa tcgetattr(ctx->fd, &t); 1141202181Sthompsa t.c_oflag = 0; 1142202181Sthompsa t.c_iflag = 0; 1143202181Sthompsa t.c_cflag = CLOCAL | CREAD; 1144202181Sthompsa t.c_lflag = 0; 1145202181Sthompsa tcsetattr(ctx->fd, TCSAFLUSH, &t); 1146202181Sthompsa 1147202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n"); 1148202181Sthompsa if (error == -2) { 1149202181Sthompsa warnx("failed to read from device"); 1150202181Sthompsa return (-1); 1151202181Sthompsa } 1152202181Sthompsa 1153202181Sthompsa /* Check for PIN */ 1154202181Sthompsa error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n"); 1155202181Sthompsa if (error != 0) { 1156202181Sthompsa if (ctx->pin == NULL) { 1157202181Sthompsa errx(1, "device requires PIN"); 1158202181Sthompsa } 1159202181Sthompsa 1160202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n", 1161202181Sthompsa ctx->pin); 1162202181Sthompsa if (error != 0) { 1163202181Sthompsa errx(1, "wrong PIN"); 1164202181Sthompsa } 1165202181Sthompsa } 1166202181Sthompsa 1167202181Sthompsa /* 1168202181Sthompsa * Check if a PDP context has been configured and configure one 1169202181Sthompsa * if needed. 1170202181Sthompsa */ 1171202181Sthompsa ra.val[0].ptr = NULL; 1172202181Sthompsa ra.val[1].int32 = 0; 1173202181Sthompsa error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n"); 1174202181Sthompsa buf = ra.val[0].ptr; 1175202181Sthompsa needcfg = 1; 1176202181Sthompsa for (i = 0; i < ra.val[1].int32; i++) { 1177202181Sthompsa char apn[256]; 1178202181Sthompsa int cid; 1179202181Sthompsa error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"", 1180202181Sthompsa &cid, apn); 1181202181Sthompsa if (error != 2) { 1182202181Sthompsa free(buf[i]); 1183202181Sthompsa continue; 1184202181Sthompsa } 1185202181Sthompsa 1186202181Sthompsa if (cid == ctx->pdp_ctx) { 1187202181Sthompsa ctx->con_apn = strdup(apn); 1188202181Sthompsa if (ctx->pdp_apn != NULL) { 1189202181Sthompsa if (strcmp(apn, ctx->pdp_apn) == 0) 1190202181Sthompsa needcfg = 0; 1191202181Sthompsa } 1192202181Sthompsa else { 1193202181Sthompsa needcfg = 0; 1194202181Sthompsa } 1195202181Sthompsa } 1196202181Sthompsa free(buf[i]); 1197202181Sthompsa } 1198202181Sthompsa free(buf); 1199202181Sthompsa 1200202181Sthompsa if (needcfg) { 1201202181Sthompsa if (ctx->pdp_apn == NULL) 1202202181Sthompsa errx(1, "device is not configured and no APN given"); 1203202181Sthompsa 1204202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, 1205202181Sthompsa "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn); 1206202181Sthompsa if (error != 0) { 1207202181Sthompsa errx(1, "failed to configure device"); 1208202181Sthompsa } 1209202181Sthompsa ctx->con_apn = strdup(ctx->pdp_apn); 1210202181Sthompsa } 1211202181Sthompsa 1212202181Sthompsa if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) { 1213202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, 1214202181Sthompsa "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx, 1215202181Sthompsa (ctx->pdp_user != NULL) ? ctx->pdp_user : "", 1216202181Sthompsa (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : ""); 1217202181Sthompsa } 1218202181Sthompsa 1219202181Sthompsa error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1220202181Sthompsa ctx->pdp_ctx); 1221202181Sthompsa if (error != 0) 1222202181Sthompsa return (-1); 1223202181Sthompsa 1224202181Sthompsa at_cmd_async(ctx->fd, "AT+CGREG?\r\n"); 1225202181Sthompsa at_cmd_async(ctx->fd, "AT+CREG?\r\n"); 1226202181Sthompsa 1227202181Sthompsa tmr_add(&timers, 1, 5, tmr_status, ctx); 1228202181Sthompsa return (0); 1229202181Sthompsa} 1230202181Sthompsa 1231202181Sthompsastatic void 1232202181Sthompsado_disconnect(struct ctx *ctx) 1233202181Sthompsa{ 1234202181Sthompsa struct sockaddr_in sin, mask; 1235202181Sthompsa 1236202181Sthompsa /* Disconnect */ 1237202181Sthompsa at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n", 1238202181Sthompsa ctx->pdp_ctx); 1239202181Sthompsa close(ctx->fd); 1240202181Sthompsa 1241202181Sthompsa /* Remove ip-address from interface */ 1242202181Sthompsa if (ctx->flags & IPASSIGNED) { 1243202181Sthompsa sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in); 1244202181Sthompsa memset(&mask.sin_addr.s_addr, 0xff, 1245202181Sthompsa sizeof(mask.sin_addr.s_addr)); 1246202181Sthompsa sin.sin_family = mask.sin_family = AF_INET; 1247202181Sthompsa memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr, 1248202181Sthompsa sizeof(sin.sin_addr.s_addr)); 1249202181Sthompsa ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin, 1250202181Sthompsa (struct sockaddr *)&mask); 1251202181Sthompsa 1252202181Sthompsa if_ifdown(ctx->ifnam); 1253202181Sthompsa ctx->flags &= ~IPASSIGNED; 1254202181Sthompsa } 1255202181Sthompsa 1256202181Sthompsa /* Attempt to reset resolv.conf */ 1257202181Sthompsa set_nameservers(ctx, ctx->resolv_path, 0); 1258202181Sthompsa} 1259202181Sthompsa 1260202181Sthompsastatic void 1261202181Sthompsadaemonize(struct ctx *ctx) 1262202181Sthompsa{ 1263202181Sthompsa struct pidfh *pfh; 1264202181Sthompsa pid_t opid; 1265202181Sthompsa 1266202181Sthompsa snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam); 1267202181Sthompsa 1268202181Sthompsa pfh = pidfile_open(ctx->pidfile, 0600, &opid); 1269202181Sthompsa if (pfh == NULL) { 1270202181Sthompsa warn("Cannot create pidfile %s", ctx->pidfile); 1271202181Sthompsa return; 1272202181Sthompsa } 1273202181Sthompsa 1274202181Sthompsa if (daemon(0, 0) == -1) { 1275202181Sthompsa warn("Cannot daemonize"); 1276202181Sthompsa pidfile_remove(pfh); 1277202181Sthompsa return; 1278202181Sthompsa } 1279202181Sthompsa 1280202181Sthompsa pidfile_write(pfh); 1281202181Sthompsa ctx->pfh = pfh; 1282202181Sthompsa ctx->flags |= FLG_DAEMON; 1283202181Sthompsa 1284202181Sthompsa snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam); 1285202181Sthompsa openlog(syslog_title, LOG_PID, LOG_USER); 1286202181Sthompsa syslog_open = 1; 1287202181Sthompsa} 1288202181Sthompsa 1289202181Sthompsastatic void 1290202181Sthompsasend_disconnect(const char *ifnam) 1291202181Sthompsa{ 1292202181Sthompsa char pidfile[128]; 1293202181Sthompsa FILE *fp; 1294202181Sthompsa pid_t pid; 1295202181Sthompsa int n; 1296202181Sthompsa 1297202181Sthompsa snprintf(pidfile, 127, PIDFILE, ifnam); 1298202181Sthompsa fp = fopen(pidfile, "r"); 1299202181Sthompsa if (fp == NULL) { 1300202181Sthompsa warn("Cannot open %s", pidfile); 1301202181Sthompsa return; 1302202181Sthompsa } 1303202181Sthompsa 1304202181Sthompsa n = fscanf(fp, "%d", &pid); 1305202181Sthompsa fclose(fp); 1306202181Sthompsa if (n != 1) { 1307202181Sthompsa warnx("unable to read daemon pid"); 1308202181Sthompsa return; 1309202181Sthompsa } 1310202181Sthompsa#ifdef DEBUG 1311202181Sthompsa fprintf(stderr, "Sending SIGTERM to %d\n", pid); 1312202181Sthompsa#endif 1313202181Sthompsa kill(pid, SIGTERM); 1314202181Sthompsa} 1315202181Sthompsa 1316202181Sthompsastatic void 1317202181Sthompsausage(const char *exec) 1318202181Sthompsa{ 1319202181Sthompsa 1320202181Sthompsa printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] " 1321202181Sthompsa "[-k password] [-r resolvpath] [-f tty] interface\n", exec); 1322202181Sthompsa printf("usage %s -d interface\n", exec); 1323202181Sthompsa} 1324202181Sthompsa 1325202181Sthompsaenum { 1326202181Sthompsa MODE_CONN, 1327202181Sthompsa MODE_DISC 1328202181Sthompsa}; 1329202181Sthompsa 1330202181Sthompsaint 1331202181Sthompsamain(int argc, char *argv[]) 1332202181Sthompsa{ 1333202181Sthompsa int ch, error, mode; 1334202181Sthompsa const char *ifnam = NULL; 1335202181Sthompsa char *tty = NULL; 1336202181Sthompsa char **p, **tty_list; 1337202181Sthompsa fd_set set; 1338202181Sthompsa struct ctx ctx; 1339202181Sthompsa struct itimerval it; 1340202181Sthompsa 1341202181Sthompsa TAILQ_INIT(&timers.head); 1342202181Sthompsa timers.res = 1; 1343202181Sthompsa 1344202181Sthompsa ctx.pdp_ctx = 1; 1345202181Sthompsa ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL; 1346202181Sthompsa ctx.pin = NULL; 1347202181Sthompsa 1348202181Sthompsa ctx.con_status = 0; 1349202181Sthompsa ctx.con_apn = NULL; 1350202181Sthompsa ctx.con_oper = NULL; 1351202181Sthompsa ctx.con_net_stat = 0; 1352202181Sthompsa ctx.con_net_type = -1; 1353202181Sthompsa ctx.flags = 0; 1354202181Sthompsa ctx.resolv_path = RESOLV_PATH; 1355202181Sthompsa ctx.resolv = NULL; 1356202181Sthompsa ctx.ns = NULL; 1357202181Sthompsa ctx.dbm = 0; 1358202181Sthompsa 1359202181Sthompsa mode = MODE_CONN; 1360202181Sthompsa ctx.flags |= FLG_DELAYED; 1361202181Sthompsa 1362202181Sthompsa while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) { 1363202181Sthompsa switch (ch) { 1364202181Sthompsa case 'a': 1365202181Sthompsa ctx.pdp_apn = argv[optind - 1]; 1366202181Sthompsa break; 1367202181Sthompsa case 'c': 1368202181Sthompsa ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10); 1369202181Sthompsa if (ctx.pdp_ctx < 1) { 1370202181Sthompsa warnx("Invalid context ID, defaulting to 1"); 1371202181Sthompsa ctx.pdp_ctx = 1; 1372202181Sthompsa } 1373202181Sthompsa break; 1374202181Sthompsa case 'p': 1375202181Sthompsa ctx.pin = argv[optind - 1]; 1376202181Sthompsa break; 1377202181Sthompsa case 'u': 1378202181Sthompsa ctx.pdp_user = argv[optind - 1]; 1379202181Sthompsa break; 1380202181Sthompsa case 'k': 1381202181Sthompsa ctx.pdp_pwd = argv[optind - 1]; 1382202181Sthompsa break; 1383202181Sthompsa case 'r': 1384202181Sthompsa ctx.resolv_path = argv[optind - 1]; 1385202181Sthompsa break; 1386202181Sthompsa case 'd': 1387202181Sthompsa mode = MODE_DISC; 1388202181Sthompsa break; 1389202181Sthompsa case 'b': 1390202181Sthompsa ctx.flags &= ~FLG_DELAYED; 1391202181Sthompsa break; 1392202181Sthompsa case 'n': 1393202181Sthompsa ctx.flags |= FLG_NODAEMON; 1394202181Sthompsa break; 1395202181Sthompsa case 'f': 1396202181Sthompsa tty = argv[optind - 1]; 1397202181Sthompsa break; 1398202181Sthompsa case 'h': 1399202181Sthompsa case '?': 1400202181Sthompsa default: 1401202181Sthompsa usage(argv[0]); 1402202181Sthompsa exit(EXIT_SUCCESS); 1403202181Sthompsa } 1404202181Sthompsa } 1405202181Sthompsa 1406202181Sthompsa argc -= optind; 1407202181Sthompsa argv += optind; 1408202181Sthompsa 1409202181Sthompsa if (argc < 1) 1410202181Sthompsa errx(1, "no interface given"); 1411202181Sthompsa 1412202181Sthompsa ifnam = argv[argc - 1]; 1413202181Sthompsa ctx.ifnam = strdup(ifnam); 1414202181Sthompsa 1415202181Sthompsa switch (mode) { 1416202181Sthompsa case MODE_DISC: 1417202181Sthompsa printf("Disconnecting %s\n", ifnam); 1418202181Sthompsa send_disconnect(ifnam); 1419202181Sthompsa exit(EXIT_SUCCESS); 1420202181Sthompsa default: 1421202181Sthompsa break; 1422202181Sthompsa } 1423202181Sthompsa 1424202181Sthompsa signal(SIGHUP, sig_handle); 1425202181Sthompsa signal(SIGINT, sig_handle); 1426202181Sthompsa signal(SIGQUIT, sig_handle); 1427202181Sthompsa signal(SIGTERM, sig_handle); 1428202181Sthompsa signal(SIGALRM, sig_handle); 1429202181Sthompsa 1430202181Sthompsa it.it_interval.tv_sec = 1; 1431202181Sthompsa it.it_interval.tv_usec = 0; 1432202181Sthompsa it.it_value.tv_sec = 1; 1433202181Sthompsa it.it_value.tv_usec = 0; 1434202181Sthompsa error = setitimer(ITIMER_REAL, &it, NULL); 1435202181Sthompsa if (error != 0) 1436202181Sthompsa errx(1, "setitimer"); 1437202181Sthompsa 1438202181Sthompsa tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx); 1439202181Sthompsa watchdog_reset(&ctx, 15); 1440202181Sthompsa 1441202181Sthompsa if (tty != NULL) { 1442202181Sthompsa error = do_connect(&ctx, tty); 1443202181Sthompsa if (error != 0) 1444202181Sthompsa errx(1, "Failed to open %s", tty); 1445202181Sthompsa } 1446202181Sthompsa else { 1447202181Sthompsa tty_list = get_tty(&ctx); 1448202181Sthompsa#ifdef DEBUG 1449202181Sthompsa if (tty_list == NULL) { 1450202181Sthompsa fprintf(stderr, "get_tty returned empty list\n"); 1451202181Sthompsa } else { 1452202181Sthompsa fprintf(stderr, "tty list:\n"); 1453202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1454202181Sthompsa fprintf(stderr, "\t %s\n", *p); 1455202181Sthompsa } 1456202181Sthompsa } 1457202181Sthompsa#endif 1458202181Sthompsa for (p = tty_list; *p != NULL; p++) { 1459202181Sthompsa error = do_connect(&ctx, *p); 1460202181Sthompsa if (error == 0) { 1461202181Sthompsa tty = *p; 1462202181Sthompsa break; 1463202181Sthompsa } 1464202181Sthompsa } 1465202181Sthompsa if (*p == NULL) 1466202181Sthompsa errx(1, "Failed to obtain a control port, " 1467202181Sthompsa "try specifying one manually"); 1468202181Sthompsa } 1469202181Sthompsa 1470202181Sthompsa if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON)) 1471202181Sthompsa daemonize(&ctx); 1472202181Sthompsa 1473202181Sthompsa 1474202181Sthompsa FD_ZERO(&set); 1475202181Sthompsa FD_SET(ctx.fd, &set); 1476202181Sthompsa for (;;) { 1477202181Sthompsa 1478202181Sthompsa watchdog_disable(&ctx); 1479202181Sthompsa error = select(ctx.fd + 1, &set, NULL, NULL, NULL); 1480202181Sthompsa if (error <= 0) { 1481202181Sthompsa if (running && errno == EINTR) 1482202181Sthompsa continue; 1483202181Sthompsa if (ctx.flags & FLG_WDEXP) { 1484202181Sthompsa ctx.flags &= ~FLG_WDEXP; 1485202181Sthompsa watchdog_reset(&ctx, 5); 1486202181Sthompsa do_disconnect(&ctx); 1487202181Sthompsa watchdog_reset(&ctx, 15); 1488202181Sthompsa do_connect(&ctx, tty); 1489202181Sthompsa running = 1; 1490202181Sthompsa continue; 1491202181Sthompsa } 1492202181Sthompsa 1493202181Sthompsa break; 1494202181Sthompsa } 1495202181Sthompsa 1496202181Sthompsa if (FD_ISSET(ctx.fd, &set)) { 1497202181Sthompsa watchdog_reset(&ctx, 15); 1498202181Sthompsa error = at_async(&ctx, &ctx); 1499202181Sthompsa if (error != 0) 1500202181Sthompsa break; 1501202181Sthompsa } 1502202181Sthompsa FD_SET(ctx.fd, &set); 1503202181Sthompsa 1504202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) { 1505202181Sthompsa printf("Status: %s (%s)", 1506202181Sthompsa ctx.con_status ? "connected" : "disconnected", 1507202181Sthompsa network_access_type[ctx.con_net_type]); 1508202181Sthompsa if (ctx.dbm < 0) 1509202181Sthompsa printf(", signal: %d dBm", ctx.dbm); 1510202181Sthompsa printf("\r"); 1511202181Sthompsa fflush(stdout); 1512202181Sthompsa } 1513202181Sthompsa } 1514202181Sthompsa if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) 1515202181Sthompsa printf("\n"); 1516202181Sthompsa 1517202181Sthompsa signal(SIGHUP, SIG_DFL); 1518202181Sthompsa signal(SIGINT, SIG_DFL); 1519202181Sthompsa signal(SIGQUIT, SIG_DFL); 1520202181Sthompsa signal(SIGTERM, SIG_DFL); 1521202181Sthompsa signal(SIGALRM, SIG_IGN); 1522202181Sthompsa 1523202181Sthompsa do_disconnect(&ctx); 1524202181Sthompsa 1525202181Sthompsa if (ctx.flags & FLG_DAEMON) { 1526202181Sthompsa pidfile_remove(ctx.pfh); 1527202181Sthompsa if (syslog_open) 1528202181Sthompsa closelog(); 1529202181Sthompsa } 1530202181Sthompsa 1531202181Sthompsa return (0); 1532202181Sthompsa} 1533