4 Copyright (C) 2006-2009 Jonathan Zarate
11 #include <ctype.h> // !!TB
16 #include <sys/ioctl.h>
18 #include <dirent.h> //!!TB
19 #include <sys/socket.h>
20 #include <arpa/inet.h>
21 #include <sys/sysinfo.h>
22 #include <sys/types.h>
32 #define _dprintf cprintf
34 #define _dprintf(args...) do { } while(0)
38 int get_wan_proto(void)
40 const char *names
[] = { // order must be synced with def at shared.h
51 p
= nvram_safe_get("wan_proto");
52 for (i
= 0; names
[i
] != NULL
; ++i
) {
53 if (strcmp(p
, names
[i
]) == 0) return i
+ 1;
60 switch (get_wan_proto()) {
70 return ((nvram_match("wl_mode", "sta")) || (nvram_match("wl_mode", "wet")));
73 void notice_set(const char *path
, const char *format
, ...)
79 va_start(args
, format
);
80 vsnprintf(buf
, sizeof(buf
), format
, args
);
83 mkdir("/var/notice", 0755);
84 snprintf(p
, sizeof(p
), "/var/notice/%s", path
);
85 f_write_string(p
, buf
, 0, 0);
86 if (buf
[0]) syslog(LOG_INFO
, "notice[%s]: %s", path
, buf
);
90 // #define _x_dprintf(args...) syslog(LOG_DEBUG, args);
91 #define _x_dprintf(args...) do { } while (0);
103 proto
= get_wan_proto();
104 if (proto
== WP_DISABLED
) return 0;
106 if ((proto
== WP_PPTP
) || (proto
== WP_L2TP
) || (proto
== WP_PPPOE
)) {
107 if (f_read_string("/tmp/ppp/link", buf1
, sizeof(buf1
)) > 0) {
108 // contains the base name of a file in /var/run/ containing pid of a daemon
109 snprintf(buf2
, sizeof(buf2
), "/var/run/%s.pid", buf1
);
110 if (f_read_string(buf2
, buf1
, sizeof(buf1
)) > 0) {
111 name
= psname(atoi(buf1
), buf2
, sizeof(buf2
));
112 if (proto
== WP_PPPOE
) {
113 if (strcmp(name
, "pppoecd") == 0) up
= 1;
116 if (strcmp(name
, "pppd") == 0) up
= 1;
120 _dprintf("%s: error reading %s\n", __FUNCTION__
, buf2
);
123 unlink("/tmp/ppp/link");
124 _x_dprintf("required daemon not found, assuming link is dead\n");
128 _x_dprintf("%s: error reading %s\n", __FUNCTION__
, "/tmp/ppp/link");
131 else if (!nvram_match("wan_ipaddr", "0.0.0.0")) {
135 _x_dprintf("%s: default !up\n", __FUNCTION__
);
138 if ((up
) && ((f
= socket(AF_INET
, SOCK_DGRAM
, 0)) >= 0)) {
139 strlcpy(ifr
.ifr_name
, nvram_safe_get("wan_iface"), sizeof(ifr
.ifr_name
));
140 if (ioctl(f
, SIOCGIFFLAGS
, &ifr
) < 0) {
142 _x_dprintf("%s: SIOCGIFFLAGS\n", __FUNCTION__
);
145 if ((ifr
.ifr_flags
& IFF_UP
) == 0) {
147 _x_dprintf("%s: !IFF_UP\n", __FUNCTION__
);
155 const dns_list_t
*get_dns(void)
157 static dns_list_t dns
;
166 strlcpy(s
, nvram_safe_get("wan_dns"), sizeof(s
));
167 if ((nvram_match("dns_addget", "1")) || (s
[0] == 0)) {
169 snprintf(s
+ n
, sizeof(s
) - n
, " %s", nvram_safe_get("wan_get_dns"));
172 n
= sscanf(s
, "%15s %15s %15s %15s", d
[0], d
[1], d
[2], d
[3]);
173 for (i
= 0; i
< n
; ++i
) {
174 if (inet_pton(AF_INET
, d
[i
], &ia
) > 0) {
175 for (j
= dns
.count
- 1; j
>= 0; --j
) {
176 if (dns
.dns
[j
].s_addr
== ia
.s_addr
) break;
179 dns
.dns
[dns
.count
++].s_addr
= ia
.s_addr
;
180 if (dns
.count
== 3) break;
188 // -----------------------------------------------------------------------------
190 void set_action(int a
)
193 while (f_write("/var/lock/action", &a
, sizeof(a
), 0, 0) != sizeof(a
)) {
195 if (--r
== 0) return;
197 if (a
!= ACT_IDLE
) sleep(2);
200 int check_action(void)
205 while (f_read("/var/lock/action", &a
, sizeof(a
)) != sizeof(a
)) {
207 if (--r
== 0) return ACT_UNKNOWN
;
212 int wait_action_idle(int n
)
215 if (check_action() == ACT_IDLE
) return 1;
221 // -----------------------------------------------------------------------------
223 const char *get_wanip(void)
227 if (!check_wanup()) return "0.0.0.0";
228 switch (get_wan_proto()) {
241 return nvram_safe_get(p
);
244 long get_uptime(void)
255 return (wl_ioctl(nvram_safe_get("wl_ifname"), WLC_GET_RADIO
, &n
, sizeof(n
)) == 0) &&
256 ((n
& WL_RADIO_SW_DISABLE
) == 0);
259 void set_radio(int on
)
263 #ifndef WL_BSS_INFO_VERSION
264 #error WL_BSS_INFO_VERSION
267 #if WL_BSS_INFO_VERSION >= 108
268 n
= on
? (WL_RADIO_SW_DISABLE
<< 16) : ((WL_RADIO_SW_DISABLE
<< 16) | 1);
269 wl_ioctl(nvram_safe_get("wl_ifname"), WLC_SET_RADIO
, &n
, sizeof(n
));
275 n
= on
? 0 : WL_RADIO_SW_DISABLE
;
276 wl_ioctl(nvram_safe_get("wl_ifname"), WLC_SET_RADIO
, &n
, sizeof(n
));
283 int nvram_get_int(const char *key
)
285 return atoi(nvram_safe_get(key
));
289 long nvram_xget_long(const char *name, long min, long max, long def)
296 if ((p != NULL) && (*p != 0)) {
297 n = strtol(p, &e, 0);
298 if ((e != p) && ((*e == 0) || (*e == ' ')) && (n > min) && (n < max)) {
306 int nvram_get_file(const char *key
, const char *fname
, int max
)
314 p
= nvram_safe_get(key
);
317 if ((b
= malloc(base64_decoded_len(n
) + 128)) != NULL
) {
318 n
= base64_decode(p
, b
, n
);
319 if (n
> 0) r
= (f_write(fname
, b
, n
, 0, 0644) == n
);
329 p = nvram_safe_get(key);
332 n = base64_decode(p, b, n);
333 if (n > 0) return (f_write(fname, b, n, 0, 0700) == n);
339 int nvram_set_file(const char *key
, const char *fname
, int max
)
347 if ((len
= f_size(fname
)) > max
) return 0;
350 if (f_read_alloc(fname
, &in
, max
) == max
) {
351 if ((out
= malloc(base64_encoded_len(max
) + 128)) != NULL
) {
352 n
= base64_encode(in
, out
, max
);
366 if (((n = f_read(fname, &a, sizeof(a))) > 0) && (n <= max)) {
367 n = base64_encode(a, b, n);
376 int nvram_contains_word(const char *key
, const char *word
)
378 return (find_word(nvram_safe_get(key
), word
) != NULL
);
381 int nvram_is_empty(const char *key
)
384 return (((p
= nvram_get(key
)) == NULL
) || (*p
== 0));
387 void nvram_commit_x(void)
389 if (!nvram_get_int("debug_nocommit")) nvram_commit();
392 int connect_timeout(int fd
, const struct sockaddr
*addr
, socklen_t len
, int timeout
)
400 if (((flags
= fcntl(fd
, F_GETFL
, 0)) < 0) ||
401 (fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
) < 0)) {
402 _dprintf("%s: error in F_*ETFL %d\n", __FUNCTION__
, fd
);
406 if (connect(fd
, addr
, len
) < 0) {
407 // _dprintf("%s: connect %d = <0\n", __FUNCTION__, fd);
409 if (errno
!= EINPROGRESS
) {
410 _dprintf("%s: error in connect %d errno=%d\n", __FUNCTION__
, fd
, errno
);
419 r
= select(fd
+ 1, NULL
, &fds
, NULL
, &tv
);
421 _dprintf("%s: timeout in select %d\n", __FUNCTION__
, fd
);
425 if (errno
!= EINTR
) {
426 _dprintf("%s: error in select %d\n", __FUNCTION__
, fd
);
434 if ((getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &r
, &n
) < 0) || (r
!= 0)) {
435 _dprintf("%s: error in SO_ERROR %d\n", __FUNCTION__
, fd
);
443 if (fcntl(fd
, F_SETFL
, flags
) < 0) {
444 _dprintf("%s: error in F_*ETFL %d\n", __FUNCTION__
, fd
);
448 // _dprintf("%s: OK %d\n", __FUNCTION__, fd);
455 return time(0) > Y2K;
459 // -----------------------------------------------------------------------------
462 /* Serialize using fcntl() calls
465 #ifndef USB_LOCK_TIMEOUT
466 #define USB_LOCK_TIMEOUT 8
471 if (nvram_get_int("usb_nolock"))
474 const char fn
[] = "/var/lock/usb.lock";
478 if ((lockfd
= open(fn
, O_CREAT
| O_RDWR
, 0666)) < 0)
481 memset(&lock
, 0, sizeof(lock
));
482 lock
.l_type
= F_WRLCK
;
483 lock
.l_pid
= getpid();
484 alarm(USB_LOCK_TIMEOUT
);
486 if (fcntl(lockfd
, F_SETLKW
, &lock
) < 0) {
495 // No proper error processing
496 syslog(LOG_DEBUG
, "Error %d locking %s, proceeding anyway", errno
, fn
);
500 void usb_unlock(int lockfd
)
507 char *detect_fs_type(char *device
)
510 unsigned char buf
[4096];
512 if ((fd
= open(device
, O_RDONLY
)) < 0)
515 if (read(fd
, buf
, sizeof(buf
)) != sizeof(buf
))
523 /* first check for mbr */
524 if (*device
&& device
[strlen(device
) - 1] > '9' &&
525 buf
[510] == 0x55 && buf
[511] == 0xAA && /* signature */
526 ((buf
[0x1be] | buf
[0x1ce] | buf
[0x1de] | buf
[0x1ee]) & 0x7f) == 0) /* boot flags */
531 else if (memcmp(buf
+ 4086, "SWAPSPACE2", 10) == 0 ||
532 memcmp(buf
+ 4086, "SWAP-SPACE", 10) == 0)
537 else if (buf
[0x438] == 0x53 && buf
[0x439] == 0xEF)
539 return ((buf
[0x460] & 0x0008 /* JOURNAL_DEV */) != 0 ||
540 (buf
[0x45c] & 0x0004 /* HAS_JOURNAL */) != 0) ? "ext3" : "ext2";
543 else if (buf
[510] == 0x55 && buf
[511] == 0xAA && /* signature */
544 memcmp(buf
+ 3, "NTFS ", 8) == 0)
549 else if (buf
[510] == 0x55 && buf
[511] == 0xAA && /* signature */
550 buf
[11] == 0 && buf
[12] >= 1 && buf
[12] <= 8 /* sector size 512 - 4096 */ &&
551 buf
[13] != 0 && (buf
[13] & (buf
[13] - 1)) == 0) /* sectors per cluster */
560 /* Execute a function for each disc partition on the specified controller.
562 * Directory /dev/discs/ looks like this:
563 * disc0 -> ../scsi/host0/bus0/target0/lun0/
564 * disc1 -> ../scsi/host1/bus0/target0/lun0/
565 * disc2 -> ../scsi/host2/bus0/target0/lun0/
566 * disc3 -> ../scsi/host2/bus0/target0/lun1/
568 * Scsi host 2 supports multiple drives.
569 * Scsi host 0 & 1 support one drive.
571 * For attached drives, like this. If not attached, there is no "part#" item.
572 * Here, only one drive, with 2 partitions, is plugged in.
573 * /dev/discs/disc0/disc
574 * /dev/discs/disc0/part1
575 * /dev/discs/disc0/part2
576 * /dev/discs/disc1/disc
577 * /dev/discs/disc2/disc
579 * Which is the same as:
580 * /dev/scsi/host0/bus0/target0/lun0/disc
581 * /dev/scsi/host0/bus0/target0/lun0/part1
582 * /dev/scsi/host0/bus0/target0/lun0/part2
583 * /dev/scsi/host1/bus0/target0/lun0/disc
584 * /dev/scsi/host2/bus0/target0/lun0/disc
585 * /dev/scsi/host2/bus0/target0/lun1/disc
587 * Implementation notes:
588 * Various mucking about with a disc that just got plugged in or unplugged
589 * will make the scsi subsystem try a re-validate, and read the partition table of the disc.
590 * This will make sure the partitions show up.
592 * It appears to try to do the revalidate and re-read & update the partition
593 * information when this code does the "readdir of /dev/discs/disc0/?". If the
594 * disc has any mounted partitions the revalidate will be rejected. So the
595 * current partition info will remain. On an unplug event, when it is doing the
596 * readdir's, it will try to do the revalidate as we are doing the readdir's.
597 * But luckily they'll be rejected, otherwise the later partitions will disappear as
598 * soon as we get the first one.
599 * But be very careful! If something goes not exactly right, the partition entries
600 * will disappear before we've had a chance to unmount from them.
602 * To avoid this automatic revalidation, we go through /proc/partitions looking for the partitions
603 * that /dev/discs point to. That will avoid the implicit revalidate attempt.
604 * Which means that we had better do it ourselves. An ioctl BLKRRPART does just that.
607 * If host < 0, do all hosts. If >= 0, it is the host number to do.
608 * When_to_update, flags:
609 * 0x01 = before reading partition info
610 * 0x02 = after reading partition info
614 /* So as not to include linux/fs.h, let's explicitly do this here. */
616 #define BLKRRPART _IO(0x12,95) /* re-read partition table */
619 int exec_for_host(int host
, int when_to_update
, uint flags
, host_exec func
)
622 char bfr
[128]; /* Will be: /dev/discs/disc# */
623 char link
[256]; /* Will be: ../scsi/host#/bus0/target0/lun# that bfr links to. */
624 /* When calling the func, will be: /dev/discs/disc#/part# */
625 char bfr2
[128]; /* Will be: /dev/discs/disc#/disc for the BLKRRPART. */
629 int host_no
; /* SCSI controller/host # */
630 int disc_num
; /* Disc # */
631 int part_num
; /* Parition # */
634 char *mp
; /* Ptr to after any leading ../ path */
639 flags
|= EFH_1ST_HOST
;
640 if ((usb_dev_disc
= opendir(DEV_DISCS_ROOT
))) {
641 while ((dp
= readdir(usb_dev_disc
))) {
642 sprintf(bfr
, "%s/%s", DEV_DISCS_ROOT
, dp
->d_name
);
643 if (strncmp(dp
->d_name
, "disc", 4) != 0)
646 disc_num
= atoi(dp
->d_name
+ 4);
647 len
= readlink(bfr
, link
, sizeof(link
) - 1);
652 cp
= strstr(link
, "/scsi/host");
656 host_no
= atoi(cp
+ 10);
657 if (host
>= 0 && host_no
!= host
)
660 /* We have found a disc that is on this controller.
661 * Loop thru all the partitions on this disc.
662 * The new way, reading thru /proc/partitions.
665 if ((cp
= strstr(link
, "../")) != NULL
)
669 if (when_to_update
& 0x01) {
670 sprintf(bfr2
, "%s/disc", bfr
); /* Prepare for BLKRRPART */
671 if ((fd
= open(bfr2
, O_RDONLY
| O_NONBLOCK
)) >= 0) {
672 ioctl(fd
, BLKRRPART
);
677 flags
|= EFH_1ST_DISC
;
678 if (func
&& (prt_fp
= fopen("/proc/partitions", "r"))) {
679 while (fgets(line
, sizeof(line
) - 2, prt_fp
)) {
680 if (sscanf(line
, " %*s %*s %*s %s", bfr2
) == 1) {
681 if ((cp
= strstr(bfr2
, "/part")) && strncmp(bfr2
, mp
, siz
) == 0) {
682 part_num
= atoi(cp
+ 5);
683 sprintf(line
, "%s/part%d", bfr
, part_num
);
684 result
= (*func
)(line
, host_no
, disc_num
, part_num
, flags
) || result
;
685 flags
&= ~(EFH_1ST_HOST
| EFH_1ST_DISC
);
692 if (when_to_update
& 0x02) {
693 sprintf(bfr2
, "%s/disc", bfr
); /* Prepare for BLKRRPART */
694 if ((fd
= open(bfr2
, O_RDONLY
| O_NONBLOCK
)) >= 0) {
695 ioctl(fd
, BLKRRPART
);
700 closedir(usb_dev_disc
);
705 /* Stolen from the e2fsprogs/ismounted.c.
706 * Find wherever 'file' (actually: device) is mounted.
707 * Either the exact same device-name, or another device-name.
708 * The latter is detected by comparing the rdev or dev&inode.
709 * So aliasing won't fool us---we'll still find if it's mounted.
710 * Return its mnt entry.
711 * In particular, the caller would look at the mnt->mountpoint.
713 struct mntent
*findmntent(char *file
)
717 dev_t file_dev
=0, file_rdev
=0;
721 if ((f
= setmntent("/proc/mounts", "r")) == NULL
)
724 if (stat(file
, &st_buf
) == 0) {
725 if (S_ISBLK(st_buf
.st_mode
)) {
726 file_rdev
= st_buf
.st_rdev
;
728 file_dev
= st_buf
.st_dev
;
729 file_ino
= st_buf
.st_ino
;
732 while ((mnt
= getmntent(f
)) != NULL
) {
733 if (strcmp(file
, mnt
->mnt_fsname
) == 0)
736 if (stat(mnt
->mnt_fsname
, &st_buf
) == 0) {
737 if (S_ISBLK(st_buf
.st_mode
)) {
738 if (file_rdev
&& (file_rdev
== st_buf
.st_rdev
))
741 if (file_dev
&& ((file_dev
== st_buf
.st_dev
) &&
742 (file_ino
== st_buf
.st_ino
)))
753 /****************************************************/
754 /* Use busybox routines to get labels for fat & ext */
755 /* Probe for label the same way that mount does. */
756 /****************************************************/
758 #define VOLUME_ID_LABEL_SIZE 64
759 #define VOLUME_ID_UUID_SIZE 36
760 #define SB_BUFFER_SIZE 0x11000
769 uint64_t seekbuf_off
;
770 char label
[VOLUME_ID_LABEL_SIZE
+1];
771 char uuid
[VOLUME_ID_UUID_SIZE
+1];
774 extern void *volume_id_get_buffer(struct volume_id
*id
, uint64_t off
, size_t len
);
775 extern void volume_id_free_buffer(struct volume_id
*id
);
776 extern int volume_id_probe_ext(struct volume_id
*id
);
777 extern int volume_id_probe_vfat(struct volume_id
*id
);
778 extern int volume_id_probe_linux_swap(struct volume_id
*id
);
780 /* Put the label in *label.
781 * Return 0 if no label found, NZ if there is a label.
783 int find_label(char *dev_name
, char *label
)
787 memset(&id
, 0x00, sizeof(id
));
789 if ((id
.fd
= open(dev_name
, O_RDONLY
)) < 0)
792 if (volume_id_probe_vfat(&id
) == 0)
794 volume_id_get_buffer(&id
, 0, SB_BUFFER_SIZE
);
795 if (volume_id_probe_ext(&id
) == 0 || volume_id_probe_linux_swap(&id
) == 0) { }
796 volume_id_free_buffer(&id
);
799 if (id
.label
[0] != '\0')
800 strcpy(label
, id
.label
);
802 return(label
[0] != '\0');
805 void *xmalloc(size_t siz
)
807 return (malloc(siz
));
810 void *xrealloc(void *old
, size_t size
)
812 return realloc(old
, size
);
815 void volume_id_set_uuid() {}
817 ssize_t
full_read(int fd
, void *buf
, size_t len
)
819 return read(fd
, buf
, len
);