From: John Crispin Date: Mon, 27 Jul 2015 11:30:13 +0000 (+0200) Subject: import v0.1 X-Git-Url: https://git.librecmc.org/?p=oweals%2Fmountd.git;a=commitdiff_plain;h=be3b285c88648c24f7e4b36bebac56a95fa80f7e import v0.1 Signed-off-by: John Crispin --- be3b285c88648c24f7e4b36bebac56a95fa80f7e 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); +}