2 * Copyright (c) 2008-2009 Fredrik Lindberg
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
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>
34 #include <sys/sysctl.h>
36 #include <sys/queue.h>
38 #include <arpa/inet.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>
63 * Connection utility to ease connectivity using the raw IP packet interface
64 * available on uhso(4) devices.
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"
77 static const char *network_access_type[] = {
87 static const char *network_reg_status[] = {
90 "Searching for network",
91 "Network registration denied",
93 "Registered (roaming)"
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 */
107 const char *pin; /* device PIN */
114 /* PDP context settings */
117 const char *pdp_user;
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 */
130 /* IP and nameserver settings */
133 const char *resolv_path;
134 char *resolv; /* Old resolv.conf */
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 *);
142 static int at_cmd_async(int, const char *, ...);
151 typedef void (*resp_cb)(resp_arg *, const char *, const char *);
153 typedef void (*async_cb)(void *, const char *);
154 struct async_handle {
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 *);
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 },
178 TAILQ_HEAD(, timer_entry) head;
182 typedef void (*tmr_cb)(int, void *);
184 TAILQ_ENTRY(timer_entry) next;
192 static struct timers timers;
193 static volatile int running = 1;
194 static int syslog_open = 0;
195 static char syslog_title[64];
197 /* Periodic timer, runs ready timer tasks every tick */
199 tmr_run(struct timers *tmrs)
201 struct timer_entry *te, *te2;
203 te = TAILQ_FIRST(&tmrs->head);
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);
219 /* Add a new timer */
221 tmr_add(struct timers *tmrs, int id, int timeout, tmr_cb func, void *arg)
223 struct timer_entry *te, *te2, *te3;
225 te = malloc(sizeof(struct timer_entry));
226 memset(te, 0, sizeof(struct timer_entry));
228 te->timeout = timeout;
233 te2 = TAILQ_FIRST(&tmrs->head);
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);
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)
248 TAILQ_INSERT_AFTER(&tmrs->head, te2, te, next);
252 #define watchdog_enable(ctx) (ctx)->flags |= FLG_WATCHDOG
253 #define watchdog_disable(ctx) (ctx)->flags &= ~FLG_WATCHDOG
256 watchdog_reset(struct ctx *ctx, int timeout)
260 clock_gettime(CLOCK_MONOTONIC, &tp),
261 ctx->watchdog = tp.tv_sec + timeout;
263 watchdog_enable(ctx);
267 tmr_creg(int id, void *arg)
269 struct ctx *ctx = arg;
271 at_cmd_async(ctx->fd, "AT+CREG?\r\n");
272 watchdog_reset(ctx, 10);
276 tmr_cgreg(int id, void *arg)
278 struct ctx *ctx = arg;
280 at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
281 watchdog_reset(ctx, 10);
285 tmr_status(int id, void *arg)
287 struct ctx *ctx = arg;
289 at_cmd_async(ctx->fd, "AT+CSQ\r\n");
290 watchdog_reset(ctx, 10);
294 tmr_watchdog(int id, void *arg)
296 struct ctx *ctx = arg;
300 tmr_add(&timers, 1, 5, tmr_watchdog, ctx);
302 if (!(ctx->flags & FLG_WATCHDOG))
305 clock_gettime(CLOCK_MONOTONIC, &tp);
307 if (tp.tv_sec >= ctx->watchdog) {
309 fprintf(stderr, "Watchdog expired\n");
311 ctx->flags |= FLG_WDEXP;
335 logger(int pri, const char *fmt, ...)
341 vasprintf(&buf, fmt, ap);
343 syslog(pri, "%s", buf);
351 fprintf(stderr, "%s: %s\n", getprogname(), buf);
360 /* Add/remove IP address from an interface */
362 ifaddr_ad(int d, const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
364 struct ifaliasreq req;
367 fd = socket(AF_INET, SOCK_DGRAM, 0);
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);
376 error = ioctl(fd, d, (char *)&req);
381 #define if_ifup(ifnam) if_setflags(ifnam, IFF_UP)
382 #define if_ifdown(ifnam) if_setflags(ifnam, -IFF_UP)
385 if_setflags(const char *ifnam, int flags)
389 unsigned int oflags = 0;
391 memset(&ifr, 0, sizeof(struct ifreq));
392 strlcpy(ifr.ifr_name, ifnam, sizeof(ifr.ifr_name));
394 fd = socket(AF_INET, SOCK_DGRAM, 0);
398 error = ioctl(fd, SIOCGIFFLAGS, &ifr);
400 oflags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
408 ifr.ifr_flags = oflags & 0xffff;
409 ifr.ifr_flagshigh = oflags >> 16;
411 error = ioctl(fd, SIOCSIFFLAGS, &ifr);
413 warn("ioctl SIOCSIFFLAGS");
420 ifaddr_add(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
424 error = ifaddr_ad(SIOCAIFADDR, ifnam, sa, mask);
426 warn("ioctl SIOCAIFADDR");
431 ifaddr_del(const char *ifnam, struct sockaddr *sa, struct sockaddr *mask)
435 error = ifaddr_ad(SIOCDIFADDR, ifnam, sa, mask);
437 warn("ioctl SIOCDIFADDR");
442 set_nameservers(struct ctx *ctx, const char *respath, int ns, ...)
451 if (ctx->ns != NULL) {
452 for (i = 0; ctx->ns[i] != NULL; i++) {
459 fd = open(respath, O_RDWR | O_CREAT | O_NOFOLLOW, 0666);
464 /* Attempt to restore old resolv.conf */
465 if (ctx->resolv != NULL) {
467 lseek(fd, 0, SEEK_SET);
468 write(fd, ctx->resolv, ctx->resolv_sz);
478 ctx->ns = malloc(sizeof(char *) * (ns + 1));
479 if (ctx->ns == NULL) {
485 for (i = 0; i < ns; i++) {
486 p = va_arg(ap, char *);
487 ctx->ns[i] = strdup(p);
492 /* Attempt to backup the old resolv.conf */
493 if (ctx->resolv == NULL) {
495 if (i == 0 && sb.st_size != 0) {
496 ctx->resolv_sz = sb.st_size;
497 ctx->resolv = malloc(sb.st_size);
498 if (ctx->resolv != NULL) {
499 n = read(fd, ctx->resolv, sb.st_size);
500 if (n != sb.st_size) {
510 lseek(fd, 0, SEEK_SET);
511 fp = fdopen(fd, "w");
514 * Write back everything other than nameserver entries to the
517 if (ctx->resolv != NULL) {
519 while ((i = readline_buf(p, ctx->resolv + ctx->resolv_sz, buf,
522 if (strncasecmp(buf, "nameserver", 10) == 0)
524 fprintf(fp, "%s", buf);
528 for (i = 0; ctx->ns[i] != NULL; i++) {
529 fprintf(fp, "nameserver %s\n", ctx->ns[i]);
535 /* Read a \n-terminated line from buffer */
537 readline_buf(const char *s, const char *e, char *buf, size_t bufsz)
545 if (pos >= (bufsz - 1))
554 /* Read a \n-terminated line from file */
556 readline(int fd, char *buf, size_t bufsz)
566 if (pos >= (bufsz - 1))
572 return (n <= 0 ? n : pos);
576 * Synchronous AT command
579 at_cmd(struct ctx *ctx, const char *resp, resp_cb cb, resp_arg *ra, const char *cf, ...)
584 int n, error, retval = 0;
590 vsnprintf(cmd, sizeof(cmd), cf, ap);
594 fprintf(stderr, "SYNC_CMD: %s", cmd);
598 n = write(ctx->fd, cmd, l);
605 fprintf(stderr, "SYNC_EXP: %s (%zu)\n", resp, l);
610 bzero(buf, sizeof(buf));
613 watchdog_reset(ctx, 5);
615 FD_SET(ctx->fd, &set);
616 error = select(ctx->fd + 1, &set, NULL, NULL, NULL);
617 if (ctx->flags & FLG_WDEXP) {
618 watchdog_disable(ctx);
621 } while (error <= 0 && errno == EINTR);
622 watchdog_disable(ctx);
629 n = readline(ctx->fd, buf, sizeof(buf));
635 if (strcmp(buf, "\r\n") == 0 || strcmp(buf, "\n") == 0)
638 if ((p = strchr(buf, '\r')) != NULL)
640 else if ((p = strchr(buf, '\n')) != NULL)
643 fprintf(stderr, "SYNC_RESP: %s\n", buf);
646 /* Skip local echo */
647 if (strncasecmp(cmd, buf, strlen(buf)) == 0)
653 if (strncmp(buf, "OK", 2) == 0) {
654 retval = retval ? retval : 0;
656 } else if (strstr(buf, "ERROR") != NULL) {
661 retval = strncmp(buf, resp, l);
664 fprintf(stderr, "SYNC_RETVAL=%d\n", retval);
670 at_cmd_async(int fd, const char *cf, ...)
677 vsnprintf(cmd, sizeof(cmd), cf, ap);
681 fprintf(stderr, "CMD: %s", cmd);
684 return (write(fd, cmd, l));
688 saveresp(resp_arg *ra, const char *cmd, const char *resp)
691 int i = ra->val[1].int32;
694 fprintf(stderr, "Save '%s'\n", resp);
697 buf = realloc(ra->val[0].ptr, sizeof(char *) * (i + 1));
701 buf[i] = strdup(resp);
703 ra->val[0].ptr = buf;
704 ra->val[1].int32 = i + 1;
708 freeresp(resp_arg *ra)
713 buf = ra->val[0].ptr;
714 for (i = 0; i < ra->val[1].int32; i++) {
721 at_async_creg(void *arg, const char *resp)
723 struct ctx *ctx = arg;
726 n = sscanf(resp, "+CREG: %*d,%d", ®);
728 n = sscanf(resp, "+CREG: %d", ®);
733 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
734 tmr_add(&timers, 1, 1, tmr_creg, ctx);
737 tmr_add(&timers, 1, 30, tmr_creg, ctx);
740 if (ctx->con_net_stat == reg)
743 ctx->con_net_stat = reg;
744 at_cmd_async(ctx->fd, "AT+COPS?\r\n");
748 at_async_cgreg(void *arg, const char *resp)
750 struct ctx *ctx = arg;
753 n = sscanf(resp, "+CGREG: %*d,%d", ®);
755 n = sscanf(resp, "+CGREG: %d", ®);
760 if (ctx->con_net_stat != 1 && ctx->con_net_stat != 5) {
761 tmr_add(&timers, 1, 1, tmr_cgreg, ctx);
764 tmr_add(&timers, 1, 30, tmr_cgreg, ctx);
767 if (ctx->con_net_stat == reg)
770 ctx->con_net_stat = reg;
771 at_cmd_async(ctx->fd, "AT+COPS?\r\n");
776 at_async_cops(void *arg, const char *resp)
778 struct ctx *ctx = arg;
782 n = sscanf(resp, "+COPS: %*d,%*d,\"%[^\"]\",%d",
787 if (ctx->con_oper != NULL) {
788 if (ctx->con_net_type == at &&
789 strcasecmp(opr, ctx->con_oper) == 0)
794 ctx->con_oper = strdup(opr);
795 ctx->con_net_type = at;
797 if (ctx->con_net_stat == 1 || ctx->con_net_stat == 5) {
798 logger(LOG_NOTICE, "%s to \"%s\" (%s)",
799 network_reg_status[ctx->con_net_stat],
800 ctx->con_oper, network_access_type[ctx->con_net_type]);
801 if (ctx->con_status != 1) {
802 at_cmd_async(ctx->fd, "AT_OWANCALL=%d,1,1\r\n",
807 logger(LOG_NOTICE, "%s (%s)",
808 network_reg_status[ctx->con_net_stat],
809 network_access_type[ctx->con_net_type]);
814 * Signal strength for pretty console output
816 * From 3GPP TS 27.007 V8.3.0, Section 8.5
817 * 0 = -113 dBm or less
819 * 2...30 = -109...-53 dBm
820 * 31 = -51 dBm or greater
822 * So, dbm = (rssi * 2) - 113
825 at_async_csq(void *arg, const char *resp)
827 struct ctx *ctx = arg;
830 n = sscanf(resp, "+CSQ: %d,%*d", &rssi);
836 ctx->dbm = (rssi * 2) - 113;
837 tmr_add(&timers, 1, 15, tmr_status, ctx);
840 ctx->flags |= FLG_NEWDATA;
844 at_async_owancall(void *arg, const char *resp)
846 struct ctx *ctx = arg;
849 n = sscanf(resp, "_OWANCALL: %*d,%d", &i);
853 if (i == ctx->con_status)
856 at_cmd_async(ctx->fd, "AT_OWANDATA=%d\r\n", ctx->pdp_ctx);
859 if (ctx->con_status == 1) {
860 logger(LOG_NOTICE, "Connected to \"%s\" (%s), %s",
861 ctx->con_oper, ctx->con_apn,
862 network_access_type[ctx->con_net_type]);
865 logger(LOG_NOTICE, "Disconnected from \"%s\" (%s)",
866 ctx->con_oper, ctx->con_apn);
871 at_async_owandata(void *arg, const char *resp)
873 struct ctx *ctx = arg;
874 char ip[40], ns1[40], ns2[40];
876 struct ifaddrs *ifap, *ifa;
877 struct sockaddr_in sin, mask;
878 struct sockaddr_dl sdl;
880 struct rt_msghdr rtm;
885 n = sscanf(resp, "_OWANDATA: %*d, %[^,], %*[^,], %[^,], %[^,]",
890 /* XXX: AF_INET assumption */
892 logger(LOG_NOTICE, "IP address: %s, Nameservers: %s, %s", ip, ns1, ns2);
894 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in);
895 memset(&mask.sin_addr.s_addr, 0xff, sizeof(mask.sin_addr.s_addr));
896 sin.sin_family = mask.sin_family = AF_INET;
898 if (ctx->flags & IPASSIGNED) {
899 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
900 sizeof(sin.sin_addr.s_addr));
901 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin,
902 (struct sockaddr *)&mask);
904 inet_pton(AF_INET, ip, &ctx->ip.s_addr);
905 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
906 sizeof(sin.sin_addr.s_addr));
908 error = ifaddr_add(ctx->ifnam, (struct sockaddr *)&sin,
909 (struct sockaddr *)&mask);
911 logger(LOG_ERR, "failed to set ip-address");
917 ctx->flags |= IPASSIGNED;
919 set_nameservers(ctx, ctx->resolv_path, 0);
920 error = set_nameservers(ctx, ctx->resolv_path, 2, ns1, ns2);
922 logger(LOG_ERR, "failed to set nameservers");
925 error = getifaddrs(&ifap);
927 logger(LOG_ERR, "getifaddrs: %s", strerror(errno));
931 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
932 if (ifa->ifa_addr->sa_family != AF_LINK)
934 if (strcmp(ctx->ifnam, ifa->ifa_name) == 0) {
935 memcpy(&sdl, (struct sockaddr_dl *)ifa->ifa_addr,
936 sizeof(struct sockaddr_dl));
943 rs = socket(PF_ROUTE, SOCK_RAW, 0);
945 logger(LOG_ERR, "socket PF_ROUTE: %s", strerror(errno));
949 memset(&r, 0, sizeof(r));
951 r.rtm.rtm_version = RTM_VERSION;
952 r.rtm.rtm_type = RTM_ADD;
953 r.rtm.rtm_flags = RTF_UP | RTF_STATIC;
954 r.rtm.rtm_pid = getpid();
955 memset(&sin, 0, sizeof(struct sockaddr_in));
956 sin.sin_family = AF_INET;
957 sin.sin_len = sizeof(struct sockaddr_in);
959 memcpy(cp, &sin, sin.sin_len);
961 memcpy(cp, &sdl, sdl.sdl_len);
963 memcpy(cp, &sin, sin.sin_len);
964 r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
965 r.rtm.rtm_msglen = sizeof(r);
967 n = write(rs, &r, r.rtm.rtm_msglen);
968 if (n != r.rtm.rtm_msglen) {
969 r.rtm.rtm_type = RTM_DELETE;
970 n = write(rs, &r, r.rtm.rtm_msglen);
971 r.rtm.rtm_type = RTM_ADD;
972 n = write(rs, &r, r.rtm.rtm_msglen);
975 if (n != r.rtm.rtm_msglen) {
976 logger(LOG_ERR, "failed to set default route: %s",
981 /* Delayed daemonization */
982 if ((ctx->flags & FLG_DELAYED) && !(ctx->flags & FLG_NODAEMON))
987 at_async(struct ctx *ctx, void *arg)
993 watchdog_reset(ctx, 15);
995 bzero(buf, sizeof(buf));
996 n = readline(ctx->fd, buf, sizeof(buf));
998 return (n <= 0 ? -1 : 0);
1001 fprintf(stderr, "AT_ASYNC_RESP: %s", buf);
1003 for (i = 0; async_cmd[i].cmd != NULL; i++) {
1004 l = strlen(async_cmd[i].cmd);
1005 if (strncmp(buf, async_cmd[i].cmd, l) == 0) {
1006 async_cmd[i].func(arg, buf);
1012 static const char *port_type_list[] = {
1013 "control", "application", "application2", NULL
1017 * Attempts to find a list of control tty for the interface
1018 * FreeBSD attaches USB devices per interface so we have to go through
1019 * hoops to find which ttys that belong to our network interface.
1022 get_tty(struct ctx *ctx)
1024 char buf[64], data[128];
1025 int error, i, usbport, usbport0, list_size = 0;
1031 * Look for the network interface first
1033 for (i = 0; ; i++) {
1034 /* Check if we still have uhso nodes to check */
1035 snprintf(buf, 64, SYSCTL_TEST, i);
1037 error = sysctlbyname(buf, data, &len, NULL, 0);
1040 fprintf(stderr, "sysctl %s returned(%d): %s\n",
1041 buf, error, error == 0 ? data : "FAILED");
1043 if (error < 0 || strcasecmp(data, "uhso") != 0)
1046 /* Check if this node contains the network interface we want */
1047 snprintf(buf, 64, SYSCTL_NETIF, i);
1049 error = sysctlbyname(buf, data, &len, NULL, 0);
1052 fprintf(stderr, "sysctl %s returned(%d): %s\n",
1053 buf, error, error == 0 ? data : "FAILED");
1055 if (error == 0 && strcasecmp(data, ctx->ifnam) == 0)
1059 /* Figure out the USB port location */
1060 snprintf(buf, 64, SYSCTL_LOCATION, i);
1062 error = sysctlbyname(buf, data, &len, NULL, 0);
1065 fprintf(stderr, "sysctl %s returned(%d): %s\n",
1066 buf, error, error == 0 ? data : "FAILED");
1071 q = strstr(data, "port=");
1073 error = sscanf(q, " port=%d", &usbport);
1076 fprintf(stderr, "failed to read usb port location from '%s'\n", data);
1082 fprintf(stderr, "failed to parse location '%s'\n", data);
1087 fprintf(stderr, "USB port location=%d\n", usbport);
1091 * Now go through it all again but only look at those matching the
1092 * usb port location we found.
1094 for (i = 0; ; i++) {
1095 snprintf(buf, 64, SYSCTL_LOCATION, i);
1097 memset(&data, 0, sizeof(data));
1098 error = sysctlbyname(buf, data, &len, NULL, 0);
1102 q = strstr(data, "port=");
1105 sscanf(q, " port=%d", &usbport0);
1106 if (usbport != usbport0)
1109 /* Try to add ports */
1110 for (p = port_type_list; *p != NULL; p++) {
1111 snprintf(buf, 64, SYSCTL_NAME_TTY, i, *p);
1113 memset(&data, 0, sizeof(data));
1114 error = sysctlbyname(buf, data, &len, NULL, 0);
1117 fprintf(stderr, "sysctl %s returned(%d): %s\n",
1118 buf, error, error == 0 ? data : "FAILED");
1121 list = realloc(list, (list_size + 1) * sizeof(char *));
1122 list[list_size] = malloc(strlen(data) + strlen(TTY_NAME));
1123 sprintf(list[list_size], TTY_NAME, data);
1128 list = realloc(list, (list_size + 1) * sizeof(char *));
1129 list[list_size] = NULL;
1134 do_connect(struct ctx *ctx, const char *tty)
1136 int i, error, needcfg;
1142 fprintf(stderr, "Attempting to open %s\n", tty);
1145 ctx->fd = open(tty, O_RDWR);
1148 fprintf(stderr, "Failed to open %s\n", tty);
1153 tcgetattr(ctx->fd, &t);
1156 t.c_cflag = CLOCAL | CREAD;
1158 tcsetattr(ctx->fd, TCSAFLUSH, &t);
1160 error = at_cmd(ctx, NULL, NULL, NULL, "AT\r\n");
1162 warnx("failed to read from device %s", tty);
1167 error = at_cmd(ctx, "+CPIN: READY", NULL, NULL, "AT+CPIN?\r\n");
1169 ra.val[0].ptr = NULL;
1170 ra.val[1].int32 = 0;
1171 error = at_cmd(ctx, "+CME ERROR", saveresp, &ra, "AT+CPIN?\r\n");
1172 if (ra.val[1].int32 > 0) {
1175 buf = ra.val[0].ptr;
1176 if (strstr(buf[0], "+CME ERROR:") != NULL) {
1178 errx(1, "%s", buf[0]);
1184 if (ctx->pin == NULL) {
1185 errx(1, "device requires PIN");
1188 error = at_cmd(ctx, NULL, NULL, NULL, "AT+CPIN=\"%s\"\r\n",
1191 errx(1, "wrong PIN");
1196 * Check if a PDP context has been configured and configure one
1199 ra.val[0].ptr = NULL;
1200 ra.val[1].int32 = 0;
1201 error = at_cmd(ctx, "+CGDCONT", saveresp, &ra, "AT+CGDCONT?\r\n");
1202 buf = ra.val[0].ptr;
1204 for (i = 0; i < ra.val[1].int32; i++) {
1207 error = sscanf(buf[i], "+CGDCONT: %d,\"%*[^\"]\",\"%[^\"]\"",
1214 if (cid == ctx->pdp_ctx) {
1215 ctx->con_apn = strdup(apn);
1216 if (ctx->pdp_apn != NULL) {
1217 if (strcmp(apn, ctx->pdp_apn) == 0)
1229 if (ctx->pdp_apn == NULL)
1230 errx(1, "device is not configured and no APN given");
1232 error = at_cmd(ctx, NULL, NULL, NULL,
1233 "AT+CGDCONT=%d,,\"%s\"\r\n", ctx->pdp_ctx, ctx->pdp_apn);
1235 errx(1, "failed to configure device");
1237 ctx->con_apn = strdup(ctx->pdp_apn);
1240 if (ctx->pdp_user != NULL || ctx->pdp_pwd != NULL) {
1241 at_cmd(ctx, NULL, NULL, NULL,
1242 "AT$QCPDPP=%d,1,\"%s\",\"%s\"\r\n", ctx->pdp_ctx,
1243 (ctx->pdp_user != NULL) ? ctx->pdp_user : "",
1244 (ctx->pdp_pwd != NULL) ? ctx->pdp_pwd : "");
1247 error = at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1252 at_cmd_async(ctx->fd, "AT+CGREG?\r\n");
1253 at_cmd_async(ctx->fd, "AT+CREG?\r\n");
1255 tmr_add(&timers, 1, 5, tmr_status, ctx);
1260 do_disconnect(struct ctx *ctx)
1262 struct sockaddr_in sin, mask;
1265 at_cmd(ctx, NULL, NULL, NULL, "AT_OWANCALL=%d,0,0\r\n",
1269 /* Remove ip-address from interface */
1270 if (ctx->flags & IPASSIGNED) {
1271 sin.sin_len = mask.sin_len = sizeof(struct sockaddr_in);
1272 memset(&mask.sin_addr.s_addr, 0xff,
1273 sizeof(mask.sin_addr.s_addr));
1274 sin.sin_family = mask.sin_family = AF_INET;
1275 memcpy(&sin.sin_addr.s_addr, &ctx->ip.s_addr,
1276 sizeof(sin.sin_addr.s_addr));
1277 ifaddr_del(ctx->ifnam, (struct sockaddr *)&sin,
1278 (struct sockaddr *)&mask);
1280 if_ifdown(ctx->ifnam);
1281 ctx->flags &= ~IPASSIGNED;
1284 /* Attempt to reset resolv.conf */
1285 set_nameservers(ctx, ctx->resolv_path, 0);
1289 daemonize(struct ctx *ctx)
1294 snprintf(ctx->pidfile, 127, PIDFILE, ctx->ifnam);
1296 pfh = pidfile_open(ctx->pidfile, 0600, &opid);
1298 warn("Cannot create pidfile %s", ctx->pidfile);
1302 if (daemon(0, 0) == -1) {
1303 warn("Cannot daemonize");
1304 pidfile_remove(pfh);
1310 ctx->flags |= FLG_DAEMON;
1312 snprintf(syslog_title, 63, "%s:%s", getprogname(), ctx->ifnam);
1313 openlog(syslog_title, LOG_PID, LOG_USER);
1318 send_disconnect(const char *ifnam)
1325 snprintf(pidfile, 127, PIDFILE, ifnam);
1326 fp = fopen(pidfile, "r");
1328 warn("Cannot open %s", pidfile);
1332 n = fscanf(fp, "%d", &pid);
1335 warnx("unable to read daemon pid");
1339 fprintf(stderr, "Sending SIGTERM to %d\n", pid);
1345 usage(const char *exec)
1348 printf("usage %s [-b] [-n] [-a apn] [-c cid] [-p pin] [-u username] "
1349 "[-k password] [-r resolvpath] [-f tty] interface\n", exec);
1350 printf("usage %s -d interface\n", exec);
1359 main(int argc, char *argv[])
1361 int ch, error, mode;
1362 const char *ifnam = NULL;
1364 char **p, **tty_list;
1367 struct itimerval it;
1369 TAILQ_INIT(&timers.head);
1373 ctx.pdp_apn = ctx.pdp_user = ctx.pdp_pwd = NULL;
1378 ctx.con_oper = NULL;
1379 ctx.con_net_stat = 0;
1380 ctx.con_net_type = -1;
1382 ctx.resolv_path = RESOLV_PATH;
1388 ctx.flags |= FLG_DELAYED;
1390 while ((ch = getopt(argc, argv, "?ha:p:c:u:k:r:f:dbn")) != -1) {
1393 ctx.pdp_apn = argv[optind - 1];
1396 ctx.pdp_ctx = strtol(argv[optind - 1], NULL, 10);
1397 if (ctx.pdp_ctx < 1) {
1398 warnx("Invalid context ID, defaulting to 1");
1403 ctx.pin = argv[optind - 1];
1406 ctx.pdp_user = argv[optind - 1];
1409 ctx.pdp_pwd = argv[optind - 1];
1412 ctx.resolv_path = argv[optind - 1];
1418 ctx.flags &= ~FLG_DELAYED;
1421 ctx.flags |= FLG_NODAEMON;
1424 tty = argv[optind - 1];
1438 errx(1, "no interface given");
1440 ifnam = argv[argc - 1];
1441 ctx.ifnam = strdup(ifnam);
1445 printf("Disconnecting %s\n", ifnam);
1446 send_disconnect(ifnam);
1452 signal(SIGHUP, sig_handle);
1453 signal(SIGINT, sig_handle);
1454 signal(SIGQUIT, sig_handle);
1455 signal(SIGTERM, sig_handle);
1456 signal(SIGALRM, sig_handle);
1458 it.it_interval.tv_sec = 1;
1459 it.it_interval.tv_usec = 0;
1460 it.it_value.tv_sec = 1;
1461 it.it_value.tv_usec = 0;
1462 error = setitimer(ITIMER_REAL, &it, NULL);
1464 errx(1, "setitimer");
1466 tmr_add(&timers, 1, 5, &tmr_watchdog, &ctx);
1467 watchdog_reset(&ctx, 15);
1470 error = do_connect(&ctx, tty);
1472 errx(1, "Failed to open %s", tty);
1475 tty_list = get_tty(&ctx);
1476 if (tty_list == NULL)
1477 errx(1, "%s does not appear to be a uhso device", ifnam);
1479 if (tty_list == NULL) {
1480 fprintf(stderr, "get_tty returned empty list\n");
1482 fprintf(stderr, "tty list:\n");
1483 for (p = tty_list; *p != NULL; p++) {
1484 fprintf(stderr, "\t %s\n", *p);
1488 for (p = tty_list; *p != NULL; p++) {
1489 error = do_connect(&ctx, *p);
1496 errx(1, "Failed to obtain a control port, "
1497 "try specifying one manually");
1500 if (!(ctx.flags & FLG_DELAYED) && !(ctx.flags & FLG_NODAEMON))
1505 FD_SET(ctx.fd, &set);
1508 watchdog_disable(&ctx);
1509 error = select(ctx.fd + 1, &set, NULL, NULL, NULL);
1511 if (running && errno == EINTR)
1513 if (ctx.flags & FLG_WDEXP) {
1514 ctx.flags &= ~FLG_WDEXP;
1515 watchdog_reset(&ctx, 5);
1516 do_disconnect(&ctx);
1517 watchdog_reset(&ctx, 15);
1518 do_connect(&ctx, tty);
1526 if (FD_ISSET(ctx.fd, &set)) {
1527 watchdog_reset(&ctx, 15);
1528 error = at_async(&ctx, &ctx);
1532 FD_SET(ctx.fd, &set);
1534 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED)) {
1535 printf("Status: %s (%s)",
1536 ctx.con_status ? "connected" : "disconnected",
1537 network_access_type[ctx.con_net_type]);
1539 printf(", signal: %d dBm", ctx.dbm);
1544 if (!(ctx.flags & FLG_DAEMON) && (ctx.flags & IPASSIGNED))
1547 signal(SIGHUP, SIG_DFL);
1548 signal(SIGINT, SIG_DFL);
1549 signal(SIGQUIT, SIG_DFL);
1550 signal(SIGTERM, SIG_DFL);
1551 signal(SIGALRM, SIG_IGN);
1553 do_disconnect(&ctx);
1555 if (ctx.flags & FLG_DAEMON) {
1556 pidfile_remove(ctx.pfh);