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