xref: /freebsd/usr.sbin/uhsoctl/uhsoctl.c (revision 4e8d558c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
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 
78 static const char *network_access_type[] = {
79 	"GSM",
80 	"Compact GSM",
81 	"UMTS",
82 	"GSM (EGPRS)",
83 	"HSDPA",
84 	"HSUPA",
85 	"HSDPA/HSUPA"
86 };
87 
88 static 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 
97 struct 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 
139 static int readline_buf(const char *, const char *, char *, size_t);
140 static int readline(int, char *, size_t);
141 static void daemonize(struct ctx *);
142 
143 static int at_cmd_async(int, const char *, ...);
144 
145 typedef union {
146 	void *ptr;
147 	uint32_t int32;
148 } resp_data;
149 typedef struct {
150 	resp_data val[2];
151 } resp_arg;
152 typedef void (*resp_cb)(resp_arg *, const char *, const char *);
153 
154 typedef void (*async_cb)(void *, const char *);
155 struct async_handle {
156 	const char *cmd;
157 	async_cb func;
158 };
159 
160 static void at_async_creg(void *, const char *);
161 static void at_async_cgreg(void *, const char *);
162 static void at_async_cops(void *, const char *);
163 static void at_async_owancall(void *, const char *);
164 static void at_async_owandata(void *, const char *);
165 static void at_async_csq(void *, const char *);
166 
167 static 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 
177 struct timer_entry;
178 struct timers {
179 	TAILQ_HEAD(, timer_entry) head;
180 	int res;
181 };
182 
183 typedef void (*tmr_cb)(int, void *);
184 struct timer_entry {
185 	TAILQ_ENTRY(timer_entry) next;
186 	int id;
187 	int timeout;
188 	tmr_cb func;
189 	void *arg;
190 };
191 
192 
193 static struct timers timers;
194 static volatile int running = 1;
195 static int syslog_open = 0;
196 static char syslog_title[64];
197 
198 /* Periodic timer, runs ready timer tasks every tick */
199 static void
200 tmr_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 */
221 static void
222 tmr_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 
256 static void
257 watchdog_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 
267 static void
268 tmr_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 
276 static void
277 tmr_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 
285 static void
286 tmr_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 
294 static void
295 tmr_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 
318 static void
319 sig_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 
335 static void
336 logger(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 */
362 static int
363 ifaddr_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 
385 static int
386 if_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 
420 static int
421 ifaddr_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 
431 static int
432 ifaddr_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 
442 static int
443 set_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 */
537 static int
538 readline_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 */
556 static int
557 readline(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  */
579 static int
580 at_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 
670 static int
671 at_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 
688 static void
689 saveresp(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 
708 static void
709 freeresp(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 
721 static void
722 at_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 
748 static void
749 at_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 
776 static void
777 at_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 */
825 static void
826 at_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 
844 static void
845 at_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 
871 static void
872 at_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 
987 static int
988 at_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 
1013 static 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  */
1022 static char **
1023 get_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 
1134 static int
1135 do_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 
1260 static void
1261 do_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 
1289 static void
1290 daemonize(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 
1318 static void
1319 send_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 
1345 static void
1346 usage(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 
1354 enum {
1355 	MODE_CONN,
1356 	MODE_DISC
1357 };
1358 
1359 int
1360 main(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