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