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