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", &reg);
727202181Sthompsa	if (n != 1) {
728202181Sthompsa		n = sscanf(resp, "+CREG: %d", &reg);
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", &reg);
754202181Sthompsa	if (n != 1) {
755202181Sthompsa		n = sscanf(resp, "+CGREG: %d", &reg);
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