1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2008-2009 Fredrik Lindberg
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * $FreeBSD$
28 */
29
30#include <sys/types.h>
31#include <sys/param.h>
32#include <sys/socket.h>
33#include <sys/sockio.h>
34#include <sys/select.h>
35#include <sys/stat.h>
36#include <sys/sysctl.h>
37#include <sys/time.h>
38#include <sys/queue.h>
39
40#include <arpa/inet.h>
41#include <net/if.h>
42#include <net/if_dl.h>
43#include <net/route.h>
44#include <netinet/in.h>
45#include <netinet/in_var.h>
46
47#include <err.h>
48#include <errno.h>
49#include <fcntl.h>
50#include <termios.h>
51#include <stdarg.h>
52#include <stdio.h>
53#include <stdlib.h>
54#include <stdint.h>
55#include <string.h>
56#include <signal.h>
57#include <syslog.h>
58#include <unistd.h>
59#include <ifaddrs.h>
60#include <libutil.h>
61#include <time.h>
62
63/*
64 * Connection utility to ease connectivity using the raw IP packet interface
65 * available on uhso(4) devices.
66 */
67
68#define TTY_NAME	"/dev/%s"
69#define SYSCTL_TEST	"dev.uhso.%d.%%driver"
70#define SYSCTL_LOCATION "dev.uhso.%d.%%location"
71#define SYSCTL_PORTS	"dev.uhso.%d.ports"
72#define SYSCTL_NETIF	"dev.uhso.%d.netif"
73#define SYSCTL_NAME_TTY	"dev.uhso.%d.port.%s.tty"
74#define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc"
75#define RESOLV_PATH	"/etc/resolv.conf"
76#define PIDFILE		"/var/run/uhsoctl.%s.pid"
77
78static const char *network_access_type[] = {
79	"GSM",
80	"Compact GSM",
81	"UMTS",
82	"GSM (EGPRS)",
83	"HSDPA",
84	"HSUPA",
85	"HSDPA/HSUPA"
86};
87
88static const char *network_reg_status[] = {
89	"Not registered",
90	"Registered",
91	"Searching for network",
92	"Network registration denied",
93	"Unknown",
94	"Registered (roaming)"
95};
96
97struct ctx {
98	int fd;
99	int flags;
100#define IPASSIGNED	0x01
101#define FLG_NODAEMON	0x02 /* Don't detach from terminal */
102#define FLG_DAEMON	0x04 /* Running as daemon */
103#define FLG_DELAYED	0x08 /* Fork into background after connect */
104#define FLG_NEWDATA	0x10
105#define FLG_WATCHDOG	0x20 /* Watchdog enabled */
106#define FLG_WDEXP	0x40 /* Watchdog expired */
107	const char *ifnam;
108	const char *pin; /* device PIN */
109
110	char pidfile[128];
111	struct pidfh *pfh;
112
113	time_t watchdog;
114
115	/* PDP context settings */
116	int pdp_ctx;
117	const char *pdp_apn;
118	const char *pdp_user;
119	const char *pdp_pwd;
120
121	/* Connection status */
122	int con_status;		/* Connected? */
123	char *con_apn;		/* Connected APN */
124	char *con_oper;		/* Operator name */
125	int con_net_stat;	/* Network connection status */
126	int con_net_type;	/* Network connection type */
127
128	/* Misc. status */
129	int dbm;
130
131	/* IP and nameserver settings */
132	struct in_addr ip;
133	char **ns;
134	const char *resolv_path;
135	char *resolv;		/* Old resolv.conf */
136	size_t resolv_sz;
137};
138
139static int readline_buf(const char *, const char *, char *, size_t);
140static int readline(int, char *, size_t);
141static void daemonize(struct ctx *);
142
143static int at_cmd_async(int, const char *, ...);
144
145typedef union {
146	void *ptr;
147	uint32_t int32;
148} resp_data;
149typedef struct {
150	resp_data val[2];
151} resp_arg;
152typedef void (*resp_cb)(resp_arg *, const char *, const char *);
153
154typedef void (*async_cb)(void *, const char *);
155struct async_handle {
156	const char *cmd;
157	async_cb func;
158};
159
160static void at_async_creg(void *, const char *);
161static void at_async_cgreg(void *, const char *);
162static void at_async_cops(void *, const char *);
163static void at_async_owancall(void *, const char *);
164static void at_async_owandata(void *, const char *);
165static void at_async_csq(void *, const char *);
166
167static struct async_handle async_cmd[] = {
168	{ "+CREG", at_async_creg },
169	{ "+CGREG", at_async_cgreg },
170	{ "+COPS", at_async_cops },
171	{ "+CSQ", at_async_csq },
172	{ "_OWANCALL", at_async_owancall },
173	{ "_OWANDATA", at_async_owandata },
174	{ NULL, NULL }
175};
176
177struct timer_entry;
178struct timers {
179	TAILQ_HEAD(, timer_entry) head;
180	int res;
181};
182
183typedef void (*tmr_cb)(int, void *);
184struct timer_entry {
185	TAILQ_ENTRY(timer_entry) next;
186	int id;
187	int timeout;
188	tmr_cb func;
189	void *arg;
190};
191
192
193static struct timers timers;
194static volatile int running = 1;
195static int syslog_open = 0;
196static char syslog_title[64];
197
198/* Periodic timer, runs ready timer tasks every tick */
199static void
200tmr_run(struct timers *tmrs)
201{
202	struct timer_entry *te, *te2;
203
204	te = TAILQ_FIRST(&tmrs->head);
205	if (te == NULL)
206		return;
207
208	te->timeout -= tmrs->res;
209	while (te->timeout <= 0) {
210		te2 = TAILQ_NEXT(te, next);
211		TAILQ_REMOVE(&tmrs->head, te, next);
212		te->func(te->id, te->arg);
213		free(te);
214		te = te2;
215		if (te == NULL)
216			break;
217	}
218}
219
220/* Add a new timer */
221static void
222tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg)
223{
224	struct timer_entry *te, *te2, *te3;
225
226	te = malloc(sizeof(struct timer_entry));
227	memset(te, 0, sizeof(struct timer_entry));
228
229	te->timeout = timeout;
230	te->func = func;
231	te->arg = arg;
232	te->id = id;
233
234	te2 = TAILQ_FIRST(&tmrs->head);
235
236	if (TAILQ_EMPTY(&tmrs->head)) {
237		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
238	} else if (te->timeout < te2->timeout) {
239		te2->timeout -= te->timeout;
240		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
241	} else {
242		while (te->timeout >= te2->timeout) {
243			te->timeout -= te2->timeout;
244			te3 = TAILQ_NEXT(te2, next);
245			if (te3 == NULL || te3->timeout > te->timeout)
246				break;
247			te2 = te3;
248		}
249		TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next);
250	}
251}
252
253#define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG
254#define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG
255
256static void
257watchdog_reset(struct ctx *ctx, int timeout)
258{
259	struct timespec tp;
260
261	clock_gettime(CLOCK_MONOTONIC, &tp),
262	ctx->watchdog = tp.tv_sec + timeout;
263
264	watchdog_enable(ctx);
265}
266
267static void
268tmr_creg(int id, void *arg)
269{
270	struct ctx *ctx = arg;
271
272	at_cmd_async(ctx->fd, "AT+CREG?\r\n");
273	watchdog_reset(ctx, 10);
274}
275
276static void
277tmr_cgreg(int id, void *arg)
278{
279	struct ctx *ctx = arg;
280
281	at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
282	watchdog_reset(ctx, 10);
283}
284
285static void
286tmr_status(int id, void *arg)
287{
288	struct ctx *ctx = arg;
289
290	at_cmd_async(ctx->fd, "AT+CSQ\r\n");
291	watchdog_reset(ctx, 10);
292}
293
294static void
295tmr_watchdog(int id, void *arg)
296{
297	struct ctx *ctx = arg;
298	pid_t self;
299	struct timespec tp;
300
301	tmr_add(&timers, 1, 5, tmr_watchdog, ctx);
302
303	if (!(ctx->flags & FLG_WATCHDOG))
304		return;
305
306	clock_gettime(CLOCK_MONOTONIC, &tp);
307
308	if (tp.tv_sec >= ctx->watchdog) {
309#ifdef DEBUG
310		fprintf(stderr, "Watchdog expired\n");
311#endif
312		ctx->flags |= FLG_WDEXP;
313		self = getpid();
314		kill(self, SIGHUP);
315	}
316}
317
318static void
319sig_handle(int sig)
320{
321
322	switch (sig) {
323	case SIGHUP:
324	case SIGINT:
325	case SIGQUIT:
326	case SIGTERM:
327		running = 0;
328		break;
329	case SIGALRM:
330		tmr_run(&timers);
331		break;
332	}
333}
334
335static void
336logger(int pri, const char *fmt, ...)
337{
338	char *buf;
339	va_list ap;
340
341	va_start(ap, fmt);
342	vasprintf(&buf, fmt, ap);
343	if (syslog_open)
344		syslog(pri, "%s", buf);
345	else {
346		switch (pri) {
347		case LOG_INFO:
348		case LOG_NOTICE:
349			printf("%s\n", buf);
350			break;
351		default:
352			fprintf(stderr, "%s: %s\n", getprogname(), buf);
353			break;
354		}
355	}
356
357	free(buf);
358	va_end(ap);
359}
360
361/* Add/remove IP address from an interface */
362static int
363ifaddr_ad(unsigned long d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
364{
365	struct ifaliasreq req;
366	int fd, error;
367
368	fd = socket(AF_INET, SOCK_DGRAM, 0);
369	if (fd < 0)
370		return (-1);
371
372	memset(&req, 0, sizeof(struct ifaliasreq));
373	strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name));
374	memcpy(&req.ifra_addr, sa, sa->sa_len);
375	memcpy(&req.ifra_mask, mask, mask->sa_len);
376
377	error = ioctl(fd, d, (char *)&req);
378	close(fd);
379	return (error);
380}
381
382#define if_ifup(ifnam) if_setflags(ifnam, IFF_UP)
383#define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP)
384
385static int
386if_setflags(const char *ifnam, int flags)
387{
388	struct ifreq ifr;
389	int fd, error;
390	unsigned int oflags = 0;
391
392	memset(&ifr, 0, sizeof(struct ifreq));
393	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
394
395	fd = socket(AF_INET, SOCK_DGRAM, 0);
396	if (fd < 0)
397		return (-1);
398
399	error = ioctl(fd, SIOCGIFFLAGS, &ifr);
400	if (error == 0) {
401		oflags = (ifr.ifr_flags & 0xffff)  | (ifr.ifr_flagshigh << 16);
402	}
403
404	if (flags < 0)
405		oflags &= ~(-flags);
406	else
407		oflags |= flags;
408
409	ifr.ifr_flags = oflags & 0xffff;
410	ifr.ifr_flagshigh = oflags >> 16;
411
412	error = ioctl(fd, SIOCSIFFLAGS, &ifr);
413	if (error != 0)
414		warn("ioctl SIOCSIFFLAGS");
415
416	close(fd);
417	return (error);
418}
419
420static int
421ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
422{
423	int error;
424
425	error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask);
426	if (error != 0)
427		warn("ioctl SIOCAIFADDR");
428	return (error);
429}
430
431static int
432ifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
433{
434	int error;
435
436	error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask);
437	if (error != 0)
438		warn("ioctl SIOCDIFADDR");
439	return (error);
440}
441
442static int
443set_nameservers(struct ctx *ctx, const char *respath, int ns, ...)
444{
445	int i, n, fd;
446	FILE *fp;
447	char *p;
448	va_list ap;
449	struct stat sb;
450	char buf[512];
451
452	if (ctx->ns != NULL) {
453		for (i = 0; ctx->ns[i] != NULL; i++) {
454			free(ctx->ns[i]);
455		}
456		free(ctx->ns);
457		ctx->ns = NULL;
458	}
459
460	fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666);
461	if (fd < 0)
462		return (-1);
463
464	if (ns == 0) {
465		/* Attempt to restore old resolv.conf */
466		if (ctx->resolv != NULL) {
467			ftruncate(fd, 0);
468			lseek(fd, 0, SEEK_SET);
469			write(fd, ctx->resolv, ctx->resolv_sz);
470			free(ctx->resolv);
471			ctx->resolv = NULL;
472			ctx->resolv_sz = 0;
473		}
474		close(fd);
475		return (0);
476	}
477
478
479	ctx->ns = malloc(sizeof(char *) * (ns + 1));
480	if (ctx->ns == NULL) {
481		close(fd);
482		return (-1);
483	}
484
485	va_start(ap, ns);
486	for (i = 0; i < ns; i++) {
487		p = va_arg(ap, char *);
488		ctx->ns[i] = strdup(p);
489	}
490	ctx->ns[i] = NULL;
491	va_end(ap);
492
493	/* Attempt to backup the old resolv.conf */
494	if (ctx->resolv == NULL) {
495		i = fstat(fd, &sb);
496		if (i == 0 && sb.st_size != 0) {
497			ctx->resolv_sz = sb.st_size;
498			ctx->resolv = malloc(sb.st_size);
499			if (ctx->resolv != NULL) {
500				n = read(fd, ctx->resolv, sb.st_size);
501				if (n != sb.st_size) {
502					free(ctx->resolv);
503					ctx->resolv = NULL;
504				}
505			}
506		}
507	}
508
509
510	ftruncate(fd, 0);
511	lseek(fd, 0, SEEK_SET);
512	fp = fdopen(fd, "w");
513
514	/*
515	 * Write back everything other than nameserver entries to the
516	 * new resolv.conf
517	 */
518	if (ctx->resolv != NULL) {
519		p = ctx->resolv;
520		while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf,
521		    sizeof(buf))) > 0) {
522			p += i;
523			if (strncasecmp(buf, "nameserver", 10) == 0)
524				continue;
525			fprintf(fp, "%s", buf);
526		}
527	}
528
529	for (i = 0; ctx->ns[i] != NULL; i++) {
530		fprintf(fp, "nameserver %s\n", ctx->ns[i]);
531	}
532	fclose(fp);
533	return (0);
534}
535
536/* Read a \n-terminated line from buffer */
537static int
538readline_buf(const char *s, const char *e, char *buf, size_t bufsz)
539{
540	int pos = 0;
541	char *p = buf;
542
543	for (; s < e; s++) {
544		*p = *s;
545		pos++;
546		if (pos >= (bufsz - 1))
547			break;
548		if (*p++ == '\n')
549			break;
550	}
551	*p = '\0';
552	return (pos);
553}
554
555/* Read a \n-terminated line from file */
556static int
557readline(int fd, char *buf, size_t bufsz)
558{
559	int n = 0, pos = 0;
560	char *p = buf;
561
562	for (;;) {
563		n = read(fd, p, 1);
564		if (n <= 0)
565			break;
566		pos++;
567		if (pos >= (bufsz - 1))
568			break;
569		if (*p++ == '\n')
570			break;
571	}
572	*p = '\0';
573	return (n <= 0 ? n : pos);
574}
575
576/*
577 * Synchronous AT command
578 */
579static int
580at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...)
581{
582	char buf[512];
583	char cmd[64];
584	size_t l;
585	int n, error, retval = 0;
586	va_list ap;
587	fd_set set;
588	char *p;
589
590	va_start(ap, cf);
591	vsnprintf(cmd, sizeof(cmd), cf, ap);
592	va_end(ap);
593
594#ifdef DEBUG
595	fprintf(stderr, "SYNC_CMD: %s", cmd);
596#endif
597
598	l = strlen(cmd);
599	n = write(ctx->fd, cmd, l);
600	if (n <= 0)
601		return (-1);
602
603	if (resp != NULL) {
604		l = strlen(resp);
605#ifdef DEBUG
606		fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l);
607#endif
608	}
609
610	for (;;) {
611		bzero(buf, sizeof(buf));
612
613		FD_ZERO(&set);
614		watchdog_reset(ctx, 5);
615		do {
616			FD_SET(ctx->fd, &set);
617			error = select(ctx->fd + 1, &set, NULL, NULL, NULL);
618			if (ctx->flags & FLG_WDEXP) {
619				watchdog_disable(ctx);
620				return (-2);
621			}
622		} while (error <= 0 && errno == EINTR);
623		watchdog_disable(ctx);
624
625		if (error <= 0) {
626			retval = -2;
627			break;
628		}
629
630		n = readline(ctx->fd, buf, sizeof(buf));
631		if (n <= 0) {
632			retval = -2;
633			break;
634		}
635
636		if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0)
637			continue;
638
639		if ((p = strchr(buf, '\r')) != NULL)
640			*p = '\0';
641		else if ((p = strchr(buf, '\n')) != NULL)
642			*p = '\0';
643#ifdef DEBUG
644		fprintf(stderr, "SYNC_RESP: %s\n", buf);
645#endif
646
647		/* Skip local echo */
648		if (strncasecmp(cmd, buf, strlen(buf)) == 0)
649			continue;
650
651		if (cb != NULL)
652			cb(ra, cmd, buf);
653
654		if (strncmp(buf, "OK", 2) == 0) {
655			retval = retval ? retval : 0;
656			break;
657		} else if (strstr(buf, "ERROR") != NULL) {
658			retval = -1;
659			break;
660		}
661		if (resp != NULL)
662			retval = strncmp(buf, resp, l);
663	}
664#ifdef DEBUG
665	fprintf(stderr, "SYNC_RETVAL=%d\n", retval);
666#endif
667	return (retval);
668}
669
670static int
671at_cmd_async(int fd, const char *cf, ...)
672{
673	size_t l;
674	va_list ap;
675	char cmd[64];
676
677	va_start(ap, cf);
678	vsnprintf(cmd, sizeof(cmd), cf, ap);
679	va_end(ap);
680
681#ifdef DEBUG
682	fprintf(stderr, "CMD: %s", cmd);
683#endif
684	l = strlen(cmd);
685	return (write(fd, cmd, l));
686}
687
688static void
689saveresp(resp_arg *ra, const char *cmd, const char *resp)
690{
691	char **buf;
692	int i = ra->val[1].int32;
693
694#ifdef DEBUG
695	fprintf(stderr, "Save '%s'\n", resp);
696#endif
697
698	buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1));
699	if (buf == NULL)
700		return;
701
702	buf[i] = strdup(resp);
703
704	ra->val[0].ptr = buf;
705	ra->val[1].int32 = i + 1;
706}
707
708static void
709freeresp(resp_arg *ra)
710{
711	char **buf;
712	int i;
713
714	buf = ra->val[0].ptr;
715	for (i = 0; i < ra->val[1].int32; i++) {
716		free(buf[i]);
717	}
718	free(buf);
719}
720
721static void
722at_async_creg(void *arg, const char *resp)
723{
724	struct ctx *ctx = arg;
725	int n, reg;
726
727	n = sscanf(resp, "+CREG: %*d,%d", &reg);
728	if (n != 1) {
729		n = sscanf(resp, "+CREG: %d", &reg);
730		if (n != 1)
731			return;
732	}
733
734	if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
735		tmr_add(&timers, 1, 1, tmr_creg, ctx);
736	}
737	else {
738		tmr_add(&timers, 1, 30, tmr_creg, ctx);
739	}
740
741	if (ctx->con_net_stat == reg)
742		return;
743
744	ctx->con_net_stat = reg;
745	at_cmd_async(ctx->fd, "AT+COPS?\r\n");
746}
747
748static void
749at_async_cgreg(void *arg, const char *resp)
750{
751	struct ctx *ctx = arg;
752	int n, reg;
753
754	n = sscanf(resp, "+CGREG: %*d,%d", &reg);
755	if (n != 1) {
756		n = sscanf(resp, "+CGREG: %d", &reg);
757		if (n != 1)
758			return;
759	}
760
761	if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
762		tmr_add(&timers, 1, 1, tmr_cgreg, ctx);
763	}
764	else {
765		tmr_add(&timers, 1, 30, tmr_cgreg, ctx);
766	}
767
768	if (ctx->con_net_stat == reg)
769		return;
770
771	ctx->con_net_stat = reg;
772	at_cmd_async(ctx->fd, "AT+COPS?\r\n");
773}
774
775
776static void
777at_async_cops(void *arg, const char *resp)
778{
779	struct ctx *ctx = arg;
780	int n, at;
781	char opr[64];
782
783	n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d",
784	    opr, &at);
785	if (n != 2)
786		return;
787
788	if (ctx->con_oper != NULL) {
789		if (ctx->con_net_type == at &&
790		    strcasecmp(opr, ctx->con_oper) == 0)
791			return;
792		free(ctx->con_oper);
793	}
794
795	ctx->con_oper = strdup(opr);
796	ctx->con_net_type = at;
797
798	if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) {
799		logger(LOG_NOTICE, "%s to \"%s\" (%s)",
800		    network_reg_status[ctx->con_net_stat],
801		    ctx->con_oper, network_access_type[ctx->con_net_type]);
802		if (ctx->con_status != 1) {
803			at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n",
804			    ctx->pdp_ctx);
805		}
806	}
807	else {
808		logger(LOG_NOTICE, "%s (%s)",
809		    network_reg_status[ctx->con_net_stat],
810		    network_access_type[ctx->con_net_type]);
811	}
812}
813
814/*
815 * Signal strength for pretty console output
816 *
817 * From 3GPP TS 27.007 V8.3.0, Section 8.5
818 * 0 = -113 dBm or less
819 * 1  = -111 dBm
820 * 2...30 = -109...-53 dBm
821 * 31 = -51 dBm or greater
822 *
823 * So, dbm = (rssi * 2) - 113
824*/
825static void
826at_async_csq(void *arg, const char *resp)
827{
828	struct ctx *ctx = arg;
829	int n, rssi;
830
831	n = sscanf(resp, "+CSQ: %d,%*d", &rssi);
832	if (n != 1)
833		return;
834	if (rssi == 99)
835		ctx->dbm = 0;
836	else {
837		ctx->dbm = (rssi * 2) - 113;
838		tmr_add(&timers, 1, 15, tmr_status, ctx);
839	}
840
841	ctx->flags |= FLG_NEWDATA;
842}
843
844static void
845at_async_owancall(void *arg, const char *resp)
846{
847	struct ctx *ctx = arg;
848	int n, i;
849
850	n = sscanf(resp, "_OWANCALL: %*d,%d", &i);
851	if (n != 1)
852		return;
853
854	if (i == ctx->con_status)
855		return;
856
857	at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx);
858
859	ctx->con_status = i;
860	if (ctx->con_status == 1) {
861		logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s",
862		    ctx->con_oper, ctx->con_apn,
863		    network_access_type[ctx->con_net_type]);
864	}
865	else {
866		logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)",
867		    ctx->con_oper, ctx->con_apn);
868	}
869}
870
871static void
872at_async_owandata(void *arg, const char *resp)
873{
874	struct ctx *ctx = arg;
875	char ip[40], ns1[40], ns2[40];
876	int n, error, rs;
877	struct ifaddrs *ifap, *ifa;
878	struct sockaddr_in sin, mask;
879	struct sockaddr_dl sdl;
880	struct {
881		struct rt_msghdr rtm;
882		char buf[512];
883	} r;
884	char *cp = r.buf;
885
886	n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]",
887	    ip, ns1, ns2);
888	if (n != 3)
889		return;
890
891	/* XXX: AF_INET assumption */
892
893	logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2);
894
895	sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in);
896	memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr));
897	sin.sin_family = mask.sin_family = AF_INET;
898
899	if (ctx->flags & IPASSIGNED) {
900		memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
901		    sizeof(sin.sin_addr.s_addr));
902		ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin,
903		    (struct sockaddr *)&mask);
904	}
905	inet_pton(AF_INET, ip, &ctx->ip.s_addr);
906	memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
907	    sizeof(sin.sin_addr.s_addr));
908
909	error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin,
910	    (struct sockaddr *)&mask);
911	if (error != 0) {
912		logger(LOG_ERR, "failed to set ip-address");
913		return;
914	}
915
916	if_ifup(ctx->ifnam);
917
918	ctx->flags |= IPASSIGNED;
919
920	set_nameservers(ctx, ctx->resolv_path, 0);
921	error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2);
922	if (error != 0) {
923		logger(LOG_ERR, "failed to set nameservers");
924	}
925
926	error = getifaddrs(&ifap);
927	if (error != 0) {
928		logger(LOG_ERR, "getifaddrs: %s", strerror(errno));
929		return;
930	}
931
932	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
933		if (ifa->ifa_addr->sa_family != AF_LINK)
934			continue;
935		if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) {
936			memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr,
937			    sizeof(struct sockaddr_dl));
938			break;
939		}
940	}
941	if (ifa == NULL)
942		return;
943
944	rs = socket(PF_ROUTE, SOCK_RAW, 0);
945	if (rs < 0) {
946		logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno));
947		return;
948	}
949
950	memset(&r, 0, sizeof(r));
951
952	r.rtm.rtm_version = RTM_VERSION;
953	r.rtm.rtm_type = RTM_ADD;
954	r.rtm.rtm_flags = RTF_UP | RTF_STATIC;
955	r.rtm.rtm_pid = getpid();
956	memset(&sin, 0, sizeof(struct sockaddr_in));
957	sin.sin_family = AF_INET;
958	sin.sin_len = sizeof(struct sockaddr_in);
959
960	memcpy(cp, &sin, sin.sin_len);
961	cp += SA_SIZE(&sin);
962	memcpy(cp, &sdl, sdl.sdl_len);
963	cp += SA_SIZE(&sdl);
964	memcpy(cp, &sin, sin.sin_len);
965	r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
966	r.rtm.rtm_msglen = sizeof(r);
967
968	n = write(rs, &r, r.rtm.rtm_msglen);
969	if (n != r.rtm.rtm_msglen) {
970		r.rtm.rtm_type = RTM_DELETE;
971		n = write(rs, &r, r.rtm.rtm_msglen);
972		r.rtm.rtm_type = RTM_ADD;
973		n = write(rs, &r, r.rtm.rtm_msglen);
974	}
975
976	if (n != r.rtm.rtm_msglen) {
977		logger(LOG_ERR, "failed to set default route: %s",
978		    strerror(errno));
979	}
980	close(rs);
981
982	/* Delayed daemonization */
983	if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON))
984		daemonize(ctx);
985}
986
987static int
988at_async(struct ctx *ctx, void *arg)
989{
990	int n, i;
991	size_t l;
992	char buf[512];
993
994	watchdog_reset(ctx, 15);
995
996	bzero(buf, sizeof(buf));
997	n = readline(ctx->fd, buf, sizeof(buf));
998	if (n <= 0)
999		return (n <= 0 ? -1 : 0);
1000
1001#ifdef DEBUG
1002	fprintf(stderr, "AT_ASYNC_RESP: %s", buf);
1003#endif
1004	for (i = 0; async_cmd[i].cmd != NULL; i++) {
1005		l = strlen(async_cmd[i].cmd);
1006		if (strncmp(buf, async_cmd[i].cmd, l) == 0) {
1007			async_cmd[i].func(arg, buf);
1008		}
1009	}
1010	return (0);
1011}
1012
1013static const char *port_type_list[] = {
1014	"control", "application", "application2", NULL
1015};
1016
1017/*
1018 * Attempts to find a list of control tty for the interface
1019 * FreeBSD attaches USB devices per interface so we have to go through
1020 * hoops to find which ttys that belong to our network interface.
1021 */
1022static char **
1023get_tty(struct ctx *ctx)
1024{
1025	char buf[64], data[128];
1026	int error, i, usbport, usbport0, list_size = 0;
1027	char **list = NULL;
1028	size_t len;
1029	const char **p, *q;
1030
1031	/*
1032	 * Look for the network interface first
1033	 */
1034	for (i = 0; ; i++) {
1035		/* Check if we still have uhso nodes to check */
1036		snprintf(buf, 64, SYSCTL_TEST, i);
1037		len = 127;
1038		error = sysctlbyname(buf, data, &len, NULL, 0);
1039		data[len] = '\0';
1040#ifdef DEBUG
1041		fprintf(stderr, "sysctl %s returned(%d): %s\n",
1042		    buf, error, error == 0 ? data : "FAILED");
1043#endif
1044		if (error < 0 || strcasecmp(data, "uhso") != 0)
1045			return NULL;
1046
1047		/* Check if this node contains the network interface we want */
1048		snprintf(buf, 64, SYSCTL_NETIF, i);
1049		len = 127;
1050		error = sysctlbyname(buf, data, &len, NULL, 0);
1051		data[len] = '\0';
1052#ifdef DEBUG
1053		fprintf(stderr, "sysctl %s returned(%d): %s\n",
1054		    buf, error, error == 0 ? data : "FAILED");
1055#endif
1056		if (error == 0 && strcasecmp(data, ctx->ifnam) == 0)
1057			break;
1058	}
1059
1060	/* Figure out the USB port location */
1061	snprintf(buf, 64, SYSCTL_LOCATION, i);
1062	len = 127;
1063	error = sysctlbyname(buf, data, &len, NULL, 0);
1064	data[len] = '\0';
1065#ifdef DEBUG
1066	fprintf(stderr, "sysctl %s returned(%d): %s\n",
1067	    buf, error, error == 0 ? data : "FAILED");
1068#endif
1069	if (error != 0)
1070		return (NULL);
1071
1072	q = strstr(data, "port=");
1073	if (q != NULL) {
1074		error = sscanf(q, " port=%d", &usbport);
1075		if (error != 1) {
1076#ifdef DEBUG
1077			fprintf(stderr, "failed to read usb port location from '%s'\n", data);
1078#endif
1079			return (NULL);
1080		}
1081	} else {
1082#ifdef DEBUG
1083			fprintf(stderr, "failed to parse location '%s'\n", data);
1084#endif
1085			return (NULL);
1086	}
1087#ifdef DEBUG
1088	fprintf(stderr, "USB port location=%d\n", usbport);
1089#endif
1090
1091	/*
1092	 * Now go through it all again but only look at those matching the
1093	 * usb port location we found.
1094	 */
1095	for (i = 0; ; i++) {
1096		snprintf(buf, 64, SYSCTL_LOCATION, i);
1097		len = 127;
1098		memset(&data, 0, sizeof(data));
1099		error = sysctlbyname(buf, data, &len, NULL, 0);
1100		if (error != 0)
1101			break;
1102		data[len] = '\0';
1103		q = strstr(data, "port=");
1104		if (q == NULL)
1105			continue;
1106		sscanf(q, " port=%d", &usbport0);
1107		if (usbport != usbport0)
1108			continue;
1109
1110		/* Try to add ports */
1111		for (p = port_type_list; *p != NULL; p++) {
1112			snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p);
1113			len = 127;
1114			memset(&data, 0, sizeof(data));
1115			error = sysctlbyname(buf, data, &len, NULL, 0);
1116			data[len] = '\0';
1117#ifdef DEBUG
1118			fprintf(stderr, "sysctl %s returned(%d): %s\n",
1119			    buf, error, error == 0 ? data : "FAILED");
1120#endif
1121			if (error == 0) {
1122				list = realloc(list, (list_size + 1) * sizeof(char *));
1123				list[list_size] = malloc(strlen(data) + strlen(TTY_NAME));
1124		    		sprintf(list[list_size], TTY_NAME, data);
1125		    		list_size++;
1126			}
1127		}
1128	}
1129	list = realloc(list, (list_size + 1) * sizeof(char *));
1130	list[list_size] = NULL;
1131	return (list);
1132}
1133
1134static int
1135do_connect(struct ctx *ctx, const char *tty)
1136{
1137	int i, error, needcfg;
1138	resp_arg ra;
1139	struct termios t;
1140	char **buf;
1141
1142#ifdef DEBUG
1143	fprintf(stderr, "Attempting to open %s\n", tty);
1144#endif
1145
1146	ctx->fd = open(tty, O_RDWR);
1147	if (ctx->fd < 0) {
1148#ifdef DEBUG
1149		fprintf(stderr, "Failed to open %s\n", tty);
1150#endif
1151		return (-1);
1152	}
1153
1154	tcgetattr(ctx->fd, &t);
1155	t.c_oflag = 0;
1156	t.c_iflag = 0;
1157	t.c_cflag = CLOCAL | CREAD;
1158	t.c_lflag = 0;
1159	tcsetattr(ctx->fd, TCSAFLUSH, &t);
1160
1161	error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n");
1162	if (error == -2) {
1163		warnx("failed to read from device %s", tty);
1164		return (-1);
1165	}
1166
1167	/* Check for PIN */
1168	error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n");
1169	if (error != 0) {
1170		ra.val[0].ptr = NULL;
1171		ra.val[1].int32 = 0;
1172		error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n");
1173		if (ra.val[1].int32 > 0) {
1174			char *p;
1175
1176			buf = ra.val[0].ptr;
1177			if (strstr(buf[0], "+CME ERROR:") != NULL) {
1178				buf[0] += 12;
1179				errx(1, "%s", buf[0]);
1180			}
1181			freeresp(&ra);
1182		} else
1183			freeresp(&ra);
1184
1185		if (ctx->pin == NULL) {
1186			errx(1, "device requires PIN");
1187		}
1188
1189		error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n",
1190		    ctx->pin);
1191		if (error != 0) {
1192			errx(1, "wrong PIN");
1193		}
1194	}
1195
1196	/*
1197	 * Check if a PDP context has been configured and configure one
1198	 * if needed.
1199	 */
1200	ra.val[0].ptr = NULL;
1201	ra.val[1].int32 = 0;
1202	error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n");
1203	buf = ra.val[0].ptr;
1204	needcfg = 1;
1205	for (i = 0; i < ra.val[1].int32; i++) {
1206		char apn[256];
1207		int cid;
1208		error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"",
1209		    &cid, apn);
1210		if (error != 2) {
1211			free(buf[i]);
1212			continue;
1213		}
1214
1215		if (cid == ctx->pdp_ctx) {
1216			ctx->con_apn = strdup(apn);
1217			if (ctx->pdp_apn != NULL) {
1218				if (strcmp(apn, ctx->pdp_apn) == 0)
1219					needcfg = 0;
1220			}
1221			else {
1222				needcfg = 0;
1223			}
1224		}
1225		free(buf[i]);
1226	}
1227	free(buf);
1228
1229	if (needcfg) {
1230		if (ctx->pdp_apn == NULL)
1231			errx(1, "device is not configured and no APN given");
1232
1233		error = at_cmd(ctx, NULL, NULL, NULL,
1234		   "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn);
1235		if (error != 0) {
1236			errx(1, "failed to configure device");
1237		}
1238		ctx->con_apn = strdup(ctx->pdp_apn);
1239	}
1240
1241	if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) {
1242		at_cmd(ctx, NULL, NULL, NULL,
1243		    "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx,
1244		    (ctx->pdp_user != NULL) ? ctx->pdp_user : "",
1245		    (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : "");
1246	}
1247
1248	error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1249	    ctx->pdp_ctx);
1250	if (error != 0)
1251		return (-1);
1252
1253	at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
1254	at_cmd_async(ctx->fd, "AT+CREG?\r\n");
1255
1256	tmr_add(&timers, 1, 5, tmr_status, ctx);
1257	return (0);
1258}
1259
1260static void
1261do_disconnect(struct ctx *ctx)
1262{
1263	struct sockaddr_in sin, mask;
1264
1265	/* Disconnect */
1266	at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1267	    ctx->pdp_ctx);
1268	close(ctx->fd);
1269
1270	/* Remove ip-address from interface */
1271	if (ctx->flags & IPASSIGNED) {
1272		sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in);
1273		memset(&mask.sin_addr.s_addr, 0xff,
1274		    sizeof(mask.sin_addr.s_addr));
1275		sin.sin_family = mask.sin_family = AF_INET;
1276		memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
1277		    sizeof(sin.sin_addr.s_addr));
1278		ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin,
1279		    (struct sockaddr *)&mask);
1280
1281		if_ifdown(ctx->ifnam);
1282		ctx->flags &= ~IPASSIGNED;
1283	}
1284
1285	/* Attempt to reset resolv.conf */
1286	set_nameservers(ctx, ctx->resolv_path, 0);
1287}
1288
1289static void
1290daemonize(struct ctx *ctx)
1291{
1292	struct pidfh *pfh;
1293	pid_t opid;
1294
1295	snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam);
1296
1297	pfh = pidfile_open(ctx->pidfile, 0600, &opid);
1298	if (pfh == NULL) {
1299		warn("Cannot create pidfile %s", ctx->pidfile);
1300		return;
1301	}
1302
1303	if (daemon(0, 0) == -1) {
1304		warn("Cannot daemonize");
1305		pidfile_remove(pfh);
1306		return;
1307	}
1308
1309	pidfile_write(pfh);
1310	ctx->pfh = pfh;
1311	ctx->flags |= FLG_DAEMON;
1312
1313	snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam);
1314	openlog(syslog_title, LOG_PID, LOG_USER);
1315	syslog_open = 1;
1316}
1317
1318static void
1319send_disconnect(const char *ifnam)
1320{
1321	char pidfile[128];
1322	FILE *fp;
1323	pid_t pid;
1324	int n;
1325
1326	snprintf(pidfile, 127, PIDFILE, ifnam);
1327	fp = fopen(pidfile, "r");
1328	if (fp == NULL) {
1329		warn("Cannot open %s", pidfile);
1330		return;
1331	}
1332
1333	n = fscanf(fp, "%d", &pid);
1334	fclose(fp);
1335	if (n != 1) {
1336		warnx("unable to read daemon pid");
1337		return;
1338	}
1339#ifdef DEBUG
1340	fprintf(stderr, "Sending SIGTERM to %d\n", pid);
1341#endif
1342	kill(pid, SIGTERM);
1343}
1344
1345static void
1346usage(const char *exec)
1347{
1348
1349	printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] "
1350	    "[-k password] [-r resolvpath] [-f tty] interface\n", exec);
1351	printf("usage %s -d interface\n", exec);
1352}
1353
1354enum {
1355	MODE_CONN,
1356	MODE_DISC
1357};
1358
1359int
1360main(int argc, char *argv[])
1361{
1362	int ch, error, mode;
1363	const char *ifnam = NULL;
1364	char *tty = NULL;
1365	char **p, **tty_list;
1366	fd_set set;
1367	struct ctx ctx;
1368	struct itimerval it;
1369
1370	TAILQ_INIT(&timers.head);
1371	timers.res = 1;
1372
1373	ctx.pdp_ctx = 1;
1374	ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL;
1375	ctx.pin = NULL;
1376
1377	ctx.con_status = 0;
1378	ctx.con_apn = NULL;
1379	ctx.con_oper = NULL;
1380	ctx.con_net_stat = 0;
1381	ctx.con_net_type = -1;
1382	ctx.flags = 0;
1383	ctx.resolv_path = RESOLV_PATH;
1384	ctx.resolv = NULL;
1385	ctx.ns = NULL;
1386	ctx.dbm = 0;
1387
1388	mode = MODE_CONN;
1389	ctx.flags |= FLG_DELAYED;
1390
1391	while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) {
1392		switch (ch) {
1393		case 'a':
1394			ctx.pdp_apn = argv[optind - 1];
1395			break;
1396		case 'c':
1397			ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10);
1398			if (ctx.pdp_ctx < 1) {
1399				warnx("Invalid context ID, defaulting to 1");
1400				ctx.pdp_ctx = 1;
1401			}
1402			break;
1403		case 'p':
1404			ctx.pin = argv[optind - 1];
1405			break;
1406		case 'u':
1407			ctx.pdp_user = argv[optind - 1];
1408			break;
1409		case 'k':
1410			ctx.pdp_pwd = argv[optind - 1];
1411			break;
1412		case 'r':
1413			ctx.resolv_path = argv[optind - 1];
1414			break;
1415		case 'd':
1416			mode = MODE_DISC;
1417			break;
1418		case 'b':
1419			ctx.flags &= ~FLG_DELAYED;
1420			break;
1421		case 'n':
1422			ctx.flags |= FLG_NODAEMON;
1423			break;
1424		case 'f':
1425			tty = argv[optind - 1];
1426			break;
1427		case 'h':
1428		case '?':
1429		default:
1430			usage(argv[0]);
1431			exit(EXIT_SUCCESS);
1432		}
1433	}
1434
1435	argc -= optind;
1436	argv += optind;
1437
1438	if (argc < 1)
1439		errx(1, "no interface given");
1440
1441	ifnam = argv[argc - 1];
1442	ctx.ifnam = strdup(ifnam);
1443
1444	switch (mode) {
1445	case MODE_DISC:
1446		printf("Disconnecting %s\n", ifnam);
1447		send_disconnect(ifnam);
1448		exit(EXIT_SUCCESS);
1449	default:
1450		break;
1451	}
1452
1453	signal(SIGHUP, sig_handle);
1454	signal(SIGINT, sig_handle);
1455	signal(SIGQUIT, sig_handle);
1456	signal(SIGTERM, sig_handle);
1457	signal(SIGALRM, sig_handle);
1458
1459	it.it_interval.tv_sec = 1;
1460	it.it_interval.tv_usec = 0;
1461	it.it_value.tv_sec = 1;
1462	it.it_value.tv_usec = 0;
1463	error = setitimer(ITIMER_REAL, &it, NULL);
1464	if (error != 0)
1465		errx(1, "setitimer");
1466
1467	tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx);
1468	watchdog_reset(&ctx, 15);
1469
1470	if (tty != NULL) {
1471		error = do_connect(&ctx, tty);
1472		if (error != 0)
1473			errx(1, "Failed to open %s", tty);
1474	}
1475	else {
1476		tty_list = get_tty(&ctx);
1477		if (tty_list == NULL)
1478			errx(1, "%s does not appear to be a uhso device", ifnam);
1479#ifdef DEBUG
1480		if (tty_list == NULL) {
1481			fprintf(stderr, "get_tty returned empty list\n");
1482		} else {
1483			fprintf(stderr, "tty list:\n");
1484			for (p = tty_list; *p != NULL; p++) {
1485				fprintf(stderr, "\t %s\n", *p);
1486			}
1487		}
1488#endif
1489		for (p = tty_list; *p != NULL; p++) {
1490			error = do_connect(&ctx, *p);
1491			if (error == 0) {
1492				tty = *p;
1493				break;
1494			}
1495		}
1496		if (*p == NULL)
1497			errx(1, "Failed to obtain a control port, "
1498			    "try specifying one manually");
1499	}
1500
1501	if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON))
1502		daemonize(&ctx);
1503
1504
1505	FD_ZERO(&set);
1506	FD_SET(ctx.fd, &set);
1507	for (;;) {
1508
1509		watchdog_disable(&ctx);
1510		error = select(ctx.fd + 1, &set, NULL, NULL, NULL);
1511		if (error <= 0) {
1512			if (running && errno == EINTR)
1513				continue;
1514			if (ctx.flags & FLG_WDEXP) {
1515				ctx.flags &= ~FLG_WDEXP;
1516				watchdog_reset(&ctx, 5);
1517				do_disconnect(&ctx);
1518				watchdog_reset(&ctx, 15);
1519				do_connect(&ctx, tty);
1520				running = 1;
1521				continue;
1522			}
1523
1524			break;
1525		}
1526
1527		if (FD_ISSET(ctx.fd, &set)) {
1528			watchdog_reset(&ctx, 15);
1529			error = at_async(&ctx, &ctx);
1530			if (error != 0)
1531				break;
1532		}
1533		FD_SET(ctx.fd, &set);
1534
1535		if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) {
1536			printf("Status: %s (%s)",
1537			    ctx.con_status ? "connected" : "disconnected",
1538			    network_access_type[ctx.con_net_type]);
1539			if (ctx.dbm < 0)
1540				printf(", signal: %d dBm", ctx.dbm);
1541			printf("\t\t\t\r");
1542			fflush(stdout);
1543		}
1544	}
1545	if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED))
1546		printf("\n");
1547
1548	signal(SIGHUP, SIG_DFL);
1549	signal(SIGINT, SIG_DFL);
1550	signal(SIGQUIT, SIG_DFL);
1551	signal(SIGTERM, SIG_DFL);
1552	signal(SIGALRM, SIG_IGN);
1553
1554	do_disconnect(&ctx);
1555
1556	if (ctx.flags & FLG_DAEMON) {
1557		pidfile_remove(ctx.pfh);
1558		if (syslog_open)
1559			closelog();
1560	}
1561
1562	return (0);
1563}
1564