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