xref: /freebsd-src/usr.sbin/uhsoctl/uhsoctl.c (revision c74a4cea0dc9dddf996179b1586fb6e9fbf6b702)
1941e2863SAndrew Thompson /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4941e2863SAndrew Thompson  * Copyright (c) 2008-2009 Fredrik Lindberg
5941e2863SAndrew Thompson  * All rights reserved.
6941e2863SAndrew Thompson  *
7941e2863SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
8941e2863SAndrew Thompson  * modification, are permitted provided that the following conditions
9941e2863SAndrew Thompson  * are met:
10941e2863SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
11941e2863SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
12941e2863SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
13941e2863SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
14941e2863SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
15941e2863SAndrew Thompson  *
16941e2863SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17941e2863SAndrew Thompson  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18941e2863SAndrew Thompson  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19941e2863SAndrew Thompson  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20941e2863SAndrew Thompson  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21941e2863SAndrew Thompson  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22941e2863SAndrew Thompson  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23941e2863SAndrew Thompson  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24941e2863SAndrew Thompson  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25941e2863SAndrew Thompson  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26941e2863SAndrew Thompson  */
27941e2863SAndrew Thompson 
28941e2863SAndrew Thompson #include <sys/types.h>
29941e2863SAndrew Thompson #include <sys/param.h>
30941e2863SAndrew Thompson #include <sys/socket.h>
31941e2863SAndrew Thompson #include <sys/sockio.h>
32941e2863SAndrew Thompson #include <sys/select.h>
33941e2863SAndrew Thompson #include <sys/stat.h>
34941e2863SAndrew Thompson #include <sys/sysctl.h>
35941e2863SAndrew Thompson #include <sys/time.h>
36941e2863SAndrew Thompson #include <sys/queue.h>
37941e2863SAndrew Thompson 
38941e2863SAndrew Thompson #include <arpa/inet.h>
39941e2863SAndrew Thompson #include <net/if.h>
40941e2863SAndrew Thompson #include <net/if_dl.h>
41941e2863SAndrew Thompson #include <net/route.h>
42941e2863SAndrew Thompson #include <netinet/in.h>
43941e2863SAndrew Thompson #include <netinet/in_var.h>
44941e2863SAndrew Thompson 
45941e2863SAndrew Thompson #include <err.h>
46941e2863SAndrew Thompson #include <errno.h>
47941e2863SAndrew Thompson #include <fcntl.h>
48941e2863SAndrew Thompson #include <termios.h>
49941e2863SAndrew Thompson #include <stdarg.h>
50941e2863SAndrew Thompson #include <stdio.h>
51941e2863SAndrew Thompson #include <stdlib.h>
52941e2863SAndrew Thompson #include <stdint.h>
53941e2863SAndrew Thompson #include <string.h>
54941e2863SAndrew Thompson #include <signal.h>
55941e2863SAndrew Thompson #include <syslog.h>
56941e2863SAndrew Thompson #include <unistd.h>
57941e2863SAndrew Thompson #include <ifaddrs.h>
58941e2863SAndrew Thompson #include <libutil.h>
59941e2863SAndrew Thompson #include <time.h>
60941e2863SAndrew Thompson 
61941e2863SAndrew Thompson /*
62941e2863SAndrew Thompson  * Connection utility to ease connectivity using the raw IP packet interface
63941e2863SAndrew Thompson  * available on uhso(4) devices.
64941e2863SAndrew Thompson  */
65941e2863SAndrew Thompson 
66941e2863SAndrew Thompson #define TTY_NAME	"/dev/%s"
67941e2863SAndrew Thompson #define SYSCTL_TEST	"dev.uhso.%d.%%driver"
68eea19fceSAndrew Thompson #define SYSCTL_LOCATION "dev.uhso.%d.%%location"
69941e2863SAndrew Thompson #define SYSCTL_PORTS	"dev.uhso.%d.ports"
70941e2863SAndrew Thompson #define SYSCTL_NETIF	"dev.uhso.%d.netif"
71941e2863SAndrew Thompson #define SYSCTL_NAME_TTY	"dev.uhso.%d.port.%s.tty"
72941e2863SAndrew Thompson #define SYSCTL_NAME_DESC "dev.uhso.%d.port.%s.desc"
73941e2863SAndrew Thompson #define RESOLV_PATH	"/etc/resolv.conf"
74941e2863SAndrew Thompson #define PIDFILE		"/var/run/uhsoctl.%s.pid"
75941e2863SAndrew Thompson 
76941e2863SAndrew Thompson static const char *network_access_type[] = {
77941e2863SAndrew Thompson 	"GSM",
78941e2863SAndrew Thompson 	"Compact GSM",
79941e2863SAndrew Thompson 	"UMTS",
80941e2863SAndrew Thompson 	"GSM (EGPRS)",
81941e2863SAndrew Thompson 	"HSDPA",
82941e2863SAndrew Thompson 	"HSUPA",
83941e2863SAndrew Thompson 	"HSDPA/HSUPA"
84941e2863SAndrew Thompson };
85941e2863SAndrew Thompson 
86941e2863SAndrew Thompson static const char *network_reg_status[] = {
87941e2863SAndrew Thompson 	"Not registered",
88941e2863SAndrew Thompson 	"Registered",
89941e2863SAndrew Thompson 	"Searching for network",
90941e2863SAndrew Thompson 	"Network registration denied",
91941e2863SAndrew Thompson 	"Unknown",
92941e2863SAndrew Thompson 	"Registered (roaming)"
93941e2863SAndrew Thompson };
94941e2863SAndrew Thompson 
95941e2863SAndrew Thompson struct ctx {
96941e2863SAndrew Thompson 	int fd;
97941e2863SAndrew Thompson 	int flags;
98941e2863SAndrew Thompson #define IPASSIGNED	0x01
99941e2863SAndrew Thompson #define FLG_NODAEMON	0x02 /* Don't detach from terminal */
100941e2863SAndrew Thompson #define FLG_DAEMON	0x04 /* Running as daemon */
101941e2863SAndrew Thompson #define FLG_DELAYED	0x08 /* Fork into background after connect */
102941e2863SAndrew Thompson #define FLG_NEWDATA	0x10
103941e2863SAndrew Thompson #define FLG_WATCHDOG	0x20 /* Watchdog enabled */
104941e2863SAndrew Thompson #define FLG_WDEXP	0x40 /* Watchdog expired */
105941e2863SAndrew Thompson 	const char *ifnam;
106941e2863SAndrew Thompson 	const char *pin; /* device PIN */
107941e2863SAndrew Thompson 
108941e2863SAndrew Thompson 	char pidfile[128];
109941e2863SAndrew Thompson 	struct pidfh *pfh;
110941e2863SAndrew Thompson 
111941e2863SAndrew Thompson 	time_t watchdog;
112941e2863SAndrew Thompson 
113941e2863SAndrew Thompson 	/* PDP context settings */
114941e2863SAndrew Thompson 	int pdp_ctx;
115941e2863SAndrew Thompson 	const char *pdp_apn;
116941e2863SAndrew Thompson 	const char *pdp_user;
117941e2863SAndrew Thompson 	const char *pdp_pwd;
118941e2863SAndrew Thompson 
119941e2863SAndrew Thompson 	/* Connection status */
120941e2863SAndrew Thompson 	int con_status;		/* Connected? */
121941e2863SAndrew Thompson 	char *con_apn;		/* Connected APN */
122941e2863SAndrew Thompson 	char *con_oper;		/* Operator name */
123941e2863SAndrew Thompson 	int con_net_stat;	/* Network connection status */
124941e2863SAndrew Thompson 	int con_net_type;	/* Network connection type */
125941e2863SAndrew Thompson 
126941e2863SAndrew Thompson 	/* Misc. status */
127941e2863SAndrew Thompson 	int dbm;
128941e2863SAndrew Thompson 
129941e2863SAndrew Thompson 	/* IP and nameserver settings */
130941e2863SAndrew Thompson 	struct in_addr ip;
131941e2863SAndrew Thompson 	char **ns;
132941e2863SAndrew Thompson 	const char *resolv_path;
133941e2863SAndrew Thompson 	char *resolv;		/* Old resolv.conf */
134941e2863SAndrew Thompson 	size_t resolv_sz;
135941e2863SAndrew Thompson };
136941e2863SAndrew Thompson 
137941e2863SAndrew Thompson static int readline_buf(const char *, const char *, char *, size_t);
138941e2863SAndrew Thompson static int readline(int, char *, size_t);
139941e2863SAndrew Thompson static void daemonize(struct ctx *);
140941e2863SAndrew Thompson 
141941e2863SAndrew Thompson static int at_cmd_async(int, const char *, ...);
142941e2863SAndrew Thompson 
143941e2863SAndrew Thompson typedef union {
144941e2863SAndrew Thompson 	void *ptr;
145941e2863SAndrew Thompson 	uint32_t int32;
146941e2863SAndrew Thompson } resp_data;
147941e2863SAndrew Thompson typedef struct {
148941e2863SAndrew Thompson 	resp_data val[2];
149941e2863SAndrew Thompson } resp_arg;
150941e2863SAndrew Thompson typedef void (*resp_cb)(resp_arg *, const char *, const char *);
151941e2863SAndrew Thompson 
152941e2863SAndrew Thompson typedef void (*async_cb)(void *, const char *);
153941e2863SAndrew Thompson struct async_handle {
154941e2863SAndrew Thompson 	const char *cmd;
155941e2863SAndrew Thompson 	async_cb func;
156941e2863SAndrew Thompson };
157941e2863SAndrew Thompson 
158941e2863SAndrew Thompson static void at_async_creg(void *, const char *);
159941e2863SAndrew Thompson static void at_async_cgreg(void *, const char *);
160941e2863SAndrew Thompson static void at_async_cops(void *, const char *);
161941e2863SAndrew Thompson static void at_async_owancall(void *, const char *);
162941e2863SAndrew Thompson static void at_async_owandata(void *, const char *);
163941e2863SAndrew Thompson static void at_async_csq(void *, const char *);
164941e2863SAndrew Thompson 
165941e2863SAndrew Thompson static struct async_handle async_cmd[] = {
166941e2863SAndrew Thompson 	{ "+CREG", at_async_creg },
167941e2863SAndrew Thompson 	{ "+CGREG", at_async_cgreg },
168941e2863SAndrew Thompson 	{ "+COPS", at_async_cops },
169941e2863SAndrew Thompson 	{ "+CSQ", at_async_csq },
170941e2863SAndrew Thompson 	{ "_OWANCALL", at_async_owancall },
171941e2863SAndrew Thompson 	{ "_OWANDATA", at_async_owandata },
172941e2863SAndrew Thompson 	{ NULL, NULL }
173941e2863SAndrew Thompson };
174941e2863SAndrew Thompson 
175941e2863SAndrew Thompson struct timer_entry;
176941e2863SAndrew Thompson struct timers {
177941e2863SAndrew Thompson 	TAILQ_HEAD(, timer_entry) head;
178941e2863SAndrew Thompson 	int res;
179941e2863SAndrew Thompson };
180941e2863SAndrew Thompson 
181941e2863SAndrew Thompson typedef void (*tmr_cb)(int, void *);
182941e2863SAndrew Thompson struct timer_entry {
183941e2863SAndrew Thompson 	TAILQ_ENTRY(timer_entry) next;
184941e2863SAndrew Thompson 	int id;
185941e2863SAndrew Thompson 	int timeout;
186941e2863SAndrew Thompson 	tmr_cb func;
187941e2863SAndrew Thompson 	void *arg;
188941e2863SAndrew Thompson };
189941e2863SAndrew Thompson 
190941e2863SAndrew Thompson 
191941e2863SAndrew Thompson static struct timers timers;
192941e2863SAndrew Thompson static volatile int running = 1;
193941e2863SAndrew Thompson static int syslog_open = 0;
194941e2863SAndrew Thompson static char syslog_title[64];
195941e2863SAndrew Thompson 
196941e2863SAndrew Thompson /* Periodic timer, runs ready timer tasks every tick */
197941e2863SAndrew Thompson static void
198941e2863SAndrew Thompson tmr_run(struct timers *tmrs)
199941e2863SAndrew Thompson {
200941e2863SAndrew Thompson 	struct timer_entry *te, *te2;
201941e2863SAndrew Thompson 
202941e2863SAndrew Thompson 	te = TAILQ_FIRST(&tmrs->head);
203941e2863SAndrew Thompson 	if (te == NULL)
204941e2863SAndrew Thompson 		return;
205941e2863SAndrew Thompson 
206941e2863SAndrew Thompson 	te->timeout -= tmrs->res;
207941e2863SAndrew Thompson 	while (te->timeout <= 0) {
208941e2863SAndrew Thompson 		te2 = TAILQ_NEXT(te, next);
209941e2863SAndrew Thompson 		TAILQ_REMOVE(&tmrs->head, te, next);
210941e2863SAndrew Thompson 		te->func(te->id, te->arg);
211941e2863SAndrew Thompson 		free(te);
212941e2863SAndrew Thompson 		te = te2;
213941e2863SAndrew Thompson 		if (te == NULL)
214941e2863SAndrew Thompson 			break;
215941e2863SAndrew Thompson 	}
216941e2863SAndrew Thompson }
217941e2863SAndrew Thompson 
218941e2863SAndrew Thompson /* Add a new timer */
219941e2863SAndrew Thompson static void
220941e2863SAndrew Thompson tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg)
221941e2863SAndrew Thompson {
222941e2863SAndrew Thompson 	struct timer_entry *te, *te2, *te3;
223941e2863SAndrew Thompson 
224941e2863SAndrew Thompson 	te = malloc(sizeof(struct timer_entry));
225941e2863SAndrew Thompson 	memset(te, 0, sizeof(struct timer_entry));
226941e2863SAndrew Thompson 
227941e2863SAndrew Thompson 	te->timeout = timeout;
228941e2863SAndrew Thompson 	te->func = func;
229941e2863SAndrew Thompson 	te->arg = arg;
230941e2863SAndrew Thompson 	te->id = id;
231941e2863SAndrew Thompson 
232941e2863SAndrew Thompson 	te2 = TAILQ_FIRST(&tmrs->head);
233941e2863SAndrew Thompson 
234941e2863SAndrew Thompson 	if (TAILQ_EMPTY(&tmrs->head)) {
235941e2863SAndrew Thompson 		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
236941e2863SAndrew Thompson 	} else if (te->timeout < te2->timeout) {
237941e2863SAndrew Thompson 		te2->timeout -= te->timeout;
238941e2863SAndrew Thompson 		TAILQ_INSERT_HEAD(&tmrs->head, te, next);
239941e2863SAndrew Thompson 	} else {
240941e2863SAndrew Thompson 		while (te->timeout >= te2->timeout) {
241941e2863SAndrew Thompson 			te->timeout -= te2->timeout;
242941e2863SAndrew Thompson 			te3 = TAILQ_NEXT(te2, next);
243941e2863SAndrew Thompson 			if (te3 == NULL || te3->timeout > te->timeout)
244941e2863SAndrew Thompson 				break;
245941e2863SAndrew Thompson 			te2 = te3;
246941e2863SAndrew Thompson 		}
247941e2863SAndrew Thompson 		TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next);
248941e2863SAndrew Thompson 	}
249941e2863SAndrew Thompson }
250941e2863SAndrew Thompson 
251941e2863SAndrew Thompson #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG
252941e2863SAndrew Thompson #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG
253941e2863SAndrew Thompson 
254941e2863SAndrew Thompson static void
255941e2863SAndrew Thompson watchdog_reset(struct ctx *ctx, int timeout)
256941e2863SAndrew Thompson {
257941e2863SAndrew Thompson 	struct timespec tp;
258941e2863SAndrew Thompson 
259941e2863SAndrew Thompson 	clock_gettime(CLOCK_MONOTONIC, &tp),
260941e2863SAndrew Thompson 	ctx->watchdog = tp.tv_sec + timeout;
261941e2863SAndrew Thompson 
262941e2863SAndrew Thompson 	watchdog_enable(ctx);
263941e2863SAndrew Thompson }
264941e2863SAndrew Thompson 
265941e2863SAndrew Thompson static void
266941e2863SAndrew Thompson tmr_creg(int id, void *arg)
267941e2863SAndrew Thompson {
268941e2863SAndrew Thompson 	struct ctx *ctx = arg;
269941e2863SAndrew Thompson 
270941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+CREG?\r\n");
271941e2863SAndrew Thompson 	watchdog_reset(ctx, 10);
272941e2863SAndrew Thompson }
273941e2863SAndrew Thompson 
274941e2863SAndrew Thompson static void
275941e2863SAndrew Thompson tmr_cgreg(int id, void *arg)
276941e2863SAndrew Thompson {
277941e2863SAndrew Thompson 	struct ctx *ctx = arg;
278941e2863SAndrew Thompson 
279941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
280941e2863SAndrew Thompson 	watchdog_reset(ctx, 10);
281941e2863SAndrew Thompson }
282941e2863SAndrew Thompson 
283941e2863SAndrew Thompson static void
284941e2863SAndrew Thompson tmr_status(int id, void *arg)
285941e2863SAndrew Thompson {
286941e2863SAndrew Thompson 	struct ctx *ctx = arg;
287941e2863SAndrew Thompson 
288941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+CSQ\r\n");
289941e2863SAndrew Thompson 	watchdog_reset(ctx, 10);
290941e2863SAndrew Thompson }
291941e2863SAndrew Thompson 
292941e2863SAndrew Thompson static void
293941e2863SAndrew Thompson tmr_watchdog(int id, void *arg)
294941e2863SAndrew Thompson {
295941e2863SAndrew Thompson 	struct ctx *ctx = arg;
296941e2863SAndrew Thompson 	pid_t self;
297941e2863SAndrew Thompson 	struct timespec tp;
298941e2863SAndrew Thompson 
299941e2863SAndrew Thompson 	tmr_add(&timers, 1, 5, tmr_watchdog, ctx);
300941e2863SAndrew Thompson 
301941e2863SAndrew Thompson 	if (!(ctx->flags & FLG_WATCHDOG))
302941e2863SAndrew Thompson 		return;
303941e2863SAndrew Thompson 
304941e2863SAndrew Thompson 	clock_gettime(CLOCK_MONOTONIC, &tp);
305941e2863SAndrew Thompson 
306941e2863SAndrew Thompson 	if (tp.tv_sec >= ctx->watchdog) {
307941e2863SAndrew Thompson #ifdef DEBUG
308941e2863SAndrew Thompson 		fprintf(stderr, "Watchdog expired\n");
309941e2863SAndrew Thompson #endif
310941e2863SAndrew Thompson 		ctx->flags |= FLG_WDEXP;
311941e2863SAndrew Thompson 		self = getpid();
312941e2863SAndrew Thompson 		kill(self, SIGHUP);
313941e2863SAndrew Thompson 	}
314941e2863SAndrew Thompson }
315941e2863SAndrew Thompson 
316941e2863SAndrew Thompson static void
317941e2863SAndrew Thompson sig_handle(int sig)
318941e2863SAndrew Thompson {
319941e2863SAndrew Thompson 
320941e2863SAndrew Thompson 	switch (sig) {
321941e2863SAndrew Thompson 	case SIGHUP:
322941e2863SAndrew Thompson 	case SIGINT:
323941e2863SAndrew Thompson 	case SIGQUIT:
324941e2863SAndrew Thompson 	case SIGTERM:
325941e2863SAndrew Thompson 		running = 0;
326941e2863SAndrew Thompson 		break;
327941e2863SAndrew Thompson 	case SIGALRM:
328941e2863SAndrew Thompson 		tmr_run(&timers);
329941e2863SAndrew Thompson 		break;
330941e2863SAndrew Thompson 	}
331941e2863SAndrew Thompson }
332941e2863SAndrew Thompson 
333941e2863SAndrew Thompson static void
334941e2863SAndrew Thompson logger(int pri, const char *fmt, ...)
335941e2863SAndrew Thompson {
336941e2863SAndrew Thompson 	char *buf;
337941e2863SAndrew Thompson 	va_list ap;
338941e2863SAndrew Thompson 
339941e2863SAndrew Thompson 	va_start(ap, fmt);
340941e2863SAndrew Thompson 	vasprintf(&buf, fmt, ap);
341941e2863SAndrew Thompson 	if (syslog_open)
3425eeae9a7SDimitry Andric 		syslog(pri, "%s", buf);
343941e2863SAndrew Thompson 	else {
344941e2863SAndrew Thompson 		switch (pri) {
345941e2863SAndrew Thompson 		case LOG_INFO:
346941e2863SAndrew Thompson 		case LOG_NOTICE:
347941e2863SAndrew Thompson 			printf("%s\n", buf);
348941e2863SAndrew Thompson 			break;
349941e2863SAndrew Thompson 		default:
350941e2863SAndrew Thompson 			fprintf(stderr, "%s: %s\n", getprogname(), buf);
351941e2863SAndrew Thompson 			break;
352941e2863SAndrew Thompson 		}
353941e2863SAndrew Thompson 	}
354941e2863SAndrew Thompson 
355941e2863SAndrew Thompson 	free(buf);
356941e2863SAndrew Thompson 	va_end(ap);
357941e2863SAndrew Thompson }
358941e2863SAndrew Thompson 
359941e2863SAndrew Thompson #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP)
360941e2863SAndrew Thompson #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP)
361941e2863SAndrew Thompson 
362941e2863SAndrew Thompson static int
363941e2863SAndrew Thompson if_setflags(const char *ifnam, int flags)
364941e2863SAndrew Thompson {
365941e2863SAndrew Thompson 	struct ifreq ifr;
366941e2863SAndrew Thompson 	int fd, error;
367941e2863SAndrew Thompson 	unsigned int oflags = 0;
368941e2863SAndrew Thompson 
369941e2863SAndrew Thompson 	memset(&ifr, 0, sizeof(struct ifreq));
370941e2863SAndrew Thompson 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
371941e2863SAndrew Thompson 
372941e2863SAndrew Thompson 	fd = socket(AF_INET, SOCK_DGRAM, 0);
373941e2863SAndrew Thompson 	if (fd < 0)
374941e2863SAndrew Thompson 		return (-1);
375941e2863SAndrew Thompson 
376941e2863SAndrew Thompson 	error = ioctl(fd, SIOCGIFFLAGS, &ifr);
377941e2863SAndrew Thompson 	if (error == 0) {
378941e2863SAndrew Thompson 		oflags = (ifr.ifr_flags & 0xffff)  | (ifr.ifr_flagshigh << 16);
379941e2863SAndrew Thompson 	}
380941e2863SAndrew Thompson 
381941e2863SAndrew Thompson 	if (flags < 0)
382941e2863SAndrew Thompson 		oflags &= ~(-flags);
383941e2863SAndrew Thompson 	else
384941e2863SAndrew Thompson 		oflags |= flags;
385941e2863SAndrew Thompson 
386941e2863SAndrew Thompson 	ifr.ifr_flags = oflags & 0xffff;
387941e2863SAndrew Thompson 	ifr.ifr_flagshigh = oflags >> 16;
388941e2863SAndrew Thompson 
389941e2863SAndrew Thompson 	error = ioctl(fd, SIOCSIFFLAGS, &ifr);
390941e2863SAndrew Thompson 	if (error != 0)
391941e2863SAndrew Thompson 		warn("ioctl SIOCSIFFLAGS");
392941e2863SAndrew Thompson 
393941e2863SAndrew Thompson 	close(fd);
394941e2863SAndrew Thompson 	return (error);
395941e2863SAndrew Thompson }
396941e2863SAndrew Thompson 
397941e2863SAndrew Thompson static int
398941e2863SAndrew Thompson ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
399941e2863SAndrew Thompson {
400*c74a4ceaSKonrad Witaszczyk 	struct ifaliasreq ifra;
401*c74a4ceaSKonrad Witaszczyk 	int error, fd;
402941e2863SAndrew Thompson 
403*c74a4ceaSKonrad Witaszczyk 	memset(&ifra, 0, sizeof(ifra));
404*c74a4ceaSKonrad Witaszczyk 	strlcpy(ifra.ifra_name, ifnam, sizeof(ifra.ifra_name));
405*c74a4ceaSKonrad Witaszczyk 	memcpy(&ifra.ifra_addr, sa, sa->sa_len);
406*c74a4ceaSKonrad Witaszczyk 	memcpy(&ifra.ifra_mask, mask, mask->sa_len);
407*c74a4ceaSKonrad Witaszczyk 
408*c74a4ceaSKonrad Witaszczyk 	fd = socket(AF_INET, SOCK_DGRAM, 0);
409*c74a4ceaSKonrad Witaszczyk 	if (fd < 0)
410*c74a4ceaSKonrad Witaszczyk 		return (-1);
411*c74a4ceaSKonrad Witaszczyk 
412*c74a4ceaSKonrad Witaszczyk 	error = ioctl(fd, SIOCAIFADDR, (caddr_t)&ifra);
413941e2863SAndrew Thompson 	if (error != 0)
414941e2863SAndrew Thompson 		warn("ioctl SIOCAIFADDR");
415*c74a4ceaSKonrad Witaszczyk 
416*c74a4ceaSKonrad Witaszczyk 	close(fd);
417941e2863SAndrew Thompson 	return (error);
418941e2863SAndrew Thompson }
419941e2863SAndrew Thompson 
420941e2863SAndrew Thompson static int
421*c74a4ceaSKonrad Witaszczyk ifaddr_del(const char *ifnam, struct sockaddr *sa)
422941e2863SAndrew Thompson {
423*c74a4ceaSKonrad Witaszczyk 	struct ifreq ifr;
424*c74a4ceaSKonrad Witaszczyk 	int error, fd;
425941e2863SAndrew Thompson 
426*c74a4ceaSKonrad Witaszczyk 	memset(&ifr, 0, sizeof(ifr));
427*c74a4ceaSKonrad Witaszczyk 	strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
428*c74a4ceaSKonrad Witaszczyk 	memcpy(&ifr.ifr_addr, sa, sa->sa_len);
429*c74a4ceaSKonrad Witaszczyk 
430*c74a4ceaSKonrad Witaszczyk 	fd = socket(AF_INET, SOCK_DGRAM, 0);
431*c74a4ceaSKonrad Witaszczyk 	if (fd < 0)
432*c74a4ceaSKonrad Witaszczyk 		return (-1);
433*c74a4ceaSKonrad Witaszczyk 
434*c74a4ceaSKonrad Witaszczyk 	error = ioctl(fd, SIOCDIFADDR, (caddr_t)&ifr);
435941e2863SAndrew Thompson 	if (error != 0)
436941e2863SAndrew Thompson 		warn("ioctl SIOCDIFADDR");
437*c74a4ceaSKonrad Witaszczyk 
438*c74a4ceaSKonrad Witaszczyk 	close(fd);
439941e2863SAndrew Thompson 	return (error);
440941e2863SAndrew Thompson }
441941e2863SAndrew Thompson 
442941e2863SAndrew Thompson static int
443941e2863SAndrew Thompson set_nameservers(struct ctx *ctx, const char *respath, int ns, ...)
444941e2863SAndrew Thompson {
445941e2863SAndrew Thompson 	int i, n, fd;
446941e2863SAndrew Thompson 	FILE *fp;
447941e2863SAndrew Thompson 	char *p;
448941e2863SAndrew Thompson 	va_list ap;
449941e2863SAndrew Thompson 	struct stat sb;
450941e2863SAndrew Thompson 	char buf[512];
451941e2863SAndrew Thompson 
452941e2863SAndrew Thompson 	if (ctx->ns != NULL) {
453941e2863SAndrew Thompson 		for (i = 0; ctx->ns[i] != NULL; i++) {
454941e2863SAndrew Thompson 			free(ctx->ns[i]);
455941e2863SAndrew Thompson 		}
456941e2863SAndrew Thompson 		free(ctx->ns);
457e830a247SEnji Cooper 		ctx->ns = NULL;
458941e2863SAndrew Thompson 	}
459941e2863SAndrew Thompson 
4609a492fd1SPawel Jakub Dawidek 	fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666);
461941e2863SAndrew Thompson 	if (fd < 0)
462941e2863SAndrew Thompson 		return (-1);
463941e2863SAndrew Thompson 
464941e2863SAndrew Thompson 	if (ns == 0) {
465941e2863SAndrew Thompson 		/* Attempt to restore old resolv.conf */
466941e2863SAndrew Thompson 		if (ctx->resolv != NULL) {
467941e2863SAndrew Thompson 			ftruncate(fd, 0);
468941e2863SAndrew Thompson 			lseek(fd, 0, SEEK_SET);
469941e2863SAndrew Thompson 			write(fd, ctx->resolv, ctx->resolv_sz);
470941e2863SAndrew Thompson 			free(ctx->resolv);
471941e2863SAndrew Thompson 			ctx->resolv = NULL;
472941e2863SAndrew Thompson 			ctx->resolv_sz = 0;
473941e2863SAndrew Thompson 		}
474941e2863SAndrew Thompson 		close(fd);
475941e2863SAndrew Thompson 		return (0);
476941e2863SAndrew Thompson 	}
477941e2863SAndrew Thompson 
478941e2863SAndrew Thompson 
479941e2863SAndrew Thompson 	ctx->ns = malloc(sizeof(char *) * (ns + 1));
480941e2863SAndrew Thompson 	if (ctx->ns == NULL) {
481941e2863SAndrew Thompson 		close(fd);
482941e2863SAndrew Thompson 		return (-1);
483941e2863SAndrew Thompson 	}
484941e2863SAndrew Thompson 
485941e2863SAndrew Thompson 	va_start(ap, ns);
486941e2863SAndrew Thompson 	for (i = 0; i < ns; i++) {
487941e2863SAndrew Thompson 		p = va_arg(ap, char *);
488941e2863SAndrew Thompson 		ctx->ns[i] = strdup(p);
489941e2863SAndrew Thompson 	}
490941e2863SAndrew Thompson 	ctx->ns[i] = NULL;
491941e2863SAndrew Thompson 	va_end(ap);
492941e2863SAndrew Thompson 
493941e2863SAndrew Thompson 	/* Attempt to backup the old resolv.conf */
494941e2863SAndrew Thompson 	if (ctx->resolv == NULL) {
495941e2863SAndrew Thompson 		i = fstat(fd, &sb);
496941e2863SAndrew Thompson 		if (i == 0 && sb.st_size != 0) {
497941e2863SAndrew Thompson 			ctx->resolv_sz = sb.st_size;
498941e2863SAndrew Thompson 			ctx->resolv = malloc(sb.st_size);
499941e2863SAndrew Thompson 			if (ctx->resolv != NULL) {
500941e2863SAndrew Thompson 				n = read(fd, ctx->resolv, sb.st_size);
501941e2863SAndrew Thompson 				if (n != sb.st_size) {
502941e2863SAndrew Thompson 					free(ctx->resolv);
503941e2863SAndrew Thompson 					ctx->resolv = NULL;
504941e2863SAndrew Thompson 				}
505941e2863SAndrew Thompson 			}
506941e2863SAndrew Thompson 		}
507941e2863SAndrew Thompson 	}
508941e2863SAndrew Thompson 
509941e2863SAndrew Thompson 
510941e2863SAndrew Thompson 	ftruncate(fd, 0);
511941e2863SAndrew Thompson 	lseek(fd, 0, SEEK_SET);
512941e2863SAndrew Thompson 	fp = fdopen(fd, "w");
513941e2863SAndrew Thompson 
514941e2863SAndrew Thompson 	/*
515941e2863SAndrew Thompson 	 * Write back everything other than nameserver entries to the
516941e2863SAndrew Thompson 	 * new resolv.conf
517941e2863SAndrew Thompson 	 */
518941e2863SAndrew Thompson 	if (ctx->resolv != NULL) {
519941e2863SAndrew Thompson 		p = ctx->resolv;
520941e2863SAndrew Thompson 		while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf,
521941e2863SAndrew Thompson 		    sizeof(buf))) > 0) {
522941e2863SAndrew Thompson 			p += i;
523941e2863SAndrew Thompson 			if (strncasecmp(buf, "nameserver", 10) == 0)
524941e2863SAndrew Thompson 				continue;
525941e2863SAndrew Thompson 			fprintf(fp, "%s", buf);
526941e2863SAndrew Thompson 		}
527941e2863SAndrew Thompson 	}
528941e2863SAndrew Thompson 
529941e2863SAndrew Thompson 	for (i = 0; ctx->ns[i] != NULL; i++) {
530941e2863SAndrew Thompson 		fprintf(fp, "nameserver %s\n", ctx->ns[i]);
531941e2863SAndrew Thompson 	}
532941e2863SAndrew Thompson 	fclose(fp);
533941e2863SAndrew Thompson 	return (0);
534941e2863SAndrew Thompson }
535941e2863SAndrew Thompson 
536941e2863SAndrew Thompson /* Read a \n-terminated line from buffer */
537941e2863SAndrew Thompson static int
538941e2863SAndrew Thompson readline_buf(const char *s, const char *e, char *buf, size_t bufsz)
539941e2863SAndrew Thompson {
540941e2863SAndrew Thompson 	int pos = 0;
541941e2863SAndrew Thompson 	char *p = buf;
542941e2863SAndrew Thompson 
543941e2863SAndrew Thompson 	for (; s < e; s++) {
544941e2863SAndrew Thompson 		*p = *s;
545941e2863SAndrew Thompson 		pos++;
546941e2863SAndrew Thompson 		if (pos >= (bufsz - 1))
547941e2863SAndrew Thompson 			break;
548941e2863SAndrew Thompson 		if (*p++ == '\n')
549941e2863SAndrew Thompson 			break;
550941e2863SAndrew Thompson 	}
551941e2863SAndrew Thompson 	*p = '\0';
552941e2863SAndrew Thompson 	return (pos);
553941e2863SAndrew Thompson }
554941e2863SAndrew Thompson 
555941e2863SAndrew Thompson /* Read a \n-terminated line from file */
556941e2863SAndrew Thompson static int
557941e2863SAndrew Thompson readline(int fd, char *buf, size_t bufsz)
558941e2863SAndrew Thompson {
559941e2863SAndrew Thompson 	int n = 0, pos = 0;
560941e2863SAndrew Thompson 	char *p = buf;
561941e2863SAndrew Thompson 
562941e2863SAndrew Thompson 	for (;;) {
563941e2863SAndrew Thompson 		n = read(fd, p, 1);
564941e2863SAndrew Thompson 		if (n <= 0)
565941e2863SAndrew Thompson 			break;
566941e2863SAndrew Thompson 		pos++;
567941e2863SAndrew Thompson 		if (pos >= (bufsz - 1))
568941e2863SAndrew Thompson 			break;
569941e2863SAndrew Thompson 		if (*p++ == '\n')
570941e2863SAndrew Thompson 			break;
571941e2863SAndrew Thompson 	}
572941e2863SAndrew Thompson 	*p = '\0';
573941e2863SAndrew Thompson 	return (n <= 0 ? n : pos);
574941e2863SAndrew Thompson }
575941e2863SAndrew Thompson 
576941e2863SAndrew Thompson /*
577941e2863SAndrew Thompson  * Synchronous AT command
578941e2863SAndrew Thompson  */
579941e2863SAndrew Thompson static int
580941e2863SAndrew Thompson at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...)
581941e2863SAndrew Thompson {
582eea19fceSAndrew Thompson 	char buf[512];
583eea19fceSAndrew Thompson 	char cmd[64];
584941e2863SAndrew Thompson 	size_t l;
585941e2863SAndrew Thompson 	int n, error, retval = 0;
586941e2863SAndrew Thompson 	va_list ap;
587941e2863SAndrew Thompson 	fd_set set;
588eea19fceSAndrew Thompson 	char *p;
589941e2863SAndrew Thompson 
590941e2863SAndrew Thompson 	va_start(ap, cf);
591941e2863SAndrew Thompson 	vsnprintf(cmd, sizeof(cmd), cf, ap);
592941e2863SAndrew Thompson 	va_end(ap);
593941e2863SAndrew Thompson 
594941e2863SAndrew Thompson #ifdef DEBUG
595941e2863SAndrew Thompson 	fprintf(stderr, "SYNC_CMD: %s", cmd);
596941e2863SAndrew Thompson #endif
597941e2863SAndrew Thompson 
598941e2863SAndrew Thompson 	l = strlen(cmd);
599941e2863SAndrew Thompson 	n = write(ctx->fd, cmd, l);
600941e2863SAndrew Thompson 	if (n <= 0)
601941e2863SAndrew Thompson 		return (-1);
602941e2863SAndrew Thompson 
603941e2863SAndrew Thompson 	if (resp != NULL) {
604941e2863SAndrew Thompson 		l = strlen(resp);
605941e2863SAndrew Thompson #ifdef DEBUG
606c57440ecSGavin Atkinson 		fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l);
607941e2863SAndrew Thompson #endif
608941e2863SAndrew Thompson 	}
609941e2863SAndrew Thompson 
610941e2863SAndrew Thompson 	for (;;) {
611941e2863SAndrew Thompson 		bzero(buf, sizeof(buf));
612941e2863SAndrew Thompson 
613941e2863SAndrew Thompson 		FD_ZERO(&set);
614941e2863SAndrew Thompson 		watchdog_reset(ctx, 5);
615941e2863SAndrew Thompson 		do {
616941e2863SAndrew Thompson 			FD_SET(ctx->fd, &set);
617941e2863SAndrew Thompson 			error = select(ctx->fd + 1, &set, NULL, NULL, NULL);
618eea19fceSAndrew Thompson 			if (ctx->flags & FLG_WDEXP) {
619941e2863SAndrew Thompson 				watchdog_disable(ctx);
620eea19fceSAndrew Thompson 				return (-2);
621941e2863SAndrew Thompson 			}
622941e2863SAndrew Thompson 		} while (error <= 0 && errno == EINTR);
623eea19fceSAndrew Thompson 		watchdog_disable(ctx);
624941e2863SAndrew Thompson 
625941e2863SAndrew Thompson 		if (error <= 0) {
626941e2863SAndrew Thompson 			retval = -2;
627941e2863SAndrew Thompson 			break;
628941e2863SAndrew Thompson 		}
629941e2863SAndrew Thompson 
630941e2863SAndrew Thompson 		n = readline(ctx->fd, buf, sizeof(buf));
631941e2863SAndrew Thompson 		if (n <= 0) {
632941e2863SAndrew Thompson 			retval = -2;
633941e2863SAndrew Thompson 			break;
634941e2863SAndrew Thompson 		}
635941e2863SAndrew Thompson 
636941e2863SAndrew Thompson 		if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0)
637941e2863SAndrew Thompson 			continue;
638941e2863SAndrew Thompson 
639eea19fceSAndrew Thompson 		if ((p = strchr(buf, '\r')) != NULL)
640eea19fceSAndrew Thompson 			*p = '\0';
641eea19fceSAndrew Thompson 		else if ((p = strchr(buf, '\n')) != NULL)
642eea19fceSAndrew Thompson 			*p = '\0';
643941e2863SAndrew Thompson #ifdef DEBUG
644eea19fceSAndrew Thompson 		fprintf(stderr, "SYNC_RESP: %s\n", buf);
645941e2863SAndrew Thompson #endif
646941e2863SAndrew Thompson 
647eea19fceSAndrew Thompson 		/* Skip local echo */
648eea19fceSAndrew Thompson 		if (strncasecmp(cmd, buf, strlen(buf)) == 0)
649eea19fceSAndrew Thompson 			continue;
650eea19fceSAndrew Thompson 
651eea19fceSAndrew Thompson 		if (cb != NULL)
652eea19fceSAndrew Thompson 			cb(ra, cmd, buf);
653eea19fceSAndrew Thompson 
654941e2863SAndrew Thompson 		if (strncmp(buf, "OK", 2) == 0) {
655eea19fceSAndrew Thompson 			retval = retval ? retval : 0;
656941e2863SAndrew Thompson 			break;
657eea19fceSAndrew Thompson 		} else if (strstr(buf, "ERROR") != NULL) {
658941e2863SAndrew Thompson 			retval = -1;
659941e2863SAndrew Thompson 			break;
660941e2863SAndrew Thompson 		}
661eea19fceSAndrew Thompson 		if (resp != NULL)
662eea19fceSAndrew Thompson 			retval = strncmp(buf, resp, l);
663941e2863SAndrew Thompson 	}
664941e2863SAndrew Thompson #ifdef DEBUG
665941e2863SAndrew Thompson 	fprintf(stderr, "SYNC_RETVAL=%d\n", retval);
666941e2863SAndrew Thompson #endif
667941e2863SAndrew Thompson 	return (retval);
668941e2863SAndrew Thompson }
669941e2863SAndrew Thompson 
670941e2863SAndrew Thompson static int
671941e2863SAndrew Thompson at_cmd_async(int fd, const char *cf, ...)
672941e2863SAndrew Thompson {
673941e2863SAndrew Thompson 	size_t l;
674941e2863SAndrew Thompson 	va_list ap;
675941e2863SAndrew Thompson 	char cmd[64];
676941e2863SAndrew Thompson 
677941e2863SAndrew Thompson 	va_start(ap, cf);
678941e2863SAndrew Thompson 	vsnprintf(cmd, sizeof(cmd), cf, ap);
679941e2863SAndrew Thompson 	va_end(ap);
680941e2863SAndrew Thompson 
681941e2863SAndrew Thompson #ifdef DEBUG
682941e2863SAndrew Thompson 	fprintf(stderr, "CMD: %s", cmd);
683941e2863SAndrew Thompson #endif
684941e2863SAndrew Thompson 	l = strlen(cmd);
685941e2863SAndrew Thompson 	return (write(fd, cmd, l));
686941e2863SAndrew Thompson }
687941e2863SAndrew Thompson 
688941e2863SAndrew Thompson static void
689941e2863SAndrew Thompson saveresp(resp_arg *ra, const char *cmd, const char *resp)
690941e2863SAndrew Thompson {
691941e2863SAndrew Thompson 	char **buf;
692941e2863SAndrew Thompson 	int i = ra->val[1].int32;
693941e2863SAndrew Thompson 
694eea19fceSAndrew Thompson #ifdef DEBUG
695eea19fceSAndrew Thompson 	fprintf(stderr, "Save '%s'\n", resp);
696eea19fceSAndrew Thompson #endif
697eea19fceSAndrew Thompson 
698941e2863SAndrew Thompson 	buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1));
699941e2863SAndrew Thompson 	if (buf == NULL)
700941e2863SAndrew Thompson 		return;
701941e2863SAndrew Thompson 
702941e2863SAndrew Thompson 	buf[i] = strdup(resp);
703941e2863SAndrew Thompson 
704941e2863SAndrew Thompson 	ra->val[0].ptr = buf;
705941e2863SAndrew Thompson 	ra->val[1].int32 = i + 1;
706941e2863SAndrew Thompson }
707941e2863SAndrew Thompson 
708941e2863SAndrew Thompson static void
709eea19fceSAndrew Thompson freeresp(resp_arg *ra)
710eea19fceSAndrew Thompson {
711eea19fceSAndrew Thompson 	char **buf;
712eea19fceSAndrew Thompson 	int i;
713eea19fceSAndrew Thompson 
714eea19fceSAndrew Thompson 	buf = ra->val[0].ptr;
715eea19fceSAndrew Thompson 	for (i = 0; i < ra->val[1].int32; i++) {
716eea19fceSAndrew Thompson 		free(buf[i]);
717eea19fceSAndrew Thompson 	}
718eea19fceSAndrew Thompson 	free(buf);
719eea19fceSAndrew Thompson }
720eea19fceSAndrew Thompson 
721eea19fceSAndrew Thompson static void
722941e2863SAndrew Thompson at_async_creg(void *arg, const char *resp)
723941e2863SAndrew Thompson {
724941e2863SAndrew Thompson 	struct ctx *ctx = arg;
725941e2863SAndrew Thompson 	int n, reg;
726941e2863SAndrew Thompson 
727941e2863SAndrew Thompson 	n = sscanf(resp, "+CREG: %*d,%d", &reg);
728941e2863SAndrew Thompson 	if (n != 1) {
729941e2863SAndrew Thompson 		n = sscanf(resp, "+CREG: %d", &reg);
730941e2863SAndrew Thompson 		if (n != 1)
731941e2863SAndrew Thompson 			return;
732941e2863SAndrew Thompson 	}
733941e2863SAndrew Thompson 
734941e2863SAndrew Thompson 	if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
735941e2863SAndrew Thompson 		tmr_add(&timers, 1, 1, tmr_creg, ctx);
736941e2863SAndrew Thompson 	}
737941e2863SAndrew Thompson 	else {
738941e2863SAndrew Thompson 		tmr_add(&timers, 1, 30, tmr_creg, ctx);
739941e2863SAndrew Thompson 	}
740941e2863SAndrew Thompson 
741941e2863SAndrew Thompson 	if (ctx->con_net_stat == reg)
742941e2863SAndrew Thompson 		return;
743941e2863SAndrew Thompson 
744941e2863SAndrew Thompson 	ctx->con_net_stat = reg;
745941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+COPS?\r\n");
746941e2863SAndrew Thompson }
747941e2863SAndrew Thompson 
748941e2863SAndrew Thompson static void
749941e2863SAndrew Thompson at_async_cgreg(void *arg, const char *resp)
750941e2863SAndrew Thompson {
751941e2863SAndrew Thompson 	struct ctx *ctx = arg;
752941e2863SAndrew Thompson 	int n, reg;
753941e2863SAndrew Thompson 
754941e2863SAndrew Thompson 	n = sscanf(resp, "+CGREG: %*d,%d", &reg);
755941e2863SAndrew Thompson 	if (n != 1) {
756941e2863SAndrew Thompson 		n = sscanf(resp, "+CGREG: %d", &reg);
757941e2863SAndrew Thompson 		if (n != 1)
758941e2863SAndrew Thompson 			return;
759941e2863SAndrew Thompson 	}
760941e2863SAndrew Thompson 
761941e2863SAndrew Thompson 	if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
762941e2863SAndrew Thompson 		tmr_add(&timers, 1, 1, tmr_cgreg, ctx);
763941e2863SAndrew Thompson 	}
764941e2863SAndrew Thompson 	else {
765941e2863SAndrew Thompson 		tmr_add(&timers, 1, 30, tmr_cgreg, ctx);
766941e2863SAndrew Thompson 	}
767941e2863SAndrew Thompson 
768941e2863SAndrew Thompson 	if (ctx->con_net_stat == reg)
769941e2863SAndrew Thompson 		return;
770941e2863SAndrew Thompson 
771941e2863SAndrew Thompson 	ctx->con_net_stat = reg;
772941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+COPS?\r\n");
773941e2863SAndrew Thompson }
774941e2863SAndrew Thompson 
775941e2863SAndrew Thompson 
776941e2863SAndrew Thompson static void
777941e2863SAndrew Thompson at_async_cops(void *arg, const char *resp)
778941e2863SAndrew Thompson {
779941e2863SAndrew Thompson 	struct ctx *ctx = arg;
780941e2863SAndrew Thompson 	int n, at;
781941e2863SAndrew Thompson 	char opr[64];
782941e2863SAndrew Thompson 
783941e2863SAndrew Thompson 	n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d",
784941e2863SAndrew Thompson 	    opr, &at);
785941e2863SAndrew Thompson 	if (n != 2)
786941e2863SAndrew Thompson 		return;
787941e2863SAndrew Thompson 
788941e2863SAndrew Thompson 	if (ctx->con_oper != NULL) {
789941e2863SAndrew Thompson 		if (ctx->con_net_type == at &&
790941e2863SAndrew Thompson 		    strcasecmp(opr, ctx->con_oper) == 0)
791941e2863SAndrew Thompson 			return;
792941e2863SAndrew Thompson 		free(ctx->con_oper);
793941e2863SAndrew Thompson 	}
794941e2863SAndrew Thompson 
795941e2863SAndrew Thompson 	ctx->con_oper = strdup(opr);
796941e2863SAndrew Thompson 	ctx->con_net_type = at;
797941e2863SAndrew Thompson 
798941e2863SAndrew Thompson 	if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) {
799941e2863SAndrew Thompson 		logger(LOG_NOTICE, "%s to \"%s\" (%s)",
800941e2863SAndrew Thompson 		    network_reg_status[ctx->con_net_stat],
801941e2863SAndrew Thompson 		    ctx->con_oper, network_access_type[ctx->con_net_type]);
802941e2863SAndrew Thompson 		if (ctx->con_status != 1) {
803941e2863SAndrew Thompson 			at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n",
804941e2863SAndrew Thompson 			    ctx->pdp_ctx);
805941e2863SAndrew Thompson 		}
806941e2863SAndrew Thompson 	}
807941e2863SAndrew Thompson 	else {
808941e2863SAndrew Thompson 		logger(LOG_NOTICE, "%s (%s)",
809941e2863SAndrew Thompson 		    network_reg_status[ctx->con_net_stat],
810941e2863SAndrew Thompson 		    network_access_type[ctx->con_net_type]);
811941e2863SAndrew Thompson 	}
812941e2863SAndrew Thompson }
813941e2863SAndrew Thompson 
814941e2863SAndrew Thompson /*
815941e2863SAndrew Thompson  * Signal strength for pretty console output
816941e2863SAndrew Thompson  *
817941e2863SAndrew Thompson  * From 3GPP TS 27.007 V8.3.0, Section 8.5
818941e2863SAndrew Thompson  * 0 = -113 dBm or less
819941e2863SAndrew Thompson  * 1  = -111 dBm
820941e2863SAndrew Thompson  * 2...30 = -109...-53 dBm
821941e2863SAndrew Thompson  * 31 = -51 dBm or greater
822941e2863SAndrew Thompson  *
823941e2863SAndrew Thompson  * So, dbm = (rssi * 2) - 113
824941e2863SAndrew Thompson */
825941e2863SAndrew Thompson static void
826941e2863SAndrew Thompson at_async_csq(void *arg, const char *resp)
827941e2863SAndrew Thompson {
828941e2863SAndrew Thompson 	struct ctx *ctx = arg;
829941e2863SAndrew Thompson 	int n, rssi;
830941e2863SAndrew Thompson 
831941e2863SAndrew Thompson 	n = sscanf(resp, "+CSQ: %d,%*d", &rssi);
832941e2863SAndrew Thompson 	if (n != 1)
833941e2863SAndrew Thompson 		return;
834941e2863SAndrew Thompson 	if (rssi == 99)
835941e2863SAndrew Thompson 		ctx->dbm = 0;
836941e2863SAndrew Thompson 	else {
837941e2863SAndrew Thompson 		ctx->dbm = (rssi * 2) - 113;
838941e2863SAndrew Thompson 		tmr_add(&timers, 1, 15, tmr_status, ctx);
839941e2863SAndrew Thompson 	}
840941e2863SAndrew Thompson 
841941e2863SAndrew Thompson 	ctx->flags |= FLG_NEWDATA;
842941e2863SAndrew Thompson }
843941e2863SAndrew Thompson 
844941e2863SAndrew Thompson static void
845941e2863SAndrew Thompson at_async_owancall(void *arg, const char *resp)
846941e2863SAndrew Thompson {
847941e2863SAndrew Thompson 	struct ctx *ctx = arg;
848941e2863SAndrew Thompson 	int n, i;
849941e2863SAndrew Thompson 
850941e2863SAndrew Thompson 	n = sscanf(resp, "_OWANCALL: %*d,%d", &i);
851941e2863SAndrew Thompson 	if (n != 1)
852941e2863SAndrew Thompson 		return;
853941e2863SAndrew Thompson 
854941e2863SAndrew Thompson 	if (i == ctx->con_status)
855941e2863SAndrew Thompson 		return;
856941e2863SAndrew Thompson 
857941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx);
858941e2863SAndrew Thompson 
859941e2863SAndrew Thompson 	ctx->con_status = i;
860941e2863SAndrew Thompson 	if (ctx->con_status == 1) {
861941e2863SAndrew Thompson 		logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s",
862941e2863SAndrew Thompson 		    ctx->con_oper, ctx->con_apn,
863941e2863SAndrew Thompson 		    network_access_type[ctx->con_net_type]);
864941e2863SAndrew Thompson 	}
865941e2863SAndrew Thompson 	else {
866941e2863SAndrew Thompson 		logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)",
867941e2863SAndrew Thompson 		    ctx->con_oper, ctx->con_apn);
868941e2863SAndrew Thompson 	}
869941e2863SAndrew Thompson }
870941e2863SAndrew Thompson 
871941e2863SAndrew Thompson static void
872941e2863SAndrew Thompson at_async_owandata(void *arg, const char *resp)
873941e2863SAndrew Thompson {
874941e2863SAndrew Thompson 	struct ctx *ctx = arg;
875941e2863SAndrew Thompson 	char ip[40], ns1[40], ns2[40];
876941e2863SAndrew Thompson 	int n, error, rs;
877941e2863SAndrew Thompson 	struct ifaddrs *ifap, *ifa;
878941e2863SAndrew Thompson 	struct sockaddr_in sin, mask;
879941e2863SAndrew Thompson 	struct sockaddr_dl sdl;
880941e2863SAndrew Thompson 	struct {
881941e2863SAndrew Thompson 		struct rt_msghdr rtm;
882941e2863SAndrew Thompson 		char buf[512];
883941e2863SAndrew Thompson 	} r;
884941e2863SAndrew Thompson 	char *cp = r.buf;
885941e2863SAndrew Thompson 
886941e2863SAndrew Thompson 	n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]",
887941e2863SAndrew Thompson 	    ip, ns1, ns2);
888941e2863SAndrew Thompson 	if (n != 3)
889941e2863SAndrew Thompson 		return;
890941e2863SAndrew Thompson 
891941e2863SAndrew Thompson 	/* XXX: AF_INET assumption */
892941e2863SAndrew Thompson 
893941e2863SAndrew Thompson 	logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2);
894941e2863SAndrew Thompson 
895941e2863SAndrew Thompson 	sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in);
896941e2863SAndrew Thompson 	memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr));
897941e2863SAndrew Thompson 	sin.sin_family = mask.sin_family = AF_INET;
898941e2863SAndrew Thompson 
899941e2863SAndrew Thompson 	if (ctx->flags & IPASSIGNED) {
900941e2863SAndrew Thompson 		memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
901941e2863SAndrew Thompson 		    sizeof(sin.sin_addr.s_addr));
902*c74a4ceaSKonrad Witaszczyk 		ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin);
903941e2863SAndrew Thompson 	}
904941e2863SAndrew Thompson 	inet_pton(AF_INET, ip, &ctx->ip.s_addr);
905941e2863SAndrew Thompson 	memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
906941e2863SAndrew Thompson 	    sizeof(sin.sin_addr.s_addr));
907941e2863SAndrew Thompson 
908941e2863SAndrew Thompson 	error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin,
909941e2863SAndrew Thompson 	    (struct sockaddr *)&mask);
910941e2863SAndrew Thompson 	if (error != 0) {
911941e2863SAndrew Thompson 		logger(LOG_ERR, "failed to set ip-address");
912941e2863SAndrew Thompson 		return;
913941e2863SAndrew Thompson 	}
914941e2863SAndrew Thompson 
915941e2863SAndrew Thompson 	if_ifup(ctx->ifnam);
916941e2863SAndrew Thompson 
917941e2863SAndrew Thompson 	ctx->flags |= IPASSIGNED;
918941e2863SAndrew Thompson 
919941e2863SAndrew Thompson 	set_nameservers(ctx, ctx->resolv_path, 0);
920941e2863SAndrew Thompson 	error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2);
921941e2863SAndrew Thompson 	if (error != 0) {
922941e2863SAndrew Thompson 		logger(LOG_ERR, "failed to set nameservers");
923941e2863SAndrew Thompson 	}
924941e2863SAndrew Thompson 
925941e2863SAndrew Thompson 	error = getifaddrs(&ifap);
926941e2863SAndrew Thompson 	if (error != 0) {
927941e2863SAndrew Thompson 		logger(LOG_ERR, "getifaddrs: %s", strerror(errno));
928941e2863SAndrew Thompson 		return;
929941e2863SAndrew Thompson 	}
930941e2863SAndrew Thompson 
931941e2863SAndrew Thompson 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
932941e2863SAndrew Thompson 		if (ifa->ifa_addr->sa_family != AF_LINK)
933941e2863SAndrew Thompson 			continue;
934941e2863SAndrew Thompson 		if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) {
935941e2863SAndrew Thompson 			memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr,
936941e2863SAndrew Thompson 			    sizeof(struct sockaddr_dl));
937941e2863SAndrew Thompson 			break;
938941e2863SAndrew Thompson 		}
939941e2863SAndrew Thompson 	}
940941e2863SAndrew Thompson 	if (ifa == NULL)
941941e2863SAndrew Thompson 		return;
942941e2863SAndrew Thompson 
943941e2863SAndrew Thompson 	rs = socket(PF_ROUTE, SOCK_RAW, 0);
944941e2863SAndrew Thompson 	if (rs < 0) {
945941e2863SAndrew Thompson 		logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno));
946941e2863SAndrew Thompson 		return;
947941e2863SAndrew Thompson 	}
948941e2863SAndrew Thompson 
949941e2863SAndrew Thompson 	memset(&r, 0, sizeof(r));
950941e2863SAndrew Thompson 
951941e2863SAndrew Thompson 	r.rtm.rtm_version = RTM_VERSION;
952941e2863SAndrew Thompson 	r.rtm.rtm_type = RTM_ADD;
953941e2863SAndrew Thompson 	r.rtm.rtm_flags = RTF_UP | RTF_STATIC;
954941e2863SAndrew Thompson 	r.rtm.rtm_pid = getpid();
955941e2863SAndrew Thompson 	memset(&sin, 0, sizeof(struct sockaddr_in));
956941e2863SAndrew Thompson 	sin.sin_family = AF_INET;
957941e2863SAndrew Thompson 	sin.sin_len = sizeof(struct sockaddr_in);
958941e2863SAndrew Thompson 
959941e2863SAndrew Thompson 	memcpy(cp, &sin, sin.sin_len);
960941e2863SAndrew Thompson 	cp += SA_SIZE(&sin);
961941e2863SAndrew Thompson 	memcpy(cp, &sdl, sdl.sdl_len);
962941e2863SAndrew Thompson 	cp += SA_SIZE(&sdl);
963941e2863SAndrew Thompson 	memcpy(cp, &sin, sin.sin_len);
964941e2863SAndrew Thompson 	r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
965941e2863SAndrew Thompson 	r.rtm.rtm_msglen = sizeof(r);
966941e2863SAndrew Thompson 
967941e2863SAndrew Thompson 	n = write(rs, &r, r.rtm.rtm_msglen);
968941e2863SAndrew Thompson 	if (n != r.rtm.rtm_msglen) {
969941e2863SAndrew Thompson 		r.rtm.rtm_type = RTM_DELETE;
970941e2863SAndrew Thompson 		n = write(rs, &r, r.rtm.rtm_msglen);
971941e2863SAndrew Thompson 		r.rtm.rtm_type = RTM_ADD;
972941e2863SAndrew Thompson 		n = write(rs, &r, r.rtm.rtm_msglen);
973941e2863SAndrew Thompson 	}
974941e2863SAndrew Thompson 
975941e2863SAndrew Thompson 	if (n != r.rtm.rtm_msglen) {
976941e2863SAndrew Thompson 		logger(LOG_ERR, "failed to set default route: %s",
977941e2863SAndrew Thompson 		    strerror(errno));
978941e2863SAndrew Thompson 	}
979941e2863SAndrew Thompson 	close(rs);
980941e2863SAndrew Thompson 
981941e2863SAndrew Thompson 	/* Delayed daemonization */
982941e2863SAndrew Thompson 	if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON))
983941e2863SAndrew Thompson 		daemonize(ctx);
984941e2863SAndrew Thompson }
985941e2863SAndrew Thompson 
986941e2863SAndrew Thompson static int
987941e2863SAndrew Thompson at_async(struct ctx *ctx, void *arg)
988941e2863SAndrew Thompson {
989941e2863SAndrew Thompson 	int n, i;
990941e2863SAndrew Thompson 	size_t l;
991941e2863SAndrew Thompson 	char buf[512];
992941e2863SAndrew Thompson 
993941e2863SAndrew Thompson 	watchdog_reset(ctx, 15);
994941e2863SAndrew Thompson 
995941e2863SAndrew Thompson 	bzero(buf, sizeof(buf));
996941e2863SAndrew Thompson 	n = readline(ctx->fd, buf, sizeof(buf));
997941e2863SAndrew Thompson 	if (n <= 0)
998941e2863SAndrew Thompson 		return (n <= 0 ? -1 : 0);
999941e2863SAndrew Thompson 
1000941e2863SAndrew Thompson #ifdef DEBUG
1001941e2863SAndrew Thompson 	fprintf(stderr, "AT_ASYNC_RESP: %s", buf);
1002941e2863SAndrew Thompson #endif
1003941e2863SAndrew Thompson 	for (i = 0; async_cmd[i].cmd != NULL; i++) {
1004941e2863SAndrew Thompson 		l = strlen(async_cmd[i].cmd);
1005941e2863SAndrew Thompson 		if (strncmp(buf, async_cmd[i].cmd, l) == 0) {
1006941e2863SAndrew Thompson 			async_cmd[i].func(arg, buf);
1007941e2863SAndrew Thompson 		}
1008941e2863SAndrew Thompson 	}
1009941e2863SAndrew Thompson 	return (0);
1010941e2863SAndrew Thompson }
1011941e2863SAndrew Thompson 
1012941e2863SAndrew Thompson static const char *port_type_list[] = {
1013941e2863SAndrew Thompson 	"control", "application", "application2", NULL
1014941e2863SAndrew Thompson };
1015941e2863SAndrew Thompson 
1016941e2863SAndrew Thompson /*
1017941e2863SAndrew Thompson  * Attempts to find a list of control tty for the interface
1018eea19fceSAndrew Thompson  * FreeBSD attaches USB devices per interface so we have to go through
1019941e2863SAndrew Thompson  * hoops to find which ttys that belong to our network interface.
1020941e2863SAndrew Thompson  */
1021941e2863SAndrew Thompson static char **
1022941e2863SAndrew Thompson get_tty(struct ctx *ctx)
1023941e2863SAndrew Thompson {
1024eea19fceSAndrew Thompson 	char buf[64], data[128];
1025eea19fceSAndrew Thompson 	int error, i, usbport, usbport0, list_size = 0;
1026941e2863SAndrew Thompson 	char **list = NULL;
1027eea19fceSAndrew Thompson 	size_t len;
1028eea19fceSAndrew Thompson 	const char **p, *q;
1029941e2863SAndrew Thompson 
1030eea19fceSAndrew Thompson 	/*
1031eea19fceSAndrew Thompson 	 * Look for the network interface first
1032eea19fceSAndrew Thompson 	 */
1033941e2863SAndrew Thompson 	for (i = 0; ; i++) {
1034eea19fceSAndrew Thompson 		/* Check if we still have uhso nodes to check */
1035941e2863SAndrew Thompson 		snprintf(buf, 64, SYSCTL_TEST, i);
1036941e2863SAndrew Thompson 		len = 127;
1037941e2863SAndrew Thompson 		error = sysctlbyname(buf, data, &len, NULL, 0);
1038eea19fceSAndrew Thompson 		data[len] = '\0';
1039941e2863SAndrew Thompson #ifdef DEBUG
1040941e2863SAndrew Thompson 		fprintf(stderr, "sysctl %s returned(%d): %s\n",
1041941e2863SAndrew Thompson 		    buf, error, error == 0 ? data : "FAILED");
1042941e2863SAndrew Thompson #endif
1043eea19fceSAndrew Thompson 		if (error < 0 || strcasecmp(data, "uhso") != 0)
1044941e2863SAndrew Thompson 			return NULL;
1045941e2863SAndrew Thompson 
1046eea19fceSAndrew Thompson 		/* Check if this node contains the network interface we want */
1047941e2863SAndrew Thompson 		snprintf(buf, 64, SYSCTL_NETIF, i);
1048941e2863SAndrew Thompson 		len = 127;
1049941e2863SAndrew Thompson 		error = sysctlbyname(buf, data, &len, NULL, 0);
1050eea19fceSAndrew Thompson 		data[len] = '\0';
1051941e2863SAndrew Thompson #ifdef DEBUG
1052941e2863SAndrew Thompson 		fprintf(stderr, "sysctl %s returned(%d): %s\n",
1053941e2863SAndrew Thompson 		    buf, error, error == 0 ? data : "FAILED");
1054941e2863SAndrew Thompson #endif
1055eea19fceSAndrew Thompson 		if (error == 0 && strcasecmp(data, ctx->ifnam) == 0)
1056941e2863SAndrew Thompson 			break;
1057941e2863SAndrew Thompson 	}
1058941e2863SAndrew Thompson 
1059eea19fceSAndrew Thompson 	/* Figure out the USB port location */
1060eea19fceSAndrew Thompson 	snprintf(buf, 64, SYSCTL_LOCATION, i);
1061eea19fceSAndrew Thompson 	len = 127;
1062eea19fceSAndrew Thompson 	error = sysctlbyname(buf, data, &len, NULL, 0);
1063eea19fceSAndrew Thompson 	data[len] = '\0';
1064eea19fceSAndrew Thompson #ifdef DEBUG
1065eea19fceSAndrew Thompson 	fprintf(stderr, "sysctl %s returned(%d): %s\n",
1066eea19fceSAndrew Thompson 	    buf, error, error == 0 ? data : "FAILED");
1067eea19fceSAndrew Thompson #endif
1068eea19fceSAndrew Thompson 	if (error != 0)
1069eea19fceSAndrew Thompson 		return (NULL);
1070eea19fceSAndrew Thompson 
1071eea19fceSAndrew Thompson 	q = strstr(data, "port=");
1072eea19fceSAndrew Thompson 	if (q != NULL) {
1073eea19fceSAndrew Thompson 		error = sscanf(q, " port=%d", &usbport);
1074eea19fceSAndrew Thompson 		if (error != 1) {
1075eea19fceSAndrew Thompson #ifdef DEBUG
1076eea19fceSAndrew Thompson 			fprintf(stderr, "failed to read usb port location from '%s'\n", data);
1077eea19fceSAndrew Thompson #endif
1078eea19fceSAndrew Thompson 			return (NULL);
1079eea19fceSAndrew Thompson 		}
1080eea19fceSAndrew Thompson 	} else {
1081eea19fceSAndrew Thompson #ifdef DEBUG
1082eea19fceSAndrew Thompson 			fprintf(stderr, "failed to parse location '%s'\n", data);
1083eea19fceSAndrew Thompson #endif
1084eea19fceSAndrew Thompson 			return (NULL);
1085eea19fceSAndrew Thompson 	}
1086eea19fceSAndrew Thompson #ifdef DEBUG
1087eea19fceSAndrew Thompson 	fprintf(stderr, "USB port location=%d\n", usbport);
1088eea19fceSAndrew Thompson #endif
1089eea19fceSAndrew Thompson 
1090eea19fceSAndrew Thompson 	/*
1091eea19fceSAndrew Thompson 	 * Now go through it all again but only look at those matching the
1092eea19fceSAndrew Thompson 	 * usb port location we found.
1093eea19fceSAndrew Thompson 	 */
1094eea19fceSAndrew Thompson 	for (i = 0; ; i++) {
1095eea19fceSAndrew Thompson 		snprintf(buf, 64, SYSCTL_LOCATION, i);
1096eea19fceSAndrew Thompson 		len = 127;
1097eea19fceSAndrew Thompson 		memset(&data, 0, sizeof(data));
1098eea19fceSAndrew Thompson 		error = sysctlbyname(buf, data, &len, NULL, 0);
1099eea19fceSAndrew Thompson 		if (error != 0)
1100eea19fceSAndrew Thompson 			break;
1101eea19fceSAndrew Thompson 		data[len] = '\0';
1102eea19fceSAndrew Thompson 		q = strstr(data, "port=");
1103eea19fceSAndrew Thompson 		if (q == NULL)
1104eea19fceSAndrew Thompson 			continue;
1105eea19fceSAndrew Thompson 		sscanf(q, " port=%d", &usbport0);
1106eea19fceSAndrew Thompson 		if (usbport != usbport0)
1107eea19fceSAndrew Thompson 			continue;
1108eea19fceSAndrew Thompson 
1109eea19fceSAndrew Thompson 		/* Try to add ports */
1110941e2863SAndrew Thompson 		for (p = port_type_list; *p != NULL; p++) {
1111941e2863SAndrew Thompson 			snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p);
1112941e2863SAndrew Thompson 			len = 127;
1113eea19fceSAndrew Thompson 			memset(&data, 0, sizeof(data));
1114941e2863SAndrew Thompson 			error = sysctlbyname(buf, data, &len, NULL, 0);
1115eea19fceSAndrew Thompson 			data[len] = '\0';
1116941e2863SAndrew Thompson #ifdef DEBUG
1117941e2863SAndrew Thompson 			fprintf(stderr, "sysctl %s returned(%d): %s\n",
1118941e2863SAndrew Thompson 			    buf, error, error == 0 ? data : "FAILED");
1119941e2863SAndrew Thompson #endif
1120941e2863SAndrew Thompson 			if (error == 0) {
1121941e2863SAndrew Thompson 				list = realloc(list, (list_size + 1) * sizeof(char *));
1122941e2863SAndrew Thompson 				list[list_size] = malloc(strlen(data) + strlen(TTY_NAME));
1123941e2863SAndrew Thompson 		    		sprintf(list[list_size], TTY_NAME, data);
1124941e2863SAndrew Thompson 		    		list_size++;
1125941e2863SAndrew Thompson 			}
1126941e2863SAndrew Thompson 		}
1127eea19fceSAndrew Thompson 	}
1128941e2863SAndrew Thompson 	list = realloc(list, (list_size + 1) * sizeof(char *));
1129941e2863SAndrew Thompson 	list[list_size] = NULL;
1130eea19fceSAndrew Thompson 	return (list);
1131941e2863SAndrew Thompson }
1132941e2863SAndrew Thompson 
1133941e2863SAndrew Thompson static int
1134941e2863SAndrew Thompson do_connect(struct ctx *ctx, const char *tty)
1135941e2863SAndrew Thompson {
1136941e2863SAndrew Thompson 	int i, error, needcfg;
1137941e2863SAndrew Thompson 	resp_arg ra;
1138941e2863SAndrew Thompson 	struct termios t;
1139941e2863SAndrew Thompson 	char **buf;
1140941e2863SAndrew Thompson 
1141941e2863SAndrew Thompson #ifdef DEBUG
1142941e2863SAndrew Thompson 	fprintf(stderr, "Attempting to open %s\n", tty);
1143941e2863SAndrew Thompson #endif
1144941e2863SAndrew Thompson 
1145941e2863SAndrew Thompson 	ctx->fd = open(tty, O_RDWR);
1146941e2863SAndrew Thompson 	if (ctx->fd < 0) {
1147941e2863SAndrew Thompson #ifdef DEBUG
1148941e2863SAndrew Thompson 		fprintf(stderr, "Failed to open %s\n", tty);
1149941e2863SAndrew Thompson #endif
1150941e2863SAndrew Thompson 		return (-1);
1151941e2863SAndrew Thompson 	}
1152941e2863SAndrew Thompson 
1153941e2863SAndrew Thompson 	tcgetattr(ctx->fd, &t);
1154941e2863SAndrew Thompson 	t.c_oflag = 0;
1155941e2863SAndrew Thompson 	t.c_iflag = 0;
1156941e2863SAndrew Thompson 	t.c_cflag = CLOCAL | CREAD;
1157941e2863SAndrew Thompson 	t.c_lflag = 0;
1158941e2863SAndrew Thompson 	tcsetattr(ctx->fd, TCSAFLUSH, &t);
1159941e2863SAndrew Thompson 
1160941e2863SAndrew Thompson 	error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n");
1161941e2863SAndrew Thompson 	if (error == -2) {
1162eea19fceSAndrew Thompson 		warnx("failed to read from device %s", tty);
1163941e2863SAndrew Thompson 		return (-1);
1164941e2863SAndrew Thompson 	}
1165941e2863SAndrew Thompson 
1166941e2863SAndrew Thompson 	/* Check for PIN */
1167941e2863SAndrew Thompson 	error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n");
1168941e2863SAndrew Thompson 	if (error != 0) {
1169eea19fceSAndrew Thompson 		ra.val[0].ptr = NULL;
1170eea19fceSAndrew Thompson 		ra.val[1].int32 = 0;
1171eea19fceSAndrew Thompson 		error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n");
1172eea19fceSAndrew Thompson 		if (ra.val[1].int32 > 0) {
1173eea19fceSAndrew Thompson 			char *p;
1174eea19fceSAndrew Thompson 
1175eea19fceSAndrew Thompson 			buf = ra.val[0].ptr;
1176eea19fceSAndrew Thompson 			if (strstr(buf[0], "+CME ERROR:") != NULL) {
1177eea19fceSAndrew Thompson 				buf[0] += 12;
11785eeae9a7SDimitry Andric 				errx(1, "%s", buf[0]);
1179eea19fceSAndrew Thompson 			}
1180eea19fceSAndrew Thompson 			freeresp(&ra);
1181eea19fceSAndrew Thompson 		} else
1182eea19fceSAndrew Thompson 			freeresp(&ra);
1183eea19fceSAndrew Thompson 
1184941e2863SAndrew Thompson 		if (ctx->pin == NULL) {
1185941e2863SAndrew Thompson 			errx(1, "device requires PIN");
1186941e2863SAndrew Thompson 		}
1187941e2863SAndrew Thompson 
1188941e2863SAndrew Thompson 		error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n",
1189941e2863SAndrew Thompson 		    ctx->pin);
1190941e2863SAndrew Thompson 		if (error != 0) {
1191941e2863SAndrew Thompson 			errx(1, "wrong PIN");
1192941e2863SAndrew Thompson 		}
1193941e2863SAndrew Thompson 	}
1194941e2863SAndrew Thompson 
1195941e2863SAndrew Thompson 	/*
1196941e2863SAndrew Thompson 	 * Check if a PDP context has been configured and configure one
1197941e2863SAndrew Thompson 	 * if needed.
1198941e2863SAndrew Thompson 	 */
1199941e2863SAndrew Thompson 	ra.val[0].ptr = NULL;
1200941e2863SAndrew Thompson 	ra.val[1].int32 = 0;
1201941e2863SAndrew Thompson 	error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n");
1202941e2863SAndrew Thompson 	buf = ra.val[0].ptr;
1203941e2863SAndrew Thompson 	needcfg = 1;
1204941e2863SAndrew Thompson 	for (i = 0; i < ra.val[1].int32; i++) {
1205941e2863SAndrew Thompson 		char apn[256];
1206941e2863SAndrew Thompson 		int cid;
1207941e2863SAndrew Thompson 		error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"",
1208941e2863SAndrew Thompson 		    &cid, apn);
1209941e2863SAndrew Thompson 		if (error != 2) {
1210941e2863SAndrew Thompson 			free(buf[i]);
1211941e2863SAndrew Thompson 			continue;
1212941e2863SAndrew Thompson 		}
1213941e2863SAndrew Thompson 
1214941e2863SAndrew Thompson 		if (cid == ctx->pdp_ctx) {
1215941e2863SAndrew Thompson 			ctx->con_apn = strdup(apn);
1216941e2863SAndrew Thompson 			if (ctx->pdp_apn != NULL) {
1217941e2863SAndrew Thompson 				if (strcmp(apn, ctx->pdp_apn) == 0)
1218941e2863SAndrew Thompson 					needcfg = 0;
1219941e2863SAndrew Thompson 			}
1220941e2863SAndrew Thompson 			else {
1221941e2863SAndrew Thompson 				needcfg = 0;
1222941e2863SAndrew Thompson 			}
1223941e2863SAndrew Thompson 		}
1224941e2863SAndrew Thompson 		free(buf[i]);
1225941e2863SAndrew Thompson 	}
1226941e2863SAndrew Thompson 	free(buf);
1227941e2863SAndrew Thompson 
1228941e2863SAndrew Thompson 	if (needcfg) {
1229941e2863SAndrew Thompson 		if (ctx->pdp_apn == NULL)
1230941e2863SAndrew Thompson 			errx(1, "device is not configured and no APN given");
1231941e2863SAndrew Thompson 
1232941e2863SAndrew Thompson 		error = at_cmd(ctx, NULL, NULL, NULL,
1233941e2863SAndrew Thompson 		   "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn);
1234941e2863SAndrew Thompson 		if (error != 0) {
1235941e2863SAndrew Thompson 			errx(1, "failed to configure device");
1236941e2863SAndrew Thompson 		}
1237941e2863SAndrew Thompson 		ctx->con_apn = strdup(ctx->pdp_apn);
1238941e2863SAndrew Thompson 	}
1239941e2863SAndrew Thompson 
1240941e2863SAndrew Thompson 	if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) {
1241941e2863SAndrew Thompson 		at_cmd(ctx, NULL, NULL, NULL,
1242941e2863SAndrew Thompson 		    "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx,
1243941e2863SAndrew Thompson 		    (ctx->pdp_user != NULL) ? ctx->pdp_user : "",
1244941e2863SAndrew Thompson 		    (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : "");
1245941e2863SAndrew Thompson 	}
1246941e2863SAndrew Thompson 
1247941e2863SAndrew Thompson 	error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1248941e2863SAndrew Thompson 	    ctx->pdp_ctx);
1249941e2863SAndrew Thompson 	if (error != 0)
1250941e2863SAndrew Thompson 		return (-1);
1251941e2863SAndrew Thompson 
1252941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
1253941e2863SAndrew Thompson 	at_cmd_async(ctx->fd, "AT+CREG?\r\n");
1254941e2863SAndrew Thompson 
1255941e2863SAndrew Thompson 	tmr_add(&timers, 1, 5, tmr_status, ctx);
1256941e2863SAndrew Thompson 	return (0);
1257941e2863SAndrew Thompson }
1258941e2863SAndrew Thompson 
1259941e2863SAndrew Thompson static void
1260941e2863SAndrew Thompson do_disconnect(struct ctx *ctx)
1261941e2863SAndrew Thompson {
1262*c74a4ceaSKonrad Witaszczyk 	struct sockaddr_in sin;
1263941e2863SAndrew Thompson 
1264941e2863SAndrew Thompson 	/* Disconnect */
1265941e2863SAndrew Thompson 	at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1266941e2863SAndrew Thompson 	    ctx->pdp_ctx);
1267941e2863SAndrew Thompson 	close(ctx->fd);
1268941e2863SAndrew Thompson 
1269941e2863SAndrew Thompson 	/* Remove ip-address from interface */
1270941e2863SAndrew Thompson 	if (ctx->flags & IPASSIGNED) {
1271*c74a4ceaSKonrad Witaszczyk 		sin.sin_len = sizeof(struct sockaddr_in);
1272*c74a4ceaSKonrad Witaszczyk 		sin.sin_family = AF_INET;
1273941e2863SAndrew Thompson 		memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
1274941e2863SAndrew Thompson 		    sizeof(sin.sin_addr.s_addr));
1275*c74a4ceaSKonrad Witaszczyk 		ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin);
1276941e2863SAndrew Thompson 
1277941e2863SAndrew Thompson 		if_ifdown(ctx->ifnam);
1278941e2863SAndrew Thompson 		ctx->flags &= ~IPASSIGNED;
1279941e2863SAndrew Thompson 	}
1280941e2863SAndrew Thompson 
1281941e2863SAndrew Thompson 	/* Attempt to reset resolv.conf */
1282941e2863SAndrew Thompson 	set_nameservers(ctx, ctx->resolv_path, 0);
1283941e2863SAndrew Thompson }
1284941e2863SAndrew Thompson 
1285941e2863SAndrew Thompson static void
1286941e2863SAndrew Thompson daemonize(struct ctx *ctx)
1287941e2863SAndrew Thompson {
1288941e2863SAndrew Thompson 	struct pidfh *pfh;
1289941e2863SAndrew Thompson 	pid_t opid;
1290941e2863SAndrew Thompson 
1291941e2863SAndrew Thompson 	snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam);
1292941e2863SAndrew Thompson 
1293941e2863SAndrew Thompson 	pfh = pidfile_open(ctx->pidfile, 0600, &opid);
1294941e2863SAndrew Thompson 	if (pfh == NULL) {
1295941e2863SAndrew Thompson 		warn("Cannot create pidfile %s", ctx->pidfile);
1296941e2863SAndrew Thompson 		return;
1297941e2863SAndrew Thompson 	}
1298941e2863SAndrew Thompson 
1299941e2863SAndrew Thompson 	if (daemon(0, 0) == -1) {
1300941e2863SAndrew Thompson 		warn("Cannot daemonize");
1301941e2863SAndrew Thompson 		pidfile_remove(pfh);
1302941e2863SAndrew Thompson 		return;
1303941e2863SAndrew Thompson 	}
1304941e2863SAndrew Thompson 
1305941e2863SAndrew Thompson 	pidfile_write(pfh);
1306941e2863SAndrew Thompson 	ctx->pfh = pfh;
1307941e2863SAndrew Thompson 	ctx->flags |= FLG_DAEMON;
1308941e2863SAndrew Thompson 
1309941e2863SAndrew Thompson 	snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam);
1310941e2863SAndrew Thompson 	openlog(syslog_title, LOG_PID, LOG_USER);
1311941e2863SAndrew Thompson 	syslog_open = 1;
1312941e2863SAndrew Thompson }
1313941e2863SAndrew Thompson 
1314941e2863SAndrew Thompson static void
1315941e2863SAndrew Thompson send_disconnect(const char *ifnam)
1316941e2863SAndrew Thompson {
1317941e2863SAndrew Thompson 	char pidfile[128];
1318941e2863SAndrew Thompson 	FILE *fp;
1319941e2863SAndrew Thompson 	pid_t pid;
1320941e2863SAndrew Thompson 	int n;
1321941e2863SAndrew Thompson 
1322941e2863SAndrew Thompson 	snprintf(pidfile, 127, PIDFILE, ifnam);
1323941e2863SAndrew Thompson 	fp = fopen(pidfile, "r");
1324941e2863SAndrew Thompson 	if (fp == NULL) {
1325941e2863SAndrew Thompson 		warn("Cannot open %s", pidfile);
1326941e2863SAndrew Thompson 		return;
1327941e2863SAndrew Thompson 	}
1328941e2863SAndrew Thompson 
1329941e2863SAndrew Thompson 	n = fscanf(fp, "%d", &pid);
1330941e2863SAndrew Thompson 	fclose(fp);
1331941e2863SAndrew Thompson 	if (n != 1) {
1332941e2863SAndrew Thompson 		warnx("unable to read daemon pid");
1333941e2863SAndrew Thompson 		return;
1334941e2863SAndrew Thompson 	}
1335941e2863SAndrew Thompson #ifdef DEBUG
1336941e2863SAndrew Thompson 	fprintf(stderr, "Sending SIGTERM to %d\n", pid);
1337941e2863SAndrew Thompson #endif
1338941e2863SAndrew Thompson 	kill(pid, SIGTERM);
1339941e2863SAndrew Thompson }
1340941e2863SAndrew Thompson 
1341941e2863SAndrew Thompson static void
1342941e2863SAndrew Thompson usage(const char *exec)
1343941e2863SAndrew Thompson {
1344941e2863SAndrew Thompson 
1345941e2863SAndrew Thompson 	printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] "
1346941e2863SAndrew Thompson 	    "[-k password] [-r resolvpath] [-f tty] interface\n", exec);
1347941e2863SAndrew Thompson 	printf("usage %s -d interface\n", exec);
1348941e2863SAndrew Thompson }
1349941e2863SAndrew Thompson 
1350941e2863SAndrew Thompson enum {
1351941e2863SAndrew Thompson 	MODE_CONN,
1352941e2863SAndrew Thompson 	MODE_DISC
1353941e2863SAndrew Thompson };
1354941e2863SAndrew Thompson 
1355941e2863SAndrew Thompson int
1356941e2863SAndrew Thompson main(int argc, char *argv[])
1357941e2863SAndrew Thompson {
1358941e2863SAndrew Thompson 	int ch, error, mode;
1359941e2863SAndrew Thompson 	const char *ifnam = NULL;
1360941e2863SAndrew Thompson 	char *tty = NULL;
1361941e2863SAndrew Thompson 	char **p, **tty_list;
1362941e2863SAndrew Thompson 	fd_set set;
1363941e2863SAndrew Thompson 	struct ctx ctx;
1364941e2863SAndrew Thompson 	struct itimerval it;
1365941e2863SAndrew Thompson 
1366941e2863SAndrew Thompson 	TAILQ_INIT(&timers.head);
1367941e2863SAndrew Thompson 	timers.res = 1;
1368941e2863SAndrew Thompson 
1369941e2863SAndrew Thompson 	ctx.pdp_ctx = 1;
1370941e2863SAndrew Thompson 	ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL;
1371941e2863SAndrew Thompson 	ctx.pin = NULL;
1372941e2863SAndrew Thompson 
1373941e2863SAndrew Thompson 	ctx.con_status = 0;
1374941e2863SAndrew Thompson 	ctx.con_apn = NULL;
1375941e2863SAndrew Thompson 	ctx.con_oper = NULL;
1376941e2863SAndrew Thompson 	ctx.con_net_stat = 0;
1377941e2863SAndrew Thompson 	ctx.con_net_type = -1;
1378941e2863SAndrew Thompson 	ctx.flags = 0;
1379941e2863SAndrew Thompson 	ctx.resolv_path = RESOLV_PATH;
1380941e2863SAndrew Thompson 	ctx.resolv = NULL;
1381941e2863SAndrew Thompson 	ctx.ns = NULL;
1382941e2863SAndrew Thompson 	ctx.dbm = 0;
1383941e2863SAndrew Thompson 
1384941e2863SAndrew Thompson 	mode = MODE_CONN;
1385941e2863SAndrew Thompson 	ctx.flags |= FLG_DELAYED;
1386941e2863SAndrew Thompson 
1387941e2863SAndrew Thompson 	while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) {
1388941e2863SAndrew Thompson 		switch (ch) {
1389941e2863SAndrew Thompson 		case 'a':
1390941e2863SAndrew Thompson 			ctx.pdp_apn = argv[optind - 1];
1391941e2863SAndrew Thompson 			break;
1392941e2863SAndrew Thompson 		case 'c':
1393941e2863SAndrew Thompson 			ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10);
1394941e2863SAndrew Thompson 			if (ctx.pdp_ctx < 1) {
1395941e2863SAndrew Thompson 				warnx("Invalid context ID, defaulting to 1");
1396941e2863SAndrew Thompson 				ctx.pdp_ctx = 1;
1397941e2863SAndrew Thompson 			}
1398941e2863SAndrew Thompson 			break;
1399941e2863SAndrew Thompson 		case 'p':
1400941e2863SAndrew Thompson 			ctx.pin = argv[optind - 1];
1401941e2863SAndrew Thompson 			break;
1402941e2863SAndrew Thompson 		case 'u':
1403941e2863SAndrew Thompson 			ctx.pdp_user = argv[optind - 1];
1404941e2863SAndrew Thompson 			break;
1405941e2863SAndrew Thompson 		case 'k':
1406941e2863SAndrew Thompson 			ctx.pdp_pwd = argv[optind - 1];
1407941e2863SAndrew Thompson 			break;
1408941e2863SAndrew Thompson 		case 'r':
1409941e2863SAndrew Thompson 			ctx.resolv_path = argv[optind - 1];
1410941e2863SAndrew Thompson 			break;
1411941e2863SAndrew Thompson 		case 'd':
1412941e2863SAndrew Thompson 			mode = MODE_DISC;
1413941e2863SAndrew Thompson 			break;
1414941e2863SAndrew Thompson 		case 'b':
1415941e2863SAndrew Thompson 			ctx.flags &= ~FLG_DELAYED;
1416941e2863SAndrew Thompson 			break;
1417941e2863SAndrew Thompson 		case 'n':
1418941e2863SAndrew Thompson 			ctx.flags |= FLG_NODAEMON;
1419941e2863SAndrew Thompson 			break;
1420941e2863SAndrew Thompson 		case 'f':
1421941e2863SAndrew Thompson 			tty = argv[optind - 1];
1422941e2863SAndrew Thompson 			break;
1423941e2863SAndrew Thompson 		case 'h':
1424941e2863SAndrew Thompson 		case '?':
1425941e2863SAndrew Thompson 		default:
1426941e2863SAndrew Thompson 			usage(argv[0]);
1427941e2863SAndrew Thompson 			exit(EXIT_SUCCESS);
1428941e2863SAndrew Thompson 		}
1429941e2863SAndrew Thompson 	}
1430941e2863SAndrew Thompson 
1431941e2863SAndrew Thompson 	argc -= optind;
1432941e2863SAndrew Thompson 	argv += optind;
1433941e2863SAndrew Thompson 
1434941e2863SAndrew Thompson 	if (argc < 1)
1435941e2863SAndrew Thompson 		errx(1, "no interface given");
1436941e2863SAndrew Thompson 
1437941e2863SAndrew Thompson 	ifnam = argv[argc - 1];
1438941e2863SAndrew Thompson 	ctx.ifnam = strdup(ifnam);
1439941e2863SAndrew Thompson 
1440941e2863SAndrew Thompson 	switch (mode) {
1441941e2863SAndrew Thompson 	case MODE_DISC:
1442941e2863SAndrew Thompson 		printf("Disconnecting %s\n", ifnam);
1443941e2863SAndrew Thompson 		send_disconnect(ifnam);
1444941e2863SAndrew Thompson 		exit(EXIT_SUCCESS);
1445941e2863SAndrew Thompson 	default:
1446941e2863SAndrew Thompson 		break;
1447941e2863SAndrew Thompson 	}
1448941e2863SAndrew Thompson 
1449941e2863SAndrew Thompson 	signal(SIGHUP, sig_handle);
1450941e2863SAndrew Thompson 	signal(SIGINT, sig_handle);
1451941e2863SAndrew Thompson 	signal(SIGQUIT, sig_handle);
1452941e2863SAndrew Thompson 	signal(SIGTERM, sig_handle);
1453941e2863SAndrew Thompson 	signal(SIGALRM, sig_handle);
1454941e2863SAndrew Thompson 
1455941e2863SAndrew Thompson 	it.it_interval.tv_sec = 1;
1456941e2863SAndrew Thompson 	it.it_interval.tv_usec = 0;
1457941e2863SAndrew Thompson 	it.it_value.tv_sec = 1;
1458941e2863SAndrew Thompson 	it.it_value.tv_usec = 0;
1459941e2863SAndrew Thompson 	error = setitimer(ITIMER_REAL, &it, NULL);
1460941e2863SAndrew Thompson 	if (error != 0)
1461941e2863SAndrew Thompson 		errx(1, "setitimer");
1462941e2863SAndrew Thompson 
1463941e2863SAndrew Thompson 	tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx);
1464941e2863SAndrew Thompson 	watchdog_reset(&ctx, 15);
1465941e2863SAndrew Thompson 
1466941e2863SAndrew Thompson 	if (tty != NULL) {
1467941e2863SAndrew Thompson 		error = do_connect(&ctx, tty);
1468941e2863SAndrew Thompson 		if (error != 0)
1469941e2863SAndrew Thompson 			errx(1, "Failed to open %s", tty);
1470941e2863SAndrew Thompson 	}
1471941e2863SAndrew Thompson 	else {
1472941e2863SAndrew Thompson 		tty_list = get_tty(&ctx);
1473eea19fceSAndrew Thompson 		if (tty_list == NULL)
1474eea19fceSAndrew Thompson 			errx(1, "%s does not appear to be a uhso device", ifnam);
1475941e2863SAndrew Thompson #ifdef DEBUG
1476941e2863SAndrew Thompson 		if (tty_list == NULL) {
1477941e2863SAndrew Thompson 			fprintf(stderr, "get_tty returned empty list\n");
1478941e2863SAndrew Thompson 		} else {
1479941e2863SAndrew Thompson 			fprintf(stderr, "tty list:\n");
1480941e2863SAndrew Thompson 			for (p = tty_list; *p != NULL; p++) {
1481941e2863SAndrew Thompson 				fprintf(stderr, "\t %s\n", *p);
1482941e2863SAndrew Thompson 			}
1483941e2863SAndrew Thompson 		}
1484941e2863SAndrew Thompson #endif
1485941e2863SAndrew Thompson 		for (p = tty_list; *p != NULL; p++) {
1486941e2863SAndrew Thompson 			error = do_connect(&ctx, *p);
1487941e2863SAndrew Thompson 			if (error == 0) {
1488941e2863SAndrew Thompson 				tty = *p;
1489941e2863SAndrew Thompson 				break;
1490941e2863SAndrew Thompson 			}
1491941e2863SAndrew Thompson 		}
1492941e2863SAndrew Thompson 		if (*p == NULL)
1493941e2863SAndrew Thompson 			errx(1, "Failed to obtain a control port, "
1494941e2863SAndrew Thompson 			    "try specifying one manually");
1495941e2863SAndrew Thompson 	}
1496941e2863SAndrew Thompson 
1497941e2863SAndrew Thompson 	if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON))
1498941e2863SAndrew Thompson 		daemonize(&ctx);
1499941e2863SAndrew Thompson 
1500941e2863SAndrew Thompson 
1501941e2863SAndrew Thompson 	FD_ZERO(&set);
1502941e2863SAndrew Thompson 	FD_SET(ctx.fd, &set);
1503941e2863SAndrew Thompson 	for (;;) {
1504941e2863SAndrew Thompson 
1505941e2863SAndrew Thompson 		watchdog_disable(&ctx);
1506941e2863SAndrew Thompson 		error = select(ctx.fd + 1, &set, NULL, NULL, NULL);
1507941e2863SAndrew Thompson 		if (error <= 0) {
1508941e2863SAndrew Thompson 			if (running && errno == EINTR)
1509941e2863SAndrew Thompson 				continue;
1510941e2863SAndrew Thompson 			if (ctx.flags & FLG_WDEXP) {
1511941e2863SAndrew Thompson 				ctx.flags &= ~FLG_WDEXP;
1512941e2863SAndrew Thompson 				watchdog_reset(&ctx, 5);
1513941e2863SAndrew Thompson 				do_disconnect(&ctx);
1514941e2863SAndrew Thompson 				watchdog_reset(&ctx, 15);
1515941e2863SAndrew Thompson 				do_connect(&ctx, tty);
1516941e2863SAndrew Thompson 				running = 1;
1517941e2863SAndrew Thompson 				continue;
1518941e2863SAndrew Thompson 			}
1519941e2863SAndrew Thompson 
1520941e2863SAndrew Thompson 			break;
1521941e2863SAndrew Thompson 		}
1522941e2863SAndrew Thompson 
1523941e2863SAndrew Thompson 		if (FD_ISSET(ctx.fd, &set)) {
1524941e2863SAndrew Thompson 			watchdog_reset(&ctx, 15);
1525941e2863SAndrew Thompson 			error = at_async(&ctx, &ctx);
1526941e2863SAndrew Thompson 			if (error != 0)
1527941e2863SAndrew Thompson 				break;
1528941e2863SAndrew Thompson 		}
1529941e2863SAndrew Thompson 		FD_SET(ctx.fd, &set);
1530941e2863SAndrew Thompson 
1531941e2863SAndrew Thompson 		if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) {
1532941e2863SAndrew Thompson 			printf("Status: %s (%s)",
1533941e2863SAndrew Thompson 			    ctx.con_status ? "connected" : "disconnected",
1534941e2863SAndrew Thompson 			    network_access_type[ctx.con_net_type]);
1535941e2863SAndrew Thompson 			if (ctx.dbm < 0)
1536941e2863SAndrew Thompson 				printf(", signal: %d dBm", ctx.dbm);
1537eea19fceSAndrew Thompson 			printf("\t\t\t\r");
1538941e2863SAndrew Thompson 			fflush(stdout);
1539941e2863SAndrew Thompson 		}
1540941e2863SAndrew Thompson 	}
1541941e2863SAndrew Thompson 	if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED))
1542941e2863SAndrew Thompson 		printf("\n");
1543941e2863SAndrew Thompson 
1544941e2863SAndrew Thompson 	signal(SIGHUP, SIG_DFL);
1545941e2863SAndrew Thompson 	signal(SIGINT, SIG_DFL);
1546941e2863SAndrew Thompson 	signal(SIGQUIT, SIG_DFL);
1547941e2863SAndrew Thompson 	signal(SIGTERM, SIG_DFL);
1548941e2863SAndrew Thompson 	signal(SIGALRM, SIG_IGN);
1549941e2863SAndrew Thompson 
1550941e2863SAndrew Thompson 	do_disconnect(&ctx);
1551941e2863SAndrew Thompson 
1552941e2863SAndrew Thompson 	if (ctx.flags & FLG_DAEMON) {
1553941e2863SAndrew Thompson 		pidfile_remove(ctx.pfh);
1554941e2863SAndrew Thompson 		if (syslog_open)
1555941e2863SAndrew Thompson 			closelog();
1556941e2863SAndrew Thompson 	}
1557941e2863SAndrew Thompson 
1558941e2863SAndrew Thompson 	return (0);
1559941e2863SAndrew Thompson }
1560