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