xref: /freebsd/usr.sbin/uhsoctl/uhsoctl.c (revision aa64588d28258aef88cc33b8043112e8856948d0)
1 /*-
2  * Copyright (c) 2008-2009 Fredrik Lindberg
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <sys/socket.h>
31 #include <sys/sockio.h>
32 #include <sys/select.h>
33 #include <sys/stat.h>
34 #include <sys/sysctl.h>
35 #include <sys/time.h>
36 #include <sys/queue.h>
37 
38 #include <arpa/inet.h>
39 #include <net/if.h>
40 #include <net/if_var.h>
41 #include <net/if_dl.h>
42 #include <net/route.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 
46 #include <err.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <termios.h>
50 #include <stdarg.h>
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <stdint.h>
54 #include <string.h>
55 #include <signal.h>
56 #include <syslog.h>
57 #include <unistd.h>
58 #include <ifaddrs.h>
59 #include <libutil.h>
60 #include <time.h>
61 
62 /*
63  * Connection utility to ease connectivity using the raw IP packet interface
64  * available on uhso(4) devices.
65  */
66 
67 #define TTY_NAME	"/dev/%s"
68 #define SYSCTL_TEST	"dev.uhso.%d.%%driver"
69 #define SYSCTL_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 		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 */
222 static void
223 tmr_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 
257 static void
258 watchdog_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 
268 static void
269 tmr_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 
277 static void
278 tmr_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 
286 static void
287 tmr_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 
295 static void
296 tmr_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 
319 static void
320 sig_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 
336 static void
337 logger(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 */
363 static int
364 ifaddr_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 
386 static int
387 if_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 
421 static int
422 ifaddr_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 
432 static int
433 ifaddr_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 
443 static int
444 set_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 */
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 	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 
663 static int
664 at_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 
681 static void
682 saveresp(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 
697 static void
698 at_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 
724 static void
725 at_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 
752 static void
753 at_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 */
801 static void
802 at_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 
820 static void
821 at_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 
847 static void
848 at_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 
963 static int
964 at_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 
989 static 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  */
998 static char **
999 get_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 
1120 static int
1121 do_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 
1231 static void
1232 do_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 
1260 static void
1261 daemonize(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 
1289 static void
1290 send_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 
1316 static void
1317 usage(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 
1325 enum {
1326 	MODE_CONN,
1327 	MODE_DISC
1328 };
1329 
1330 int
1331 main(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