xref: /freebsd/usr.sbin/uhsoctl/uhsoctl.c (revision f126890ac5386406dadf7c4cfa9566cbb56537c5)
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 
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_dl.h>
41 #include <net/route.h>
42 #include <netinet/in.h>
43 #include <netinet/in_var.h>
44 
45 #include <err.h>
46 #include <errno.h>
47 #include <fcntl.h>
48 #include <termios.h>
49 #include <stdarg.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <stdint.h>
53 #include <string.h>
54 #include <signal.h>
55 #include <syslog.h>
56 #include <unistd.h>
57 #include <ifaddrs.h>
58 #include <libutil.h>
59 #include <time.h>
60 
61 /*
62  * Connection utility to ease connectivity using the raw IP packet interface
63  * available on uhso(4) devices.
64  */
65 
66 #define TTY_NAME	"/dev/%s"
67 #define SYSCTL_TEST	"dev.uhso.%d.%%driver"
68 #define SYSCTL_LOCATION "dev.uhso.%d.%%location"
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 
76 static const char *network_access_type[] = {
77 	"GSM",
78 	"Compact GSM",
79 	"UMTS",
80 	"GSM (EGPRS)",
81 	"HSDPA",
82 	"HSUPA",
83 	"HSDPA/HSUPA"
84 };
85 
86 static 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 
95 struct 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 
137 static int readline_buf(const char *, const char *, char *, size_t);
138 static int readline(int, char *, size_t);
139 static void daemonize(struct ctx *);
140 
141 static int at_cmd_async(int, const char *, ...);
142 
143 typedef union {
144 	void *ptr;
145 	uint32_t int32;
146 } resp_data;
147 typedef struct {
148 	resp_data val[2];
149 } resp_arg;
150 typedef void (*resp_cb)(resp_arg *, const char *, const char *);
151 
152 typedef void (*async_cb)(void *, const char *);
153 struct async_handle {
154 	const char *cmd;
155 	async_cb func;
156 };
157 
158 static void at_async_creg(void *, const char *);
159 static void at_async_cgreg(void *, const char *);
160 static void at_async_cops(void *, const char *);
161 static void at_async_owancall(void *, const char *);
162 static void at_async_owandata(void *, const char *);
163 static void at_async_csq(void *, const char *);
164 
165 static 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 
175 struct timer_entry;
176 struct timers {
177 	TAILQ_HEAD(, timer_entry) head;
178 	int res;
179 };
180 
181 typedef void (*tmr_cb)(int, void *);
182 struct timer_entry {
183 	TAILQ_ENTRY(timer_entry) next;
184 	int id;
185 	int timeout;
186 	tmr_cb func;
187 	void *arg;
188 };
189 
190 
191 static struct timers timers;
192 static volatile int running = 1;
193 static int syslog_open = 0;
194 static char syslog_title[64];
195 
196 /* Periodic timer, runs ready timer tasks every tick */
197 static void
198 tmr_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 		te->func(te->id, te->arg);
211 		free(te);
212 		te = te2;
213 		if (te == NULL)
214 			break;
215 	}
216 }
217 
218 /* Add a new timer */
219 static void
220 tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg)
221 {
222 	struct timer_entry *te, *te2, *te3;
223 
224 	te = malloc(sizeof(struct timer_entry));
225 	memset(te, 0, sizeof(struct timer_entry));
226 
227 	te->timeout = timeout;
228 	te->func = func;
229 	te->arg = arg;
230 	te->id = id;
231 
232 	te2 = TAILQ_FIRST(&tmrs->head);
233 
234 	if (TAILQ_EMPTY(&tmrs->head)) {
235 		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
236 	} else if (te->timeout < te2->timeout) {
237 		te2->timeout -= te->timeout;
238 		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
239 	} else {
240 		while (te->timeout >= te2->timeout) {
241 			te->timeout -= te2->timeout;
242 			te3 = TAILQ_NEXT(te2, next);
243 			if (te3 == NULL || te3->timeout > te->timeout)
244 				break;
245 			te2 = te3;
246 		}
247 		TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next);
248 	}
249 }
250 
251 #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG
252 #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG
253 
254 static void
255 watchdog_reset(struct ctx *ctx, int timeout)
256 {
257 	struct timespec tp;
258 
259 	clock_gettime(CLOCK_MONOTONIC, &tp),
260 	ctx->watchdog = tp.tv_sec + timeout;
261 
262 	watchdog_enable(ctx);
263 }
264 
265 static void
266 tmr_creg(int id, void *arg)
267 {
268 	struct ctx *ctx = arg;
269 
270 	at_cmd_async(ctx->fd, "AT+CREG?\r\n");
271 	watchdog_reset(ctx, 10);
272 }
273 
274 static void
275 tmr_cgreg(int id, void *arg)
276 {
277 	struct ctx *ctx = arg;
278 
279 	at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
280 	watchdog_reset(ctx, 10);
281 }
282 
283 static void
284 tmr_status(int id, void *arg)
285 {
286 	struct ctx *ctx = arg;
287 
288 	at_cmd_async(ctx->fd, "AT+CSQ\r\n");
289 	watchdog_reset(ctx, 10);
290 }
291 
292 static void
293 tmr_watchdog(int id, void *arg)
294 {
295 	struct ctx *ctx = arg;
296 	pid_t self;
297 	struct timespec tp;
298 
299 	tmr_add(&timers, 1, 5, tmr_watchdog, ctx);
300 
301 	if (!(ctx->flags & FLG_WATCHDOG))
302 		return;
303 
304 	clock_gettime(CLOCK_MONOTONIC, &tp);
305 
306 	if (tp.tv_sec >= ctx->watchdog) {
307 #ifdef DEBUG
308 		fprintf(stderr, "Watchdog expired\n");
309 #endif
310 		ctx->flags |= FLG_WDEXP;
311 		self = getpid();
312 		kill(self, SIGHUP);
313 	}
314 }
315 
316 static void
317 sig_handle(int sig)
318 {
319 
320 	switch (sig) {
321 	case SIGHUP:
322 	case SIGINT:
323 	case SIGQUIT:
324 	case SIGTERM:
325 		running = 0;
326 		break;
327 	case SIGALRM:
328 		tmr_run(&timers);
329 		break;
330 	}
331 }
332 
333 static void
334 logger(int pri, const char *fmt, ...)
335 {
336 	char *buf;
337 	va_list ap;
338 
339 	va_start(ap, fmt);
340 	vasprintf(&buf, fmt, ap);
341 	if (syslog_open)
342 		syslog(pri, "%s", buf);
343 	else {
344 		switch (pri) {
345 		case LOG_INFO:
346 		case LOG_NOTICE:
347 			printf("%s\n", buf);
348 			break;
349 		default:
350 			fprintf(stderr, "%s: %s\n", getprogname(), buf);
351 			break;
352 		}
353 	}
354 
355 	free(buf);
356 	va_end(ap);
357 }
358 
359 /* Add/remove IP address from an interface */
360 static int
361 ifaddr_ad(unsigned long d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
362 {
363 	struct ifaliasreq req;
364 	int fd, error;
365 
366 	fd = socket(AF_INET, SOCK_DGRAM, 0);
367 	if (fd < 0)
368 		return (-1);
369 
370 	memset(&req, 0, sizeof(struct ifaliasreq));
371 	strlcpy(req.ifra_name, ifnam, sizeof(req.ifra_name));
372 	memcpy(&req.ifra_addr, sa, sa->sa_len);
373 	memcpy(&req.ifra_mask, mask, mask->sa_len);
374 
375 	error = ioctl(fd, d, (char *)&req);
376 	close(fd);
377 	return (error);
378 }
379 
380 #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP)
381 #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP)
382 
383 static int
384 if_setflags(const char *ifnam, int flags)
385 {
386 	struct ifreq ifr;
387 	int fd, error;
388 	unsigned int oflags = 0;
389 
390 	memset(&ifr, 0, sizeof(struct ifreq));
391 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
392 
393 	fd = socket(AF_INET, SOCK_DGRAM, 0);
394 	if (fd < 0)
395 		return (-1);
396 
397 	error = ioctl(fd, SIOCGIFFLAGS, &ifr);
398 	if (error == 0) {
399 		oflags = (ifr.ifr_flags & 0xffff)  | (ifr.ifr_flagshigh << 16);
400 	}
401 
402 	if (flags < 0)
403 		oflags &= ~(-flags);
404 	else
405 		oflags |= flags;
406 
407 	ifr.ifr_flags = oflags & 0xffff;
408 	ifr.ifr_flagshigh = oflags >> 16;
409 
410 	error = ioctl(fd, SIOCSIFFLAGS, &ifr);
411 	if (error != 0)
412 		warn("ioctl SIOCSIFFLAGS");
413 
414 	close(fd);
415 	return (error);
416 }
417 
418 static int
419 ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
420 {
421 	int error;
422 
423 	error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask);
424 	if (error != 0)
425 		warn("ioctl SIOCAIFADDR");
426 	return (error);
427 }
428 
429 static int
430 ifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
431 {
432 	int error;
433 
434 	error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask);
435 	if (error != 0)
436 		warn("ioctl SIOCDIFADDR");
437 	return (error);
438 }
439 
440 static int
441 set_nameservers(struct ctx *ctx, const char *respath, int ns, ...)
442 {
443 	int i, n, fd;
444 	FILE *fp;
445 	char *p;
446 	va_list ap;
447 	struct stat sb;
448 	char buf[512];
449 
450 	if (ctx->ns != NULL) {
451 		for (i = 0; ctx->ns[i] != NULL; i++) {
452 			free(ctx->ns[i]);
453 		}
454 		free(ctx->ns);
455 		ctx->ns = NULL;
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 */
535 static int
536 readline_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 */
554 static int
555 readline(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  */
577 static int
578 at_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 (%zu)\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 
668 static int
669 at_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 
686 static void
687 saveresp(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 
706 static void
707 freeresp(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 
719 static void
720 at_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 
746 static void
747 at_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 
774 static void
775 at_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 */
823 static void
824 at_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 
842 static void
843 at_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 
869 static void
870 at_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 
985 static int
986 at_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 
1011 static 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  */
1020 static char **
1021 get_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 
1132 static int
1133 do_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 
1258 static void
1259 do_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 
1287 static void
1288 daemonize(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 
1316 static void
1317 send_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 
1343 static void
1344 usage(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 
1352 enum {
1353 	MODE_CONN,
1354 	MODE_DISC
1355 };
1356 
1357 int
1358 main(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