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