From be3b285c88648c24f7e4b36bebac56a95fa80f7e Mon Sep 17 00:00:00 2001 From: John Crispin Date: Mon, 27 Jul 2015 13:30:13 +0200 Subject: [PATCH 1/1] import v0.1 Signed-off-by: John Crispin --- Makefile | 19 ++ autofs.c | 241 ++++++++++++++++ fs.c | 202 ++++++++++++++ include/autofs.h | 7 + include/fs.h | 11 + include/led.h | 26 ++ include/list.h | 617 ++++++++++++++++++++++++++++++++++++++++ include/log.h | 6 + include/mount.h | 10 + include/signal.h | 9 + include/sys.h | 6 + include/timer.h | 9 + include/ucix.h | 57 ++++ led.c | 65 +++++ log.c | 30 ++ main.c | 27 ++ mount.c | 711 +++++++++++++++++++++++++++++++++++++++++++++++ signal.c | 26 ++ sys.c | 18 ++ timer.c | 60 ++++ uci.c | 107 +++++++ ucix.c | 209 ++++++++++++++ 22 files changed, 2473 insertions(+) create mode 100644 Makefile create mode 100644 autofs.c create mode 100644 fs.c create mode 100644 include/autofs.h create mode 100644 include/fs.h create mode 100644 include/led.h create mode 100644 include/list.h create mode 100644 include/log.h create mode 100644 include/mount.h create mode 100644 include/signal.h create mode 100644 include/sys.h create mode 100644 include/timer.h create mode 100644 include/ucix.h create mode 100644 led.c create mode 100644 log.c create mode 100644 main.c create mode 100644 mount.c create mode 100644 signal.c create mode 100644 sys.c create mode 100644 timer.c create mode 100644 uci.c create mode 100644 ucix.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..38d849e --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +PROG=mountd +OBJS=main.o lib/log.o lib/sys.o lib/autofs.o lib/mount.o lib/timer.o lib/signal.o lib/ucix.o lib/led.o lib/fs.o lib/ucix.o + +LDFLAGS?= +LDFLAGS+=-ldl -luci + +CFLAGS?= +CFLAGS+= -Wall + +all: mountd + +mountd: $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ + +clean: + rm -f lib/*.o *.o $(PROG) + +%.o: %.c + $(CC) $(CFLAGS) -c $^ -o $@ diff --git a/autofs.c b/autofs.c new file mode 100644 index 0000000..cbcada3 --- /dev/null +++ b/autofs.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/log.h" +#include "include/sys.h" +#include "include/timer.h" +#include "include/mount.h" +#include "include/signal.h" +#include "include/ucix.h" + +int fdin = 0; /* data coming out of the kernel */ +int fdout = 0;/* data going into the kernel */ +dev_t dev; + +time_t uci_timeout; +char uci_path[32]; + +void umount_autofs(void) +{ + system_printf("umount %s 2> /dev/null", "/tmp/run/mountd/"); +} + +static int mount_autofs(void) +{ + int pipefd[2]; + struct stat st; + log_printf("trying to mount %s as the autofs root\n", "/tmp/run/mountd/"); + if(is_mounted(0, "/tmp/run/mountd/")) + { + log_printf("%s is already mounted\n", "/tmp/run/mountd/"); + return -1; + } + fdout = fdin = -1; + mkdir("/tmp/run/mountd/", 0555); + if(pipe(pipefd) < 0) + { + log_printf("failed to get kernel pipe\n"); + return -1; + } + if(system_printf("/bin/mount -t autofs -o fd=%d,pgrp=%u,minproto=5,maxproto=5 \"mountd(pid%u)\" %s", + pipefd[1], (unsigned) getpgrp(), getpid(), "/tmp/run/mountd/") != 0) + { + log_printf("unable to mount autofs on %s\n", "/tmp/run/mountd/"); + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + close(pipefd[1]); + fdout = pipefd[0]; + + fdin = open("/tmp/run/mountd/", O_RDONLY); + if(fdin < 0) + { + umount_autofs(); + return -1; + } + stat("/tmp/run/mountd/", &st); + return 0; +} + +static void send_ready(unsigned int wait_queue_token) +{ + if(ioctl(fdin, AUTOFS_IOC_READY, wait_queue_token) < 0) + log_printf("failed to report ready to kernel\n"); +} + +static void send_fail(unsigned int wait_queue_token) +{ + if(ioctl(fdin, AUTOFS_IOC_FAIL, wait_queue_token) < 0) + log_printf("failed to report fail to kernel\n"); +} + +static int autofs_process_request(const struct autofs_v5_packet *pkt) +{ + struct stat st; + log_printf("kernel is requesting a mount -> %s\n", pkt->name); + chdir("/tmp/run/mountd/"); + if (lstat(pkt->name, &st) == -1 || (S_ISDIR(st.st_mode) && st.st_dev == dev)) { + if(!mount_new("/tmp/run/mountd/", (char*)pkt->name)) + { + send_ready(pkt->wait_queue_token); + } else { + send_fail(pkt->wait_queue_token); + log_printf("failed to mount %s\n", pkt->name); + } + } else { + send_ready(pkt->wait_queue_token); + } + chdir("/"); + + return 0; +} + +void expire_proc(void) +{ + struct autofs_packet_expire pkt; + while(ioctl(fdin, AUTOFS_IOC_EXPIRE, &pkt) == 0) + mount_remove("/tmp/run/mountd/", pkt.name); +} + +static int fullread(void *ptr, size_t len) +{ + char *buf = (char *) ptr; + while(len > 0) + { + ssize_t r = read(fdout, buf, len); + if(r == -1) + { + if (errno == EINTR) + continue; + break; + } + buf += r; + len -= r; + } + return len; +} + +static int autofs_in(union autofs_v5_packet_union *pkt) +{ + struct pollfd fds[1]; + + fds[0].fd = fdout; + fds[0].events = POLLIN; + + while(1) + { + if(poll(fds, 2, 1000) == -1) + { + if (errno == EINTR) + continue; + log_printf("failed while trying to read packet from kernel\n"); + return -1; + } + if(fds[0].revents & POLLIN) + return fullread(pkt, sizeof(*pkt)); + } +} + +pid_t autofs_safe_fork(void) +{ + pid_t pid = fork(); + if(!pid) + { + close(fdin); + close(fdout); + } + return pid; +} + +void autofs_cleanup_handler(void) +{ + close(fdin); + close(fdout); + umount_autofs(); +} + +void autofs_init(void) +{ + int kproto_version; + char *p; + struct uci_context *ctx; + signal_init(autofs_cleanup_handler); + ctx = ucix_init("mountd"); + uci_timeout = ucix_get_option_int(ctx, "mountd", "mountd", "timeout", 60); + p = ucix_get_option(ctx, "mountd", "mountd", "path"); + ucix_cleanup(ctx); + if(p) + snprintf(uci_path, 31, p); + else + snprintf(uci_path, 31, "/tmp/mounts/"); + uci_path[31] = '\0'; + mkdir("/tmp/run/", 0555); + mkdir("/tmp/mounts", 0555); + system_printf("rm -rf %s*", uci_path); + if(uci_timeout < 16) + uci_timeout = 16; + umount_autofs(); + mount_init(); + if(mount_autofs() < 0) + { + closelog(); + exit(1); + } + ioctl(fdin, AUTOFS_IOC_PROTOVER, &kproto_version); + if(kproto_version != 5) + { + log_printf("only kernel protocol version 5 is tested. You have %d.\n", + kproto_version); + closelog(); + exit(1); + } + ioctl(fdin, AUTOFS_IOC_SETTIMEOUT, &uci_timeout); + timer_add(expire_proc, 15); +} + +int autofs_loop(void) +{ + chdir("/"); + autofs_init(); + while(1) + { + union autofs_v5_packet_union pkt; + if(autofs_in(&pkt)) + { + continue; + } + log_printf("Got a autofs packet\n"); + if(pkt.hdr.type == autofs_ptype_missing_indirect) + autofs_process_request(&pkt.missing_indirect); + else + log_printf("unknown packet type %d\n", pkt.hdr.type); + poll(0, 0, 200); + } + umount_autofs(); + log_printf("... quitting\n"); + closelog(); + return 0; +} diff --git a/fs.c b/fs.c new file mode 100644 index 0000000..2d6b5ba --- /dev/null +++ b/fs.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/fs.h" + +typedef int (*dfunc)(int); + +unsigned short +get_le_short(void *from) +{ + unsigned char *p = from; + return ((unsigned short)(p[1]) << 8) + + (unsigned short)p[0]; +} + +unsigned int get_le_long(void *from) +{ + unsigned char *p = from; + return ((unsigned int)(p[3]) << 24) + + ((unsigned int)(p[2]) << 16) + + ((unsigned int)(p[1]) << 8) + + (unsigned int)p[0]; +} + +unsigned short get_be_short(void *from) +{ + unsigned char *p = from; + return ((unsigned short)(p[0]) << 8) + + (unsigned short)p[1]; +} + +unsigned int get_be_long(void *from) +{ + unsigned char *p = from; + return ((unsigned int)(p[0]) << 24) + + ((unsigned int)(p[1]) << 16) + + ((unsigned int)(p[2]) << 8) + + (unsigned int)p[3]; +} + +int get_buffer(int fd, unsigned char *b, int offset, int len) +{ + if(lseek(fd, offset, SEEK_SET) != offset) + return -1; + if(read(fd, b, len) != len) + return -1; + return 0; +} + +#define MBR_BUF_SIZE 512 +int detect_mbr(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(MBR_BUF_SIZE); + if(get_buffer(fd, buffer, 0, MBR_BUF_SIZE) != 0) + goto out; + if((buffer[510] == 0x55) && (buffer[511] == 0xAA)) + ret = MBR; +out: + free(buffer); + return ret; +} + +#define EFI_BUF_OFFSET 512 +#define EFI_BUF_SIZE 512 +int detect_efi(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(EFI_BUF_SIZE); + if(get_buffer(fd, buffer, EFI_BUF_OFFSET, EFI_BUF_SIZE) != 0) + goto out; + if(!memcmp(buffer, "EFI PART", 8)) + ret = EFI; +out: + free(buffer); + return ret; +} + +#define EXT2_BUF_SIZE 1024 +int detect_ext23(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(EXT2_BUF_SIZE); + if(get_buffer(fd, buffer, 1024, EXT2_BUF_SIZE) != 0) + goto out; + if(get_le_short(buffer + 56) == 0xEF53) + { + if((get_le_long(buffer + 96) & 0x0008) + || (get_le_long(buffer + 92) & 0x0004)) + ret = EXT3; + else + ret = EXT2; + } +out: + free(buffer); + return ret; +} + +#define FAT_BUF_SIZE 512 +int detect_fat(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(FAT_BUF_SIZE); + if(get_buffer(fd, buffer, 0, FAT_BUF_SIZE) != 0) + goto out; + + if (((((buffer[0] & 0xff) == 0xEB) && ((buffer[2] & 0xff) == 0x90)) || ((buffer[0] & 0xff) == 0xE9)) + && ((buffer[510] & 0xff) == 0x55) /*&& ((buffer[511] & 0xff == 0xAA))*/ + && (memcmp(buffer + 3, "NTFS ", 8))) + ret = FAT; +out: + free(buffer); + return ret; +} + +#define HFSPLUS_VOL_JOURNALED (1 << 13) +#define HFSPLUS_BUF_SIZE 512 +int detect_hfsplus(int fd) +{ + int ret = NONE; + unsigned short magic; + unsigned int journal; + unsigned char *buffer = (unsigned char*)malloc(HFSPLUS_BUF_SIZE); + if(get_buffer(fd, buffer, 1024, HFSPLUS_BUF_SIZE) != 0) + goto out; + magic = get_be_short(buffer); + journal = get_be_long(buffer + 4) & HFSPLUS_VOL_JOURNALED; + if(magic == 0x482B) + { + if(!journal) + ret = HFSPLUS; + // else + // ret = HFSPLUSJOURNAL; + } +out: + free(buffer); + return ret; +} + +#define NTFS_BUF_SIZE 512 +int detect_ntfs(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(NTFS_BUF_SIZE); + if(get_buffer(fd, buffer, 0, NTFS_BUF_SIZE) != 0) + goto out; + if(!memcmp(buffer + 3, "NTFS ", 8)) + ret = NTFS; +out: + free(buffer); + return ret; +} + +#define EXTENDED_BUF_SIZE 512 +int detect_extended(int fd) +{ + int ret = NONE; + unsigned char *buffer = (unsigned char*)malloc(MBR_BUF_SIZE); + if(get_buffer(fd, buffer, 0, 512) != 0) + goto out; + if((((buffer[0] & 0xff) == 0xEB) && ((buffer[2] & 0xff) == 0x90)) || ((buffer[0] & 0xff) == 0xE9)) + goto out; + if(((buffer[510] & 0xff) == 0x55) && ((buffer[511] & 0xff) == 0xAA)) + ret = EXTENDED; +out: + free(buffer); + return ret; +} + +dfunc funcs[] = { + detect_ext23, + detect_fat, + detect_ntfs, + detect_hfsplus, + detect_extended, + detect_efi, + detect_mbr, +}; + +int detect_fs(char *device) +{ + int i = 0; + int ret = NONE; + int fd; + + fd = open(device, O_RDONLY); + if(!fd) + return NONE; + + while((i < 6) && (ret == NONE)) + ret = funcs[i++](fd); + + close(fd); + + return ret; +} diff --git a/include/autofs.h b/include/autofs.h new file mode 100644 index 0000000..62f7704 --- /dev/null +++ b/include/autofs.h @@ -0,0 +1,7 @@ +#ifndef _AUTOFS_H__ +#define _AUTOFS_H__ + +int autofs_loop(void); +pid_t autofs_safe_fork(void); + +#endif diff --git a/include/fs.h b/include/fs.h new file mode 100644 index 0000000..fbe9ffa --- /dev/null +++ b/include/fs.h @@ -0,0 +1,11 @@ +#define NONE -1 +#define MBR 2 +#define EXT2 3 +#define EXT3 4 +#define FAT 5 +#define HFSPLUS 6 +#define EFI 7 +#define NTFS 8 +#define EXTENDED 9 + +int detect_fs(char *device); diff --git a/include/led.h b/include/led.h new file mode 100644 index 0000000..3ee86c5 --- /dev/null +++ b/include/led.h @@ -0,0 +1,26 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2009 John Crispin + */ + +#ifndef _LED_H__ +#define _LED_H__ + +void led_init(char *led); +void led_ping(void); + +#endif diff --git a/include/list.h b/include/list.h new file mode 100644 index 0000000..958e1e7 --- /dev/null +++ b/include/list.h @@ -0,0 +1,617 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include +/** + * container_of - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + */ +/*#ifndef container_of +#define container_of(ptr, type, member) ( \ + (type *)( (char *)ptr - offsetof(type,member) )) +#endif +*/ + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty() on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = NULL; + entry->prev = NULL; +} + +/** + * list_replace - replace old entry by new one + * @old : the element to be replaced + * @new : the new element to insert + * + * If @old was empty, it will be overwritten. + */ +static inline void list_replace(struct list_head *old, + struct list_head *new) +{ + new->next = old->next; + new->next->prev = new; + new->prev = old->prev; + new->prev->next = new; +} + +static inline void list_replace_init(struct list_head *old, + struct list_head *new) +{ + list_replace(old, new); + INIT_LIST_HEAD(old); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_move - delete from one list and add as another's head + * @list: the entry to move + * @head: the head that will precede our entry + */ +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); +} + +/** + * list_move_tail - delete from one list and add as another's tail + * @list: the entry to move + * @head: the head that will follow our entry + */ +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); +} + +/** + * list_is_last - tests whether @list is the last entry in list @head + * @list: the entry to test + * @head: the head of the list + */ +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_count + * @head: the list to test. + */ +static inline int list_count(const struct list_head *head) +{ + int i = 0; + const struct list_head *p = head->next; + while(p != head) + { + p = p->next; + i++; + } + return i; +} + +/** + * list_empty_careful - tests whether a list is empty and not being modified + * @head: the list to test + * + * Description: + * tests whether a list is empty _and_ checks that no other CPU might be + * in the process of modifying either member (next or prev) + * + * NOTE: using list_empty_careful() without synchronization + * can only be safe if the only activity that can happen + * to the list entry is list_del_init(). Eg. it cannot be used + * if another CPU could re-list_add() it. + */ +static inline int list_empty_careful(const struct list_head *head) +{ + struct list_head *next = head->next; + return (next == head) && (next == head->prev); +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +/** + * list_splice_init - join two lists and reinitialise the emptied list. + * @list: the new list to add. + * @head: the place to add it in the first list. + * + * The list at @list is reinitialised + */ +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * __list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + * + * This variant differs from list_for_each() in that it's the + * simplest possible list iteration code, no prefetching is done. + * Use this for code that knows the list to be very short (empty + * or 1 entry) most of the time. + */ +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +/** + * list_for_each_prev - iterate over a list backwards + * @pos: the &struct list_head to use as a loop cursor. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry + * @pos: the &struct list_head to use as a loop cursor. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_prev_safe(pos, n, head) \ + for (pos = (head)->prev, n = pos->prev; \ + pos != (head); \ + pos = n, n = pos->prev) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_reverse - iterate backwards over list of given type. + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue() + * @pos: the type * to use as a start point + * @head: the head of the list + * @member: the name of the list_struct within the struct. + * + * Prepares a pos entry for use as a start point in list_for_each_entry_continue(). + */ +#define list_prepare_entry(pos, head, member) \ + ((pos) ? : list_entry(head, typeof(*pos), member)) + +/** + * list_for_each_entry_continue - continue iteration over list of given type + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Continue to iterate over list of given type, continuing after + * the current position. + */ +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_continue_reverse - iterate backwards from the given point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Start to iterate over list of given type backwards, continuing after + * the current position. + */ +#define list_for_each_entry_continue_reverse(pos, head, member) \ + for (pos = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) + +/** + * list_for_each_entry_from - iterate over list of given type from the current point + * @pos: the type * to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing from current position. + */ +#define list_for_each_entry_from(pos, head, member) \ + for (; &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_continue + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type, continuing after current point, + * safe against removal of list entry. + */ +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_from + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate over list of given type from current point, safe against + * removal of list entry. + */ +#define list_for_each_entry_safe_from(pos, n, head, member) \ + for (n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +/** + * list_for_each_entry_safe_reverse + * @pos: the type * to use as a loop cursor. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Iterate backwards over list of given type, safe against removal + * of list entry. + */ +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) + +/* + * Double linked lists with a single pointer list head. + * Mostly useful for hash tables where the two pointer list head is + * too wasteful. + * You lose the ability to access the tail in O(1). + */ + +struct hlist_head { + struct hlist_node *first; +}; + +struct hlist_node { + struct hlist_node *next, **pprev; +}; + +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } +#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) +static inline void INIT_HLIST_NODE(struct hlist_node *h) +{ + h->next = NULL; + h->pprev = NULL; +} + +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; +} + +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; +} + +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; +} + +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = NULL; + n->pprev = NULL; +} + +static inline void hlist_del_init(struct hlist_node *n) +{ + if (!hlist_unhashed(n)) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } +} + + +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; +} + + +/* next must be != NULL */ +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; +} + +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; + + if(next->next) + next->next->pprev = &next->next; +} + +#define hlist_entry(ptr, type, member) container_of(ptr,type,member) + +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos; pos = pos->next) + +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos; pos = n) + +/** + * hlist_for_each_entry - iterate over list of given type + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_continue - iterate over a hlist continuing after current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_from - iterate over a hlist continuing from current point + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = pos->next) + +/** + * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @tpos: the type * to use as a loop cursor. + * @pos: the &struct hlist_node to use as a loop cursor. + * @n: another &struct hlist_node to use as temporary storage + * @head: the head for your list. + * @member: the name of the hlist_node within the struct. + */ +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ n = pos->next; 1; }) && \ + ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \ + pos = n) + +#endif diff --git a/include/log.h b/include/log.h new file mode 100644 index 0000000..0d78b44 --- /dev/null +++ b/include/log.h @@ -0,0 +1,6 @@ +#ifndef _LOG_H__ +#define _LOG_H__ +void log_start(void); +void log_stop(void); +void log_printf(char *fmt, ...); +#endif diff --git a/include/mount.h b/include/mount.h new file mode 100644 index 0000000..5683e47 --- /dev/null +++ b/include/mount.h @@ -0,0 +1,10 @@ +#ifndef _MOUNT_H__ +#define _MOUNT_H__ + +char* is_mounted(char *block, char *path); +int mount_new(char *path, char *name); +int mount_remove(char *path, char *name); +void mount_dump_list(void); +void mount_init(void); + +#endif diff --git a/include/signal.h b/include/signal.h new file mode 100644 index 0000000..6ab3130 --- /dev/null +++ b/include/signal.h @@ -0,0 +1,9 @@ +#ifndef _SIGNAL_H__ +#define _SIGNAL_H__ + +void signal_init(void (*_crtlc_cb)(void)); + +int signal_usr1(void); +int signal_usr2(void); + +#endif diff --git a/include/sys.h b/include/sys.h new file mode 100644 index 0000000..cb34c7a --- /dev/null +++ b/include/sys.h @@ -0,0 +1,6 @@ +#ifndef _SYS_H__ +#define _SYS_H__ + +int system_printf(char *fmt, ...); + +#endif diff --git a/include/timer.h b/include/timer.h new file mode 100644 index 0000000..ed42798 --- /dev/null +++ b/include/timer.h @@ -0,0 +1,9 @@ +#ifndef _TIMER_H__ +#define _TIMER_H__ + +typedef void (*timercb_t)(void); + +void timer_init(void); +void timer_add(timercb_t timercb, int timeout); + +#endif diff --git a/include/ucix.h b/include/ucix.h new file mode 100644 index 0000000..dc9b3b6 --- /dev/null +++ b/include/ucix.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Copyright (C) 2008 John Crispin + */ + +#ifndef _UCI_H__ +#define _UCI_H__ +#include +#include "list.h" + +struct ucilist { + struct list_head list; + char *val; +}; + +struct uci_context* ucix_init(const char *config_file); +struct uci_context* ucix_init_path(const char *path, const char *config_file); +void ucix_cleanup(struct uci_context *ctx); +int ucix_save(struct uci_context *ctx, const char *p); +int ucix_save_state(struct uci_context *ctx, const char *p); +char* ucix_get_option(struct uci_context *ctx, + const char *p, const char *s, const char *o); +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l); +int ucix_get_option_int(struct uci_context *ctx, + const char *p, const char *s, const char *o, int def); +void ucix_add_section(struct uci_context *ctx, + const char *p, const char *s, const char *t); +void ucix_add_option(struct uci_context *ctx, + const char *p, const char *s, const char *o, const char *t); +void ucix_add_option_int(struct uci_context *ctx, + const char *p, const char *s, const char *o, int t); +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv); +void ucix_for_each_section_option(struct uci_context *ctx, + const char *p, const char *s, + void (*cb)(const char*, const char*, void*), void *priv); +int ucix_commit(struct uci_context *ctx, const char *p); +void ucix_revert(struct uci_context *ctx, + const char *p, const char *s, const char *o); +void ucix_del(struct uci_context *ctx, const char *p, + const char *s, const char *o); +#endif diff --git a/led.c b/led.c new file mode 100644 index 0000000..6ff01f7 --- /dev/null +++ b/led.c @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2009 John Crispin + */ + + +#include +#include +#include +#include +#include "include/ucix.h" +#include "include/log.h" +#include "include/timer.h" + +char usbled[16]; + +void led_ping(void) +{ + FILE *fp; + static int last = 0; + static struct uci_context *ctx; + int mounted, count; + int led = 1; + char path[256]; + ctx = ucix_init("mountd"); + mounted = ucix_get_option_int(ctx, "mountd", "mountd", "mounted", 0); + count = ucix_get_option_int(ctx, "mountd", "mountd", "count", 0); + ucix_cleanup(ctx); + if(!count) + led = 0; + if(count && !mounted) + led = 1; + if(count && mounted) + last = led = (last + 1) % 2; + snprintf(path, 256, "/sys/class/leds/%s/brightness", usbled); + fp = fopen(path, "w"); + if(fp) + { + fprintf(fp, "%d", led); + fclose(fp); + } +} + +void led_init(char *led) +{ + if(led) + { + strncpy(usbled, led, 16); + timer_add(led_ping, 1); + } +} diff --git a/log.c b/log.c new file mode 100644 index 0000000..263e1d5 --- /dev/null +++ b/log.c @@ -0,0 +1,30 @@ +#include +#include +#include + +extern int daemonize; + +void log_start(void) +{ + openlog("mountd", LOG_PID, LOG_DAEMON); +} + +void log_stop(void) +{ + closelog(); +} + +void log_printf(char *fmt, ...) +{ + char p[256]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(p, 256, fmt, ap); + va_end(ap); + + if(daemonize) + syslog(10, p); + else + printf(p); +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..2c08bde --- /dev/null +++ b/main.c @@ -0,0 +1,27 @@ +#include +#include +#include +#include + +#include "include/log.h" +#include "include/sys.h" +#include "include/timer.h" +#include "include/autofs.h" +#include "include/led.h" + +int daemonize = 0; + +int main(int argc, char *argv[]) +{ + daemon(0,0); + daemonize = 1; + log_start(); + log_printf("Starting OpenWrt (auto)mountd V1\n"); + timer_init(); + led_init(0); + if (geteuid() != 0) { + fprintf(stderr, "This program must be run by root.\n"); + return 1; + } + return autofs_loop(); +} diff --git a/mount.c b/mount.c new file mode 100644 index 0000000..16c3529 --- /dev/null +++ b/mount.c @@ -0,0 +1,711 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/log.h" +#include "include/list.h" +#include "include/sys.h" +#include "include/signal.h" +#include "include/timer.h" +#include "include/autofs.h" +#include "include/ucix.h" +#include "include/fs.h" + +int mount_new(char *path, char *dev); + +struct list_head mounts; + +struct mount { + struct list_head list; + char name[64]; + char dev[64]; + char serial[64]; + char vendor[64]; + char model[64]; + char rev[64]; + int mounted; + int ignore; + char size[64]; + char sector_size[64]; + int fs; +}; + +char *fs_names[] = { + "", + "", + "MBR", + "EXT2", + "EXT3", + "FAT", + "HFSPLUS", + "NTFS" +}; + +#define MAX_MOUNTED 32 +#define MAX_MOUNT_NAME 32 + +char mounted[MAX_MOUNTED][3][MAX_MOUNT_NAME]; +int mounted_count = 0; +extern char uci_path[32]; + +void mount_dump_uci_state(void) +{ + struct uci_context *ctx; + struct list_head *p; + char mountd[] = {"mountd"}; + char type[] = {"mountd_disc"}; + int mounted = 0; + unsigned long long int size = 0; + unlink("/var/state/mountd"); + ctx = ucix_init("mountd"); + uci_set_savedir(ctx, "/var/state/"); + ucix_add_option_int(ctx, mountd, mountd, "count", list_count(&mounts)); + list_for_each(p, &mounts) + { + struct mount *q = container_of(p, struct mount, list); + char t[64]; + if(q->fs == EXTENDED) + continue; + ucix_add_section(ctx, mountd, q->serial, type); + strcpy(t, q->dev); + t[3] = '\0'; + ucix_add_option(ctx, mountd, q->serial, "disc", t); + ucix_add_option(ctx, mountd, q->serial, "sector_size", q->sector_size); + snprintf(t, 64, "part%dmounted", atoi(&q->dev[3])); + ucix_add_option(ctx, mountd, q->serial, t, (q->mounted)?("1"):("0")); + ucix_add_option(ctx, mountd, q->serial, "vendor", q->vendor); + ucix_add_option(ctx, mountd, q->serial, "model", q->model); + ucix_add_option(ctx, mountd, q->serial, "rev", q->rev); + snprintf(t, 64, "size%d", atoi(&q->dev[3])); + ucix_add_option(ctx, mountd, q->serial, t, q->size); + if(q->fs > MBR && q->fs <= NTFS) + { + snprintf(t, 64, "fs%d", atoi(&q->dev[3])); + ucix_add_option(ctx, mountd, q->serial, t, fs_names[q->fs]); + } + if(q->mounted) + mounted++; + if((!q->ignore) && q->size && q->sector_size) + size = size + (((unsigned long long int)atoi(q->size)) * ((unsigned long long int)atoi(q->sector_size))); + } + ucix_add_option_int(ctx, mountd, mountd, "mounted", mounted); + ucix_add_option_int(ctx, mountd, mountd, "total", size); + system_printf("echo -n %llu > /tmp/run/mountd_size", size); + ucix_save_state(ctx, "mountd"); + ucix_cleanup(ctx); +} + +struct mount* mount_find(char *name, char *dev) +{ + struct list_head *p; + list_for_each(p, &mounts) + { + struct mount *q = container_of(p, struct mount, list); + if(name) + if(!strcmp(q->name, name)) + return q; + if(dev) + if(!strcmp(q->dev, dev)) + return q; + } + return 0; +} + +void mount_add_list(char *name, char *dev, char *serial, + char *vendor, char *model, char *rev, int ignore, char *size, char *sector_size, int fs) +{ + struct mount *mount; + char tmp[64], tmp2[64]; + if(fs <= MBR || fs > NTFS) + return; + mount = malloc(sizeof(struct mount)); + INIT_LIST_HEAD(&mount->list); + strncpy(mount->vendor, vendor, 64); + strncpy(mount->model, model, 64); + strncpy(mount->rev, rev, 64); + strncpy(mount->name, name, 64); + strncpy(mount->dev, dev, 64); + strncpy(mount->serial, serial, 64); + strncpy(mount->size, size, 64); + strncpy(mount->sector_size, sector_size, 64); + mount->ignore = ignore; + mount->mounted = 0; + mount->fs = fs; + list_add(&mount->list, &mounts); + if((!mount->ignore) && (mount->fs > MBR) && (mount->fs <= NTFS)) + { + log_printf("new mount : %s -> %s (%s)\n", name, dev, fs_names[mount->fs]); + snprintf(tmp, 64, "%s%s", uci_path, name); + snprintf(tmp2, 64, "/tmp/run/mountd/%s", dev); + symlink(tmp2, tmp); + mount_new("/tmp/run/mountd/", dev); + } +} + +int mount_check_disc(char *disc) +{ + FILE *fp = fopen("/proc/mounts", "r"); + char tmp[256]; + int avail = -1; + if(!fp) + { + log_printf("error reading /proc/mounts"); + fclose(fp); + return avail; + } + while((fgets(tmp, 256, fp) > 0) && (avail == -1)) + { + char *t; + char tmp2[32]; + t = strstr(tmp, " "); + if(t) + { + int l; + *t = '\0'; + l = snprintf(tmp2, 31, "/dev/%s", disc); + + if(!strncmp(tmp, tmp2, l)) + avail = 0; + } + } + fclose(fp); + return avail; +} + +int mount_wait_for_disc(char *disc) +{ + int i = 10; + while(i--) + { + int ret = mount_check_disc(disc); + if(!ret) + return ret; + poll(0, 0, 100); + } + return -1; +} + +int mount_new(char *path, char *dev) +{ + struct mount *mount; + char tmp[256]; + int ret = 1; + pid_t pid; + mount = mount_find(0, dev); + if(!mount) + { + log_printf("request for invalid path %s%s\n", path, dev); + return -1; + } + if(mount->ignore || mount->mounted || mount->fs == EXTENDED) + return -1; + snprintf(tmp, 256, "%s%s", path, mount->dev); + log_printf("mounting %s\n", tmp); + mkdir(tmp, 777); + + pid = autofs_safe_fork(); + if(!pid) + { + if(mount->fs == FAT) + { + log_printf("mount -t vfat -o rw,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp); + ret = system_printf("mount -t vfat -o rw,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp); + } + if(mount->fs == EXT3) + { + log_printf("mount -t ext3 -o rw,defaults /dev/%s %s", mount->dev, tmp); + ret = system_printf("mount -t ext3 -o rw,defaults /dev/%s %s", mount->dev, tmp); + } + if(mount->fs == EXT2) + { + log_printf("mount -t ext2 -o rw,defaults /dev/%s %s", mount->dev, tmp); + ret = system_printf("mount -t ext2 -o rw,defaults /dev/%s %s", mount->dev, tmp); + } + if(mount->fs == HFSPLUS) + { + log_printf("mount -t hfsplus -o rw,defaults,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp); + ret = system_printf("mount -t hfsplus -o rw,defaults,uid=1000,gid=1000 /dev/%s %s", mount->dev, tmp); + } + if(mount->fs == NTFS) + { + log_printf("ntfs-3g /dev/%s %s -o force", mount->dev, tmp); + ret = system_printf("ntfs-3g /dev/%s %s -o force", mount->dev, tmp); + } + exit(WEXITSTATUS(ret)); + } + pid = waitpid(pid, &ret, 0); + ret = WEXITSTATUS(ret); + log_printf("----------> mount ret = %d\n", ret); + if(ret && (ret != 0xff)) + return -1; + if(mount_wait_for_disc(mount->dev) == 0) + { + mount->mounted = 1; + mount_dump_uci_state(); + } else return -1; + return 0; +} + +int mount_remove(char *path, char *dev) +{ + struct mount *mount; + char tmp[256]; + int ret; + snprintf(tmp, 256, "%s%s", path, dev); + log_printf("%s has expired... unmounting\n", tmp); + ret = system_printf("/bin/umount %s", tmp); + if(ret != 0) + return 0; + rmdir(tmp); + mount = mount_find(0, dev); + if(mount) + mount->mounted = 0; + log_printf("finished unmounting\n"); + mount_dump_uci_state(); + return 0; +} + +int dir_sort(const void *a, const void *b) +{ + return 0; +} + +int dir_filter(const struct dirent *a) +{ + if(strstr(a->d_name, ":")) + return 1; + return 0; +} + +char* mount_get_serial(char *dev) +{ + static char tmp[64]; + static char tmp2[64]; + int disc; + static struct hd_driveid hd; + int i; + static char *serial; + snprintf(tmp, 64, "/dev/%s", dev); + disc = open(tmp, O_RDONLY); + if(!disc) + { + log_printf("Trying to open unknown disc\n"); + return 0; + } + i = ioctl(disc, HDIO_GET_IDENTITY, &hd); + close(disc); + if(!i) + serial = (char*)hd.serial_no; + /* if we failed, it probably a usb storage device */ + /* there must be a better way for this */ + if(i) + { + struct dirent **namelist; + int n = scandir("/sys/bus/scsi/devices/", &namelist, dir_filter, dir_sort); + if(n > 0) + { + while(n--) + { + char *t = strstr(namelist[n]->d_name, ":"); + if(t) + { + int id; + struct stat buf; + char tmp3[64]; + int ret; + *t = 0; + id = atoi(namelist[n]->d_name); + *t = ':'; + sprintf(tmp3, "/sys/bus/scsi/devices/%s/block:%s/", namelist[n]->d_name, dev); + ret = stat(tmp3, &buf); + if(ret) + { + sprintf(tmp3, "/sys/bus/scsi/devices/%s/block/%s/", namelist[n]->d_name, dev); + ret = stat(tmp3, &buf); + } + if(!ret) + { + FILE *fp; + snprintf(tmp2, 64, "/proc/scsi/usb-storage/%d", id); + fp = fopen(tmp2, "r"); + if(fp) + { + while(fgets(tmp2, 64, fp) > 0) + { + serial = strstr(tmp2, "Serial Number:"); + if(serial) + { + serial += strlen("Serial Number: "); + serial[strlen(serial) - 1] = '\0'; + i = 0; + break; + } + } + fclose(fp); + } + } + } + free(namelist[n]); + } + free(namelist); + } + } + if(i) + { + log_printf("could not find a serial number for the device %s\n", dev); + } else { + /* serial string id is cheap, but makes the discs anonymous */ + unsigned char uniq[6]; + int l = strlen(serial); + int i; + static char disc_id[13]; + memset(disc_id, 0, 13); + memset(uniq, 0, 6); + for(i = 0; i < l; i++) + { + uniq[i%6] += serial[i]; + } + sprintf(disc_id, "%08X%02X%02X", *((unsigned int*)&uniq[0]), uniq[4], uniq[5]); + //log_printf("Serial number - %s %s\n", serial, disc_id); + return disc_id; + } + return 0; +} + +void mount_dev_add(char *dev) +{ + struct mount *mount = mount_find(0, dev); + if(!mount) + { + char node[64]; + char name[64]; + int ignore = 0; + char *s; + char tmp[64]; + char tmp2[64]; + char *p; + struct uci_context *ctx; + char vendor[64]; + char model[64]; + char rev[64]; + char size[64]; + char sector_size[64]; + FILE *fp; + strcpy(name, dev); + name[3] = '\0'; + s = mount_get_serial(name); + if(!s) + return; + snprintf(tmp, 64, "part%s", &dev[3]); + snprintf(node, 64, "Disc-%s", &dev[2]); + if(node[5] >= 'a' && node[5] <= 'z') + { + node[5] -= 'a'; + node[5] += 'A'; + } + ctx = ucix_init("mountd"); + p = ucix_get_option(ctx, "mountd", s, tmp); + ucix_cleanup(ctx); + if(p) + { + if(strlen(p) == 1) + { + if(*p == '0') + ignore = 1; + } else { + snprintf(node, 64, "%s", p); + } + } + strcpy(name, dev); + name[3] = '\0'; + snprintf(tmp, 64, "/sys/class/block/%s/device/model", name); + fp = fopen(tmp, "r"); + if(!fp) + { + snprintf(tmp, 64, "/sys/block/%s/device/model", name); + fp = fopen(tmp, "r"); + } + if(!fp) + snprintf(model, 64, "unknown"); + else { + fgets(model, 64, fp); + model[strlen(model) - 1] = '\0';; + fclose(fp); + } + snprintf(tmp, 64, "/sys/class/block/%s/device/vendor", name); + fp = fopen(tmp, "r"); + if(!fp) + { + snprintf(tmp, 64, "/sys/block/%s/device/vendor", name); + fp = fopen(tmp, "r"); + } + if(!fp) + snprintf(vendor, 64, "unknown"); + else { + fgets(vendor, 64, fp); + vendor[strlen(vendor) - 1] = '\0'; + fclose(fp); + } + snprintf(tmp, 64, "/sys/class/block/%s/device/rev", name); + fp = fopen(tmp, "r"); + if(!fp) + { + snprintf(tmp, 64, "/sys/block/%s/device/rev", name); + fp = fopen(tmp, "r"); + } + if(!fp) + snprintf(rev, 64, "unknown"); + else { + fgets(rev, 64, fp); + rev[strlen(rev) - 1] = '\0'; + fclose(fp); + } + snprintf(tmp, 64, "/sys/class/block/%s/size", dev); + fp = fopen(tmp, "r"); + if(!fp) + { + snprintf(tmp, 64, "/sys/block/%s/%s/size", name, dev); + fp = fopen(tmp, "r"); + } + if(!fp) + snprintf(size, 64, "unknown"); + else { + fgets(size, 64, fp); + size[strlen(size) - 1] = '\0'; + fclose(fp); + } + strcpy(tmp2, dev); + tmp2[3] = '\0'; + snprintf(tmp, 64, "/sys/block/%s/queue/hw_sector_size", tmp2); + fp = fopen(tmp, "r"); + if(!fp) + snprintf(sector_size, 64, "unknown"); + else { + fgets(sector_size, 64, fp); + sector_size[strlen(sector_size) - 1] = '\0'; + fclose(fp); + } + snprintf(tmp, 64, "/dev/%s", dev); + mount_add_list(node, dev, s, vendor, model, rev, ignore, size, sector_size, detect_fs(tmp)); + mount_dump_uci_state(); + } +} + +void mount_dev_del(char *dev) +{ + struct mount *mount = mount_find(0, dev); + char tmp[256]; + if(mount) + { + if(mount->mounted) + { + snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->name); + log_printf("%s has dissappeared ... unmounting\n", tmp); + snprintf(tmp, 256, "%s%s", "/tmp/run/mountd/", mount->dev); + system_printf("/bin/umount %s", tmp); + rmdir(tmp); + snprintf(tmp, 64, "%s%s", uci_path, mount->name); + unlink(tmp); + mount_dump_uci_state(); + } + } +} + +void mount_dump_list(void) +{ + struct list_head *p; + list_for_each(p, &mounts) + { + struct mount *q = container_of(p, struct mount, list); + log_printf("* %s %s %d\n", q->name, q->dev, q->mounted); + } +} + +char* is_mounted(char *block, char *path) +{ + int i; + for(i = 0; i < mounted_count; i++) + { + if(block) + if(!strncmp(&mounted[i][0][0], block, strlen(&mounted[i][0][0]))) + return &mounted[i][0][0]; + if(path) + if(!strncmp(&mounted[i][1][1], &path[1], strlen(&mounted[i][1][0]))) + return &mounted[i][0][0]; + } + return 0; +} + +void mount_check_mount_list(void) +{ + FILE *fp = fopen("/proc/mounts", "r"); + char tmp[256]; + + if(!fp) + { + log_printf("error reading /proc/mounts"); + fclose(fp); + return; + } + mounted_count = 0; + while(fgets(tmp, 256, fp) > 0) + { + char *t, *t2; + t = strstr(tmp, " "); + if(t) + { + *t = '\0'; + t++; + } else t = tmp; + strncpy(&mounted[mounted_count][0][0], tmp, MAX_MOUNT_NAME); + t2 = strstr(t, " "); + if(t2) + { + *t2 = '\0'; + t2++; + } else t2 = t; + strncpy(&mounted[mounted_count][1][0], t, MAX_MOUNT_NAME); + t = strstr(t2, " "); + if(t) + { + *t = '\0'; + t++; + } else t = tmp; + strncpy(&mounted[mounted_count][2][0], t2, MAX_MOUNT_NAME); + /* printf("%s %s %s\n", + mounted[mounted_count][0], + mounted[mounted_count][1], + mounted[mounted_count][2]);*/ + if(mounted_count < MAX_MOUNTED - 1) + mounted_count++; + else + log_printf("found more than %d mounts \n", MAX_MOUNTED); + } + fclose(fp); +} + +/* FIXME: we need ore intelligence here */ +int dir_filter2(const struct dirent *a) +{ + if(/*strcmp(a->d_name, "sda") &&*/(!strncmp(a->d_name, "sd", 2))) + return 1; + return 0; +} +#define MAX_BLOCK 64 +char block[MAX_BLOCK][MAX_BLOCK]; +int blk_cnt = 0; + +int check_block(char *b) +{ + int i; + for(i = 0; i < blk_cnt; i++) + { + if(!strcmp(block[i], b)) + return 1; + } + return 0; +} + +void mount_enum_drives(void) +{ + struct dirent **namelist, **namelist2; + int i, n = scandir("/sys/block/", &namelist, dir_filter2, dir_sort); + struct list_head *p; + blk_cnt = 0; + if(n > 0) + { + while(n--) + { + if(blk_cnt < MAX_BLOCK) + { + int m; + char tmp[64]; + snprintf(tmp, 64, "/sys/block/%s/", namelist[n]->d_name); + m = scandir(tmp, &namelist2, dir_filter2, dir_sort); + while(m--) + { + strncpy(&block[blk_cnt][0], namelist2[m]->d_name, MAX_BLOCK); + blk_cnt++; + free(namelist2[m]); + } + free(namelist2); + } + free(namelist[n]); + } + free(namelist); + } + p = mounts.next; + while(p != &mounts) + { + struct mount *q = container_of(p, struct mount, list); + char tmp[64]; + struct uci_context *ctx; + int del = 0; + char *t; + snprintf(tmp, 64, "part%s", &q->dev[3]); + ctx = ucix_init("mountd"); + t = ucix_get_option(ctx, "mountd", q->serial, tmp); + ucix_cleanup(ctx); + if(t && !q->mounted) + { + if(!strcmp(t, "0")) + { + if(!q->ignore) + del = 1; + } else if(!strcmp(t, "1")) + { + if(strncmp(q->name, "Disc-", 5)) + del = 1; + } else if(strcmp(q->name, t)) + { + del = 1; + } + } + if(!check_block(q->dev)||del) + { + mount_dev_del(q->dev); + p->prev->next = p->next; + p->next->prev = p->prev; + p = p->next; + log_printf("removing %s\n", q->dev); + snprintf(tmp, 64, "%s%s", uci_path, q->name); + unlink(tmp); + system_printf("/etc/mountd/event remove %s %s", q->dev, q->name); + free(q); + mount_dump_uci_state(); + system_printf("/etc/fonstated/ReloadSamba"); + } else p = p->next; + } + + for(i = 0; i < blk_cnt; i++) + mount_dev_add(block[i]); +} + +void mount_check_enum(void) +{ + waitpid(-1, 0, WNOHANG); + mount_enum_drives(); +} + +void mount_init(void) +{ + INIT_LIST_HEAD(&mounts); + timer_add(mount_check_mount_list, 2); + timer_add(mount_check_enum, 1); + mount_check_mount_list(); +} diff --git a/signal.c b/signal.c new file mode 100644 index 0000000..1772fb4 --- /dev/null +++ b/signal.c @@ -0,0 +1,26 @@ +#include +#include +#include + +#include "include/log.h" +#include "include/list.h" +#include "include/led.h" + +void (*crtlc_cb)(void) = 0; + +void handlerINT(int s) +{ + log_printf("caught sig int ... cleaning up\n"); + if(crtlc_cb) + crtlc_cb(); + exit(0); +} + +void signal_init(void (*_crtlc_cb)(void)) +{ + struct sigaction s; + crtlc_cb = _crtlc_cb; + s.sa_handler = handlerINT; + s.sa_flags = 0; + sigaction(SIGINT, &s, NULL); +} diff --git a/sys.c b/sys.c new file mode 100644 index 0000000..2b81a53 --- /dev/null +++ b/sys.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +#include "include/log.h" + +int system_printf(char *fmt, ...) +{ + char p[256]; + va_list ap; + int r; + va_start(ap, fmt); + vsnprintf(p, 256, fmt, ap); + va_end(ap); + r = system(p); + return r; +} diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..f6901f7 --- /dev/null +++ b/timer.c @@ -0,0 +1,60 @@ +#include +#include +#include + +#include "include/log.h" +#include "include/list.h" +#include "include/timer.h" +#include "include/ucix.h" + +/* when using this file, alarm() is used */ + +struct list_head timers; + +struct timer { + struct list_head list; + int count; + int timeout; + timercb_t timercb; +}; + +void timer_add(timercb_t timercb, int timeout) +{ + struct timer *timer; + timer = malloc(sizeof(struct timer)); + if(!timer) + { + log_printf("unable to get timer buffer\n"); + return; + } + timer->count = 0; + timer->timeout = timeout; + timer->timercb = timercb; + INIT_LIST_HEAD(&timer->list); + list_add(&timer->list, &timers); +} + +void timer_proc(int signo) +{ + struct list_head *p; + list_for_each(p, &timers) + { + struct timer *q = container_of(p, struct timer, list); + q->count++; + if(!(q->count%q->timeout)) + { + q->timercb(); + } + } + alarm(1); +} + +void timer_init(void) +{ + struct sigaction s; + INIT_LIST_HEAD(&timers); + s.sa_handler = timer_proc; + s.sa_flags = 0; + sigaction(SIGALRM, &s, NULL); + alarm(1); +} diff --git a/uci.c b/uci.c new file mode 100644 index 0000000..4f7adb8 --- /dev/null +++ b/uci.c @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + * + * Provided by fon.com + * Copyright (C) 2008 John Crispin + */ + +#include +#include + +#include "include/log.h" +#include + +struct uci_package *p = NULL; + +struct uci_context* uci_init(char *config_file) +{ + struct uci_context *ctx = uci_alloc_context(); + uci_add_history_path(ctx, "/var/state"); + if(uci_load(ctx, config_file, &p) != UCI_OK) + { + log_printf("/etc/config/%s is missing or corrupt\n", config_file); + exit(-1); + } + return ctx; +} + +void uci_cleanup(struct uci_context *ctx) +{ + uci_unload(ctx, p); + uci_free_context(ctx); +} + +void uci_save_state(struct uci_context *ctx) +{ + uci_save(ctx, p); +} + +char* uci_get_option(struct uci_context *ctx, char *section, char *option) +{ + struct uci_element *e = NULL; + char *value = NULL; + struct uci_ptr ptr; + + memset(&ptr, 0, sizeof(ptr)); + ptr.package = p->e.name; + ptr.section = section; + ptr.option = option; + if (uci_lookup_ptr(ctx, &ptr, NULL, true) != UCI_OK) + return NULL; + + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) + return NULL; + + e = ptr.last; + switch (e->type) + { + case UCI_TYPE_SECTION: + value = uci_to_section(e)->type; + break; + case UCI_TYPE_OPTION: + switch(ptr.o->type) { + case UCI_TYPE_STRING: + value = ptr.o->v.string; + break; + default: + value = NULL; + break; + } + break; + default: + return 0; + } + + return value; +} + +int uci_get_option_int(struct uci_context *ctx, char *section, char *option, int def) +{ + char *tmp = uci_get_option(ctx, section, option); + int ret = def; + + if (tmp) + ret = atoi(tmp); + return ret; +} + +void uci_for_each_section_type(char *type, void (*cb)(char*, void*), void *priv) +{ + struct uci_element *e; + + uci_foreach_element(&p->sections, e) + if (!strcmp(type, uci_to_section(e)->type)) + cb(e->name, priv); +} diff --git a/ucix.c b/ucix.c new file mode 100644 index 0000000..66f8d77 --- /dev/null +++ b/ucix.c @@ -0,0 +1,209 @@ +#include +#include + +#include "include/ucix.h" + +static struct uci_ptr ptr; + +static inline int ucix_get_ptr(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + memset(&ptr, 0, sizeof(ptr)); + ptr.package = p; + ptr.section = s; + ptr.option = o; + ptr.value = t; + return uci_lookup_ptr(ctx, &ptr, NULL, true); +} + +struct uci_context* ucix_init(const char *config_file) +{ + struct uci_context *ctx = uci_alloc_context(); + uci_add_history_path(ctx, "/var/state"); + if(uci_load(ctx, config_file, NULL) != UCI_OK) + { + printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file); + return NULL; + } + return ctx; +} + +struct uci_context* ucix_init_path(const char *path, const char *config_file) +{ + struct uci_context *ctx = uci_alloc_context(); + if(path) + { + uci_set_savedir(ctx, path); + } + if(uci_load(ctx, config_file, NULL) != UCI_OK) + { + printf("%s/%s is missing or corrupt\n", ctx->savedir, config_file); + return NULL; + } + return ctx; +} + +void ucix_cleanup(struct uci_context *ctx) +{ + uci_free_context(ctx); +} + +int ucix_save(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + uci_set_savedir(ctx, "/tmp/.uci/"); + uci_save(ctx, ptr.p); + return 0; +} + +int ucix_save_state(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + uci_set_savedir(ctx, "/var/state/"); + uci_save(ctx, ptr.p); + return 0; +} + +int ucix_get_option_list(struct uci_context *ctx, const char *p, + const char *s, const char *o, struct list_head *l) +{ + struct uci_element *e = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return 1; + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) + return 1; + e = ptr.last; + switch (e->type) + { + case UCI_TYPE_OPTION: + switch(ptr.o->type) { + case UCI_TYPE_LIST: + uci_foreach_element(&ptr.o->v.list, e) + { + struct ucilist *ul = malloc(sizeof(struct ucilist)); + ul->val = strdup((e->name)?(e->name):("")); + INIT_LIST_HEAD(&ul->list); + list_add(&ul->list, l); + } + break; + default: + break; + } + break; + default: + return 1; + } + + return 0; +} + +char* ucix_get_option(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + struct uci_element *e = NULL; + const char *value = NULL; + if(ucix_get_ptr(ctx, p, s, o, NULL)) + return NULL; + if (!(ptr.flags & UCI_LOOKUP_COMPLETE)) + return NULL; + e = ptr.last; + switch (e->type) + { + case UCI_TYPE_SECTION: + value = uci_to_section(e)->type; + break; + case UCI_TYPE_OPTION: + switch(ptr.o->type) { + case UCI_TYPE_STRING: + value = ptr.o->v.string; + break; + default: + value = NULL; + break; + } + break; + default: + return 0; + } + + return (value) ? (strdup(value)):(NULL); +} + +int ucix_get_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int def) +{ + char *tmp = ucix_get_option(ctx, p, s, o); + int ret = def; + + if (tmp) + { + ret = atoi(tmp); + free(tmp); + } + return ret; +} + +void ucix_add_section(struct uci_context *ctx, const char *p, const char *s, const char *t) +{ + if(ucix_get_ptr(ctx, p, s, NULL, t)) + return; + uci_set(ctx, &ptr); +} + +void ucix_add_option(struct uci_context *ctx, const char *p, const char *s, const char *o, const char *t) +{ + if(ucix_get_ptr(ctx, p, s, o, (t)?(t):(""))) + return; + uci_set(ctx, &ptr); +} + +void ucix_add_option_int(struct uci_context *ctx, const char *p, const char *s, const char *o, int t) +{ + char tmp[64]; + snprintf(tmp, 64, "%d", t); + ucix_add_option(ctx, p, s, o, tmp); +} + +void ucix_del(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if(!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_delete(ctx, &ptr); +} + +void ucix_revert(struct uci_context *ctx, const char *p, const char *s, const char *o) +{ + if(!ucix_get_ptr(ctx, p, s, o, NULL)) + uci_revert(ctx, &ptr); +} + +void ucix_for_each_section_type(struct uci_context *ctx, + const char *p, const char *t, + void (*cb)(const char*, void*), void *priv) +{ + struct uci_element *e; + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return; + uci_foreach_element(&ptr.p->sections, e) + if (!strcmp(t, uci_to_section(e)->type)) + cb(e->name, priv); +} + +void ucix_for_each_section_option(struct uci_context *ctx, + const char *p, const char *s, + void (*cb)(const char*, const char*, void*), void *priv) +{ + struct uci_element *e; + if(ucix_get_ptr(ctx, p, s, NULL, NULL)) + return; + uci_foreach_element(&ptr.s->options, e) + { + struct uci_option *o = uci_to_option(e); + cb(o->e.name, o->v.string, priv); + } +} + +int ucix_commit(struct uci_context *ctx, const char *p) +{ + if(ucix_get_ptr(ctx, p, NULL, NULL, NULL)) + return 1; + return uci_commit(ctx, &ptr.p, false); +} -- 2.25.1