Index: latest/arch/alpha/kernel/systbls.S =================================================================== --- latest.orig/arch/alpha/kernel/systbls.S +++ latest/arch/alpha/kernel/systbls.S @@ -239,7 +239,15 @@ sys_call_table: .quad alpha_ni_syscall .quad alpha_ni_syscall /* 220 */ .quad alpha_ni_syscall +#ifdef CONFIG_TUX + .quad __sys_tux +#else +# ifdef CONFIG_TUX_MODULE + .quad sys_tux +# else .quad alpha_ni_syscall +# endif +#endif .quad alpha_ni_syscall .quad alpha_ni_syscall .quad alpha_ni_syscall /* 225 */ Index: latest/arch/i386/kernel/syscall_table.S =================================================================== --- latest.orig/arch/i386/kernel/syscall_table.S +++ latest/arch/i386/kernel/syscall_table.S @@ -221,7 +221,15 @@ ENTRY(sys_call_table) .long sys_madvise .long sys_getdents64 /* 220 */ .long sys_fcntl64 - .long sys_ni_syscall /* reserved for TUX */ +#ifdef CONFIG_TUX + .long __sys_tux +#else +# ifdef CONFIG_TUX_MODULE + .long sys_tux +# else + .long sys_ni_syscall +# endif +#endif .long sys_ni_syscall .long sys_gettid .long sys_readahead /* 225 */ Index: latest/arch/ia64/kernel/entry.S =================================================================== --- latest.orig/arch/ia64/kernel/entry.S +++ latest/arch/ia64/kernel/entry.S @@ -1427,7 +1427,15 @@ sys_call_table: data8 sys_syslog data8 sys_setitimer data8 sys_getitimer +#ifdef CONFIG_TUX + data8 __sys_tux // 1120 /* was: ia64_oldstat */ +#else +# ifdef CONFIG_TUX_MODULE + data8 sys_tux // 1120 /* was: ia64_oldstat */ +# else data8 sys_ni_syscall // 1120 /* was: ia64_oldstat */ +# endif +#endif data8 sys_ni_syscall /* was: ia64_oldlstat */ data8 sys_ni_syscall /* was: ia64_oldfstat */ data8 sys_vhangup Index: latest/arch/ia64/kernel/ia64_ksyms.c =================================================================== --- latest.orig/arch/ia64/kernel/ia64_ksyms.c +++ latest/arch/ia64/kernel/ia64_ksyms.c @@ -43,6 +43,11 @@ EXPORT_SYMBOL(__strlen_user); EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strnlen_user); +#define __KERNEL_SYSCALLS__ +#include <asm/unistd.h> +EXPORT_SYMBOL_GPL(sys_execve); +EXPORT_SYMBOL_GPL(clone); + /* from arch/ia64/lib */ extern void __divsi3(void); extern void __udivsi3(void); Index: latest/arch/x86_64/ia32/ia32entry.S =================================================================== --- latest.orig/arch/x86_64/ia32/ia32entry.S +++ latest/arch/x86_64/ia32/ia32entry.S @@ -617,7 +617,15 @@ ia32_sys_call_table: .quad sys_madvise .quad compat_sys_getdents64 /* 220 getdents64 */ .quad compat_sys_fcntl64 - .quad quiet_ni_syscall /* tux */ +#ifdef CONFIG_TUX + .quad __sys_tux +#else +# ifdef CONFIG_TUX_MODULE + .quad sys_tux +# else + .quad quiet_ni_syscall +# endif +#endif .quad quiet_ni_syscall /* security */ .quad sys_gettid .quad sys_readahead /* 225 */ Index: latest/fs/dcache.c =================================================================== --- latest.orig/fs/dcache.c +++ latest/fs/dcache.c @@ -84,6 +84,10 @@ static void d_free(struct dentry *dentry { if (dentry->d_op && dentry->d_op->d_release) dentry->d_op->d_release(dentry); + if (dentry->d_extra_attributes) { + kfree(dentry->d_extra_attributes); + dentry->d_extra_attributes = NULL; + } call_rcu(&dentry->d_u.d_rcu, d_callback); } @@ -750,6 +754,7 @@ struct dentry *d_alloc(struct dentry * p dentry->d_sb = NULL; dentry->d_op = NULL; dentry->d_fsdata = NULL; + dentry->d_extra_attributes = NULL; dentry->d_mounted = 0; #ifdef CONFIG_PROFILING dentry->d_cookie = NULL; @@ -1358,6 +1363,16 @@ already_unhashed: /* Unhash the target: dput() will then get rid of it */ __d_drop(target); + /* flush any possible attributes */ + if (dentry->d_extra_attributes) { + kfree(dentry->d_extra_attributes); + dentry->d_extra_attributes = NULL; + } + if (target->d_extra_attributes) { + kfree(target->d_extra_attributes); + target->d_extra_attributes = NULL; + } + list_del(&dentry->d_u.d_child); list_del(&target->d_u.d_child); @@ -1402,7 +1417,7 @@ already_unhashed: * * "buflen" should be positive. Caller holds the dcache_lock. */ -static char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, +char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, struct dentry *root, struct vfsmount *rootmnt, char *buffer, int buflen) { @@ -1470,6 +1485,8 @@ Elong: return ERR_PTR(-ENAMETOOLONG); } +EXPORT_SYMBOL_GPL(__d_path); + /* write full pathname into buffer and return start of pathname */ char * d_path(struct dentry *dentry, struct vfsmount *vfsmnt, char *buf, int buflen) @@ -1698,6 +1715,23 @@ static void __init dcache_init_early(voi INIT_HLIST_HEAD(&dentry_hashtable[loop]); } +void flush_dentry_attributes (void) +{ + struct hlist_node *tmp; + struct dentry *dentry; + int i; + + spin_lock(&dcache_lock); + for (i = 0; i <= d_hash_mask; i++) + hlist_for_each_entry(dentry, tmp, dentry_hashtable+i, d_hash) { + kfree(dentry->d_extra_attributes); + dentry->d_extra_attributes = NULL; + } + spin_unlock(&dcache_lock); +} + +EXPORT_SYMBOL_GPL(flush_dentry_attributes); + static void __init dcache_init(unsigned long mempages) { int loop; Index: latest/fs/exec.c =================================================================== --- latest.orig/fs/exec.c +++ latest/fs/exec.c @@ -1466,6 +1466,8 @@ int do_coredump(long signr, int exit_cod binfmt = current->binfmt; if (!binfmt || !binfmt->core_dump) goto fail; + if (current->tux_exit) + current->tux_exit(); down_write(&mm->mmap_sem); if (!mm->dumpable) { up_write(&mm->mmap_sem); Index: latest/fs/fcntl.c =================================================================== --- latest.orig/fs/fcntl.c +++ latest/fs/fcntl.c @@ -111,7 +111,7 @@ out: return error; } -static int dupfd(struct file *file, unsigned int start) +int dupfd(struct file *file, unsigned int start) { struct files_struct * files = current->files; struct fdtable *fdt; @@ -134,6 +134,8 @@ static int dupfd(struct file *file, unsi return fd; } +EXPORT_SYMBOL_GPL(dupfd); + asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd) { int err = -EBADF; Index: latest/fs/namei.c =================================================================== --- latest.orig/fs/namei.c +++ latest/fs/namei.c @@ -745,7 +745,7 @@ static __always_inline void follow_dotdo * It _is_ time-critical. */ static int do_lookup(struct nameidata *nd, struct qstr *name, - struct path *path) + struct path *path, int atomic) { struct vfsmount *mnt = nd->mnt; struct dentry *dentry = __d_lookup(nd->dentry, name); @@ -761,12 +761,16 @@ done: return 0; need_lookup: + if (atomic) + return -EWOULDBLOCKIO; dentry = real_lookup(nd->dentry, name, nd); if (IS_ERR(dentry)) goto fail; goto done; need_revalidate: + if (atomic) + return -EWOULDBLOCKIO; if (dentry->d_op->d_revalidate(dentry, nd)) goto done; if (d_invalidate(dentry)) @@ -790,9 +794,11 @@ static fastcall int __link_path_walk(con { struct path next; struct inode *inode; - int err; + int err, atomic; unsigned int lookup_flags = nd->flags; - + + atomic = (lookup_flags & LOOKUP_ATOMIC); + while (*name=='/') name++; if (!*name) @@ -861,7 +867,7 @@ static fastcall int __link_path_walk(con break; } /* This does the actual lookups.. */ - err = do_lookup(nd, &this, &next); + err = do_lookup(nd, &this, &next, atomic); if (err) break; @@ -916,7 +922,7 @@ last_component: if (err < 0) break; } - err = do_lookup(nd, &this, &next); + err = do_lookup(nd, &this, &next, atomic); if (err) break; inode = next.dentry->d_inode; @@ -1418,6 +1424,8 @@ static inline int lookup_flags(unsigned if (f & O_DIRECTORY) retval |= LOOKUP_DIRECTORY; + if (f & O_ATOMICLOOKUP) + retval |= LOOKUP_ATOMIC; return retval; } Index: latest/fs/namespace.c =================================================================== --- latest.orig/fs/namespace.c +++ latest/fs/namespace.c @@ -1609,6 +1609,8 @@ void set_fs_root(struct fs_struct *fs, s } } +EXPORT_SYMBOL_GPL(set_fs_root); + /* * Replace the fs->{pwdmnt,pwd} with {mnt,dentry}. Put the old values. * It can block. Requires the big lock held. Index: latest/fs/open.c =================================================================== --- latest.orig/fs/open.c +++ latest/fs/open.c @@ -562,6 +562,8 @@ out: return error; } +EXPORT_SYMBOL_GPL(sys_chdir); + asmlinkage long sys_fchdir(unsigned int fd) { struct file *file; @@ -618,6 +620,8 @@ out: return error; } +EXPORT_SYMBOL_GPL(sys_chroot); + asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) { struct inode * inode; Index: latest/fs/pipe.c =================================================================== --- latest.orig/fs/pipe.c +++ latest/fs/pipe.c @@ -973,6 +973,8 @@ no_files: return error; } +EXPORT_SYMBOL_GPL(do_pipe); + /* * pipefs should _never_ be mounted by userland - too much of security hassle, * no real gain from having the whole whorehouse mounted. So we don't need Index: latest/fs/read_write.c =================================================================== --- latest.orig/fs/read_write.c +++ latest/fs/read_write.c @@ -374,6 +374,8 @@ asmlinkage ssize_t sys_write(unsigned in return ret; } +EXPORT_SYMBOL_GPL(sys_write); + asmlinkage ssize_t sys_pread64(unsigned int fd, char __user *buf, size_t count, loff_t pos) { Index: latest/include/asm-alpha/fcntl.h =================================================================== --- latest.orig/include/asm-alpha/fcntl.h +++ latest/include/asm-alpha/fcntl.h @@ -14,6 +14,7 @@ #define O_DIRECTORY 0100000 /* must be a directory */ #define O_NOFOLLOW 0200000 /* don't follow links */ #define O_LARGEFILE 0400000 /* will be set by the kernel on every open */ +#define O_ATOMICLOOKUP 01000000 /* do atomic file lookup */ #define O_DIRECT 02000000 /* direct disk access - should check with OSF/1 */ #define O_NOATIME 04000000 Index: latest/include/asm-generic/fcntl.h =================================================================== --- latest.orig/include/asm-generic/fcntl.h +++ latest/include/asm-generic/fcntl.h @@ -48,6 +48,10 @@ #ifndef O_NOATIME #define O_NOATIME 01000000 #endif +#ifndef O_ATOMICLOOKUP +#define O_ATOMICLOOKUP 02000000 /* do atomic file lookup */ +#endif + #ifndef O_NDELAY #define O_NDELAY O_NONBLOCK #endif Index: latest/include/asm-i386/unistd.h =================================================================== --- latest.orig/include/asm-i386/unistd.h +++ latest/include/asm-i386/unistd.h @@ -328,6 +328,7 @@ #define NR_syscalls 318 +#ifndef __KERNEL_SYSCALLS_NO_ERRNO__ /* * user-visible error numbers are in the range -1 - -128: see * <asm-i386/errno.h> @@ -341,6 +342,10 @@ do { \ return (type) (res); \ } while (0) +#else +# define __syscall_return(type, res) return (type) (res) +#endif + /* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */ #define _syscall0(type,name) \ type name(void) \ Index: latest/include/asm-ia64/unistd.h =================================================================== --- latest.orig/include/asm-ia64/unistd.h +++ latest/include/asm-ia64/unistd.h @@ -109,7 +109,7 @@ #define __NR_syslog 1117 #define __NR_setitimer 1118 #define __NR_getitimer 1119 -/* 1120 was __NR_old_stat */ +#define __NR_tux 1120 /* was __NR_old_stat */ /* 1121 was __NR_old_lstat */ /* 1122 was __NR_old_fstat */ #define __NR_vhangup 1123 Index: latest/include/asm-sparc/fcntl.h =================================================================== --- latest.orig/include/asm-sparc/fcntl.h +++ latest/include/asm-sparc/fcntl.h @@ -14,6 +14,7 @@ #define O_NDELAY (0x0004 | O_NONBLOCK) #define O_NOCTTY 0x8000 /* not fcntl */ #define O_LARGEFILE 0x40000 +#define O_ATOMICLOOKUP 0x80000 /* do atomic file lookup */ #define O_DIRECT 0x100000 /* direct disk access hint */ #define O_NOATIME 0x200000 Index: latest/include/asm-sparc64/fcntl.h =================================================================== --- latest.orig/include/asm-sparc64/fcntl.h +++ latest/include/asm-sparc64/fcntl.h @@ -14,6 +14,7 @@ #define O_NONBLOCK 0x4000 #define O_NOCTTY 0x8000 /* not fcntl */ #define O_LARGEFILE 0x40000 +#define O_ATOMICLOOKUP 0x80000 /* do atomic file lookup */ #define O_DIRECT 0x100000 /* direct disk access hint */ #define O_NOATIME 0x200000 Index: latest/include/asm-x86_64/unistd.h =================================================================== --- latest.orig/include/asm-x86_64/unistd.h +++ latest/include/asm-x86_64/unistd.h @@ -425,7 +425,15 @@ __SYSCALL(__NR_putpmsg, sys_ni_syscall) __SYSCALL(__NR_afs_syscall, sys_ni_syscall) #define __NR_tuxcall 184 /* reserved for tux */ -__SYSCALL(__NR_tuxcall, sys_ni_syscall) +#ifdef CONFIG_TUX + __SYSCALL(__NR_tuxcall, __sys_tux) +#else +# ifdef CONFIG_TUX_MODULE + __SYSCALL(__NR_tuxcall, sys_tux) +# else + __SYSCALL(__NR_tuxcall, sys_ni_syscall) +# endif +#endif #define __NR_security 185 __SYSCALL(__NR_security, sys_ni_syscall) Index: latest/include/linux/buffer_head.h =================================================================== --- latest.orig/include/linux/buffer_head.h +++ latest/include/linux/buffer_head.h @@ -203,6 +203,7 @@ int generic_cont_expand(struct inode *in int generic_cont_expand_simple(struct inode *inode, loff_t size); int block_commit_write(struct page *page, unsigned from, unsigned to); void block_sync_page(struct page *); +void flush_inode_pages (struct inode * inode); sector_t generic_block_bmap(struct address_space *, sector_t, get_block_t *); int generic_commit_write(struct file *, struct page *, unsigned, unsigned); int block_truncate_page(struct address_space *, loff_t, get_block_t *); Index: latest/include/linux/dcache.h =================================================================== --- latest.orig/include/linux/dcache.h +++ latest/include/linux/dcache.h @@ -107,6 +107,7 @@ struct dentry { struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ void *d_fsdata; /* fs-specific data */ + void *d_extra_attributes; /* TUX-specific data */ #ifdef CONFIG_PROFILING struct dcookie_struct *d_cookie; /* cookie, if any */ #endif @@ -230,6 +231,7 @@ extern struct dentry * d_splice_alias(st extern void shrink_dcache_sb(struct super_block *); extern void shrink_dcache_parent(struct dentry *); extern int d_invalidate(struct dentry *); +extern void flush_dentry_attributes(void); /* only used at mount-time */ extern struct dentry * d_alloc_root(struct inode *); @@ -291,8 +293,12 @@ extern struct dentry * d_hash_and_lookup /* validate "insecure" dentry pointer */ extern int d_validate(struct dentry *, struct dentry *); +char * __d_path( struct dentry *dentry, struct vfsmount *vfsmnt, + struct dentry *root, struct vfsmount *rootmnt, + char *buffer, int buflen); + extern char * d_path(struct dentry *, struct vfsmount *, char *, int); - + /* Allocation counts.. */ /** Index: latest/include/linux/errno.h =================================================================== --- latest.orig/include/linux/errno.h +++ latest/include/linux/errno.h @@ -24,6 +24,9 @@ #define EIOCBQUEUED 529 /* iocb queued, will get completion event */ #define EIOCBRETRY 530 /* iocb queued, will trigger a retry */ +/* Defined for TUX async IO */ +#define EWOULDBLOCKIO 530 /* Would block due to block-IO */ + #endif #endif Index: latest/include/linux/file.h =================================================================== --- latest.orig/include/linux/file.h +++ latest/include/linux/file.h @@ -113,4 +113,6 @@ struct task_struct; struct files_struct *get_files_struct(struct task_struct *); void FASTCALL(put_files_struct(struct files_struct *fs)); +extern int dupfd(struct file *file, unsigned int start); + #endif /* __LINUX_FILE_H */ Index: latest/include/linux/fs.h =================================================================== --- latest.orig/include/linux/fs.h +++ latest/include/linux/fs.h @@ -1659,7 +1659,7 @@ ssize_t generic_file_write_nolock(struct extern ssize_t generic_file_sendfile(struct file *, loff_t *, size_t, read_actor_t, void *); extern void do_generic_mapping_read(struct address_space *mapping, struct file_ra_state *, struct file *, - loff_t *, read_descriptor_t *, read_actor_t); + loff_t *, read_descriptor_t *, read_actor_t, int); /* fs/splice.c */ extern ssize_t generic_file_splice_read(struct file *, loff_t *, @@ -1702,14 +1702,15 @@ static inline int xip_truncate_page(stru static inline void do_generic_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc, - read_actor_t actor) + read_actor_t actor, int nonblock) { do_generic_mapping_read(filp->f_mapping, &filp->f_ra, filp, ppos, desc, - actor); + actor, + nonblock); } ssize_t __blockdev_direct_IO(int rw, struct kiocb *iocb, struct inode *inode, Index: latest/include/linux/kmod.h =================================================================== --- latest.orig/include/linux/kmod.h +++ latest/include/linux/kmod.h @@ -46,5 +46,7 @@ call_usermodehelper(char *path, char **a } extern void usermodehelper_init(void); +extern int __exec_usermodehelper(char *path, char **argv, char **envp, + struct key *ring); #endif /* __LINUX_KMOD_H__ */ Index: latest/include/linux/namei.h =================================================================== --- latest.orig/include/linux/namei.h +++ latest/include/linux/namei.h @@ -48,6 +48,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LA #define LOOKUP_PARENT 16 #define LOOKUP_NOALT 32 #define LOOKUP_REVAL 64 +#define LOOKUP_ATOMIC 128 + /* * Intent data */ Index: latest/include/linux/net.h =================================================================== --- latest.orig/include/linux/net.h +++ latest/include/linux/net.h @@ -189,6 +189,7 @@ extern int sock_create_kern(int fam struct socket **res); extern int sock_create_lite(int family, int type, int proto, struct socket **res); +extern struct socket *sock_alloc(void); extern void sock_release(struct socket *sock); extern int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t len); Index: latest/include/linux/sched.h =================================================================== --- latest.orig/include/linux/sched.h +++ latest/include/linux/sched.h @@ -895,6 +895,11 @@ struct task_struct { int (*notifier)(void *priv); void *notifier_data; sigset_t *notifier_mask; + + /* TUX state */ + void *tux_info; + void (*tux_exit)(void); + void *security; struct audit_context *audit_context; Index: latest/include/linux/skbuff.h =================================================================== --- latest.orig/include/linux/skbuff.h +++ latest/include/linux/skbuff.h @@ -1422,6 +1422,8 @@ static inline unsigned int skb_checksum_ __skb_checksum_complete(skb); } +struct tux_req_struct; + #ifdef CONFIG_NETFILTER static inline void nf_conntrack_put(struct nf_conntrack *nfct) { Index: latest/include/linux/socket.h =================================================================== --- latest.orig/include/linux/socket.h +++ latest/include/linux/socket.h @@ -300,6 +300,10 @@ extern int move_addr_to_user(void *kaddr extern int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr); extern int put_cmsg(struct msghdr*, int level, int type, int len, void *data); +struct socket; +extern int sock_map_fd(struct socket *sock); +extern struct socket *sockfd_lookup(int fd, int *err); + #endif #endif /* not kernel and not glibc */ #endif /* _LINUX_SOCKET_H */ Index: latest/include/linux/sysctl.h =================================================================== --- latest.orig/include/linux/sysctl.h +++ latest/include/linux/sysctl.h @@ -220,6 +220,7 @@ enum NET_LLC=18, NET_NETFILTER=19, NET_DCCP=20, + NET_TUX=21, }; /* /proc/sys/kernel/random */ @@ -774,6 +775,55 @@ enum { NET_BRIDGE_NF_FILTER_VLAN_TAGGED = 4, }; +/* /proc/sys/net/tux/ */ +enum { + NET_TUX_DOCROOT = 1, + NET_TUX_LOGFILE = 2, + NET_TUX_EXTCGI = 3, + NET_TUX_STOP = 4, + NET_TUX_CLIENTPORT = 5, + NET_TUX_LOGGING = 6, + NET_TUX_SERVERPORT = 7, + NET_TUX_THREADS = 8, + NET_TUX_KEEPALIVE_TIMEOUT = 9, + NET_TUX_MAX_KEEPALIVE_BW = 10, + NET_TUX_DEFER_ACCEPT = 11, + NET_TUX_MAX_FREE_REQUESTS = 12, + NET_TUX_MAX_CONNECT = 13, + NET_TUX_MAX_BACKLOG = 14, + NET_TUX_MODE_FORBIDDEN = 15, + NET_TUX_MODE_ALLOWED = 16, + NET_TUX_MODE_USERSPACE = 17, + NET_TUX_MODE_CGI = 18, + NET_TUX_CGI_UID = 19, + NET_TUX_CGI_GID = 20, + NET_TUX_CGIROOT = 21, + NET_TUX_LOGENTRY_ALIGN_ORDER = 22, + NET_TUX_NONAGLE = 23, + NET_TUX_ACK_PINGPONG = 24, + NET_TUX_PUSH_ALL = 25, + NET_TUX_ZEROCOPY_PARSE = 26, + NET_CONFIG_TUX_DEBUG_BLOCKING = 27, + NET_TUX_PAGE_AGE_START = 28, + NET_TUX_PAGE_AGE_ADV = 29, + NET_TUX_PAGE_AGE_MAX = 30, + NET_TUX_VIRTUAL_SERVER = 31, + NET_TUX_MAX_OBJECT_SIZE = 32, + NET_TUX_COMPRESSION = 33, + NET_TUX_NOID = 34, + NET_TUX_CGI_INHERIT_CPU = 35, + NET_TUX_CGI_CPU_MASK = 36, + NET_TUX_ZEROCOPY_HEADER = 37, + NET_TUX_ZEROCOPY_SENDFILE = 38, + NET_TUX_ALL_USERSPACE = 39, + NET_TUX_REDIRECT_LOGGING = 40, + NET_TUX_REFERER_LOGGING = 41, + NET_TUX_MAX_HEADER_LEN = 42, + NET_TUX_404_PAGE = 43, + NET_TUX_MAX_KEEPALIVES = 44, + NET_TUX_IGNORE_QUERY = 45, +}; + /* CTL_FS names: */ enum { Index: latest/include/net/sock.h =================================================================== --- latest.orig/include/net/sock.h +++ latest/include/net/sock.h @@ -62,7 +62,7 @@ */ /* Define this to get the SOCK_DBG debugging facility. */ -#define SOCK_DEBUGGING +//#define SOCK_DEBUGGING #ifdef SOCK_DEBUGGING #define SOCK_DEBUG(sk, msg...) do { if ((sk) && sock_flag((sk), SOCK_DBG)) \ printk(KERN_DEBUG msg); } while (0) @@ -166,7 +166,7 @@ struct sock_common { * @sk_timer: sock cleanup timer * @sk_stamp: time stamp of last packet received * @sk_socket: Identd and reporting IO signals - * @sk_user_data: RPC layer private data + * @sk_user_data: RPC and Tux layer private data * @sk_sndmsg_page: cached page for sendmsg * @sk_sndmsg_off: cached offset for sendmsg * @sk_send_head: front of stuff to transmit @@ -176,6 +176,7 @@ struct sock_common { * @sk_data_ready: callback to indicate there is data to be processed * @sk_write_space: callback to indicate there is bf sending space available * @sk_error_report: callback to indicate errors (e.g. %MSG_ERRQUEUE) + * @sk_create_child - callback to get new socket events * @sk_backlog_rcv: callback to process the backlog * @sk_destruct: called at sock freeing time, i.e. when all refcnt == 0 */ @@ -257,6 +258,7 @@ struct sock { void (*sk_error_report)(struct sock *sk); int (*sk_backlog_rcv)(struct sock *sk, struct sk_buff *skb); + void (*sk_create_child)(struct sock *sk, struct sock *newsk); void (*sk_destruct)(struct sock *sk); }; @@ -760,7 +762,7 @@ extern struct sock *sk_alloc(int family gfp_t priority, struct proto *prot, int zero_it); extern void sk_free(struct sock *sk); -extern struct sock *sk_clone(const struct sock *sk, +extern struct sock *sk_clone(struct sock *sk, const gfp_t priority); extern struct sk_buff *sock_wmalloc(struct sock *sk, Index: latest/include/net/tcp.h =================================================================== --- latest.orig/include/net/tcp.h +++ latest/include/net/tcp.h @@ -271,6 +271,8 @@ extern void tcp_shutdown (struct sock extern int tcp_v4_rcv(struct sk_buff *skb); +extern struct sock * tcp_v4_lookup_listener(u32 daddr, unsigned short hnum, int dif); + extern int tcp_v4_remember_stamp(struct sock *sk); extern int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); @@ -435,6 +437,7 @@ extern int tcp_send_synack(struct sock extern void tcp_push_one(struct sock *, unsigned int mss_now); extern void tcp_send_ack(struct sock *sk); extern void tcp_send_delayed_ack(struct sock *sk); +extern void tcp_cleanup_rbuf(struct sock *sk, int copied); /* tcp_input.c */ extern void tcp_cwnd_application_limited(struct sock *sk); Index: latest/include/net/tux.h =================================================================== --- /dev/null +++ latest/include/net/tux.h @@ -0,0 +1,803 @@ +#ifndef _NET_TUX_H +#define _NET_TUX_H + +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * tux.h: main structure definitions and function prototypes + */ + +#define __KERNEL_SYSCALLS__ + +#include <linux/mm.h> +#include <linux/net.h> +#include <linux/wait.h> +#include <linux/namei.h> +#include <linux/file.h> +#include <linux/mman.h> +#include <linux/swap.h> +#include <linux/ctype.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/unistd.h> +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/pagemap.h> +#include <linux/vmalloc.h> +#include <linux/utsname.h> +#include <linux/smp_lock.h> +#include <linux/kernel_stat.h> +#include <linux/kernel_stat.h> +#include <linux/time.h> +#include <asm/div64.h> +#include <asm/unaligned.h> +#include <linux/compiler.h> +#include <linux/mount.h> +#include <linux/zlib.h> +#include <linux/syscalls.h> +#include <linux/cpumask.h> + +#include <net/tcp.h> +#include <net/tux_u.h> + +/* Maximum number of threads: */ +#define CONFIG_TUX_NUMTHREADS 16 + +/* Number of cachemiss/IO threads: */ +#define NR_IO_THREADS 64 + +/* Maximum number of listen sockets per thread: */ +#define CONFIG_TUX_NUMSOCKETS 16 + +extern spinlock_t tux_module_lock; +extern struct module *tux_module; +extern asmlinkage long (*sys_tux_ptr) (unsigned int action, user_req_t *u_info); + +#undef Dprintk + +extern int tux_TDprintk; +extern int tux_Dprintk; + +#ifdef CONFIG_TUX_DEBUG +# define TUX_BUG() BUG() + +# define TUX_DPRINTK 1 +# define TDprintk(x...) do { if (tux_TDprintk) { printk("<%ld:%s:%d>: ", jiffies, __FILE__, __LINE__); printk(x); } } while (0) +# define Dprintk(x...) do { if (tux_Dprintk == 1) TDprintk(x); } while (0) +#else +# define TUX_DPRINTK 0 +# define Dprintk(x...) do { } while (0) +# define TDprintk(x...) do { } while (0) +//# define TUX_BUG() BUG() +# define TUX_BUG() do { } while (0) +#endif + +#if 1 +# define INC_STAT(x) do { } while (0) +# define DEC_STAT(x) do { } while (0) +# define ADD_STAT(x,y) do { } while (0) +# define SUB_STAT(x,y) do { } while (0) +#else +# define INC_STAT(x) atomic_inc((atomic_t *)&kstat.x) +# define DEC_STAT(x) atomic_dec((atomic_t *)&kstat.x) +# define ADD_STAT(y,x) atomic_add(y,(atomic_t *)&kstat.x) +# define SUB_STAT(y,x) atomic_sub(y,(atomic_t *)&kstat.x) +#endif + +// lru needs this: + +# define DEBUG_DEL_LIST(x...) do { INIT_LIST_HEAD((x)); } while (0) + + +#define LOG_LEN (8*1024*1024UL) + +struct tux_req_struct; +typedef struct tux_req_struct tux_req_t; +typedef struct tux_threadinfo threadinfo_t; + +extern struct address_space_operations url_aops; + +typedef struct tcapi_template_s { + char *vfs_name; + struct list_head modules; + int (*query) (tux_req_t *req); + struct module *mod; + unsigned int userspace_id; +} tcapi_template_t; + +typedef struct mimetype_s { + struct list_head list; + + char *ext; + unsigned int ext_len; + char *type; + unsigned int type_len; + char *expire_str; + unsigned int expire_str_len; + + unsigned int special; +} mimetype_t; + +typedef struct tux_attribute_s { + mimetype_t *mime; + tcapi_template_t *tcapi; +} tux_attribute_t; + +#define MAX_TUX_ATOMS 8 + +typedef void (atom_func_t)(tux_req_t *req, int cachemiss); + +typedef struct tux_proto_s +{ + unsigned int defer_accept; + unsigned int can_redirect; + void (*got_request) (tux_req_t *req); + int (*parse_message) (tux_req_t *req, const int total_len); + atom_func_t *illegal_request; + atom_func_t *request_timeout; + void (*pre_log) (tux_req_t *req); + int (*check_req_err) (tux_req_t *req, int cachemiss); + char * (*print_dir_line) (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode); + const char *name; + struct nameidata main_docroot; +} tux_proto_t; + +typedef struct tux_socket_s { + tux_proto_t *proto; + unsigned int ip; + unsigned short port; + struct proc_dir_entry *entry; +} tux_socket_t; + +extern tux_socket_t tux_listen [CONFIG_TUX_NUMTHREADS][CONFIG_TUX_NUMSOCKETS]; + + +typedef struct abuf_s { + struct page *page; + char *buf; + unsigned int size; + unsigned int max_len; + unsigned int offset; + unsigned int left; + unsigned long flags; +} abuf_t; + +struct linux_dirent64 { + u64 d_ino; + s64 d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[0]; +}; + +struct getdents_callback64 { + struct linux_dirent64 * current_dir; + struct linux_dirent64 * previous; + int count; + int error; +}; + +#define TUX_MAGIC 0x12457801 + +#define MAX_TUX_ATOMS 8 + +struct tux_req_struct +{ + tux_proto_t *proto; + + int atom_idx; + atom_func_t *atoms [MAX_TUX_ATOMS]; + struct list_head work; + + struct list_head all; + struct list_head free; + struct list_head lru; + + unsigned long idle_input; + unsigned long wait_output_space; + + struct socket *sock; + struct dentry *dentry; + struct vfsmount *mnt; + struct dentry *docroot_dentry; + struct vfsmount *docroot_mnt; + struct dentry *cwd_dentry; + struct vfsmount *cwd_mnt; + + struct file *in_file; + int fd; + read_descriptor_t desc; + u32 client_addr; + u32 client_port; + unsigned int virtual; + + loff_t total_file_len; + unsigned int lendigits; + loff_t offset_start; + loff_t offset_end; + loff_t output_len; + + loff_t ftp_offset_start; + + time_t mtime; + unsigned int etaglen; + char etag [40]; + + char usermode; + unsigned int usermodule_idx; + struct dentry *module_dentry; + struct vfsmount *module_mnt; + char *userbuf; + unsigned int userlen; + + tux_attribute_t *attr; + + threadinfo_t *ti; + wait_queue_t sleep; + wait_queue_t ftp_sleep; + + abuf_t abuf; + /* + * Parsed request fields. In-line strings are zero-delimited. + */ + const char *headers; + unsigned int headers_len; + + unsigned int parsed_len; + + // FTP part + ftp_command_t ftp_command; + u32 ftp_user_addr; + u16 ftp_user_port; + + struct socket *data_sock; + unsigned int prev_pos; + + // ls handing: + struct linux_dirent64 *dirp0; + unsigned int curroff, total; + +#define MAX_USERNAME_LEN 16 + char username[MAX_USERNAME_LEN]; + unsigned int username_len; + + // HTTP part + http_method_t method; + const char *method_str; + unsigned int method_len; + + http_version_t version; + const char *version_str; + unsigned int version_len; + + /* requested URI: */ + + const char *uri_str; + unsigned int uri_len; + + /* Objectname (filename/scriptname) this URI refers to: */ + +#define MAX_OBJECTNAME_LEN 256 + char objectname[MAX_OBJECTNAME_LEN + 4]; // space for .gz as well + unsigned int objectname_len; + + /* Query string within the URI: */ + + const char *query_str; + unsigned int query_len; + + /* Cookies: */ + + const char *cookies_str; + unsigned int cookies_len; + unsigned int parse_cookies; + + /* Content-TYpe */ + const char *content_type_str; + unsigned int content_type_len; + + /* Content-Length: */ + + const char *contentlen_str; + unsigned int contentlen_len; + unsigned int content_len; + + /* User-Agent: */ + + const char *user_agent_str; + unsigned int user_agent_len; + + /* Accept: */ + + const char *accept_str; + unsigned int accept_len; + + /* Accept-Charset: */ + + const char *accept_charset_str; + unsigned int accept_charset_len; + + /* Accept-Language: */ + + const char *accept_language_str; + unsigned int accept_language_len; + + /* Cache-Control: */ + + const char *cache_control_str; + unsigned int cache_control_len; + + /* If-Modified-Since: */ + + const char *if_modified_since_str; + unsigned int if_modified_since_len; + + /* If-None-Match: */ + const char *if_none_match_str; + unsigned int if_none_match_len; + + /* If-Range: */ + + const char *if_range_str; + unsigned int if_range_len; + + /* Negotiate: */ + + const char *negotiate_str; + unsigned int negotiate_len; + + /* Pragma: */ + + const char *pragma_str; + unsigned int pragma_len; + + /* Referer: */ + + const char *referer_str; + unsigned int referer_len; + + /* Accept-Encoding: */ + + const char *accept_encoding_str; + unsigned int accept_encoding_len; + unsigned int may_send_gzip; + unsigned int content_gzipped; + + /* Host */ + +#define MAX_HOST_LEN 128 + char host[MAX_HOST_LEN]; + unsigned int host_len; + + /* POSTed data: */ + + const char *post_data_str; + unsigned int post_data_len; + + unsigned int status; + + /* the file being sent */ + + unsigned int bytes_sent; +#ifdef CONFIG_TUX_DEBUG + unsigned int bytes_expected; +#endif + unsigned long first_timestamp; + unsigned int body_len; + + unsigned int user_error; + + char error; + char postponed; + + char had_cachemiss; + char lookup_dir; + char lookup_404; + + char keep_alive; + struct timer_list keepalive_timer; + unsigned int total_bytes; + struct timer_list output_timer; + + unsigned int nr_keepalives; + + unsigned int event; + u64 private; + + unsigned int magic; + void (*real_data_ready)(struct sock *sk, int space); + void (*real_state_change)(struct sock *sk); + void (*real_write_space)(struct sock *sk); + void (*real_error_report)(struct sock *sk); + void (*real_destruct)(struct sock *sk); + + void (*ftp_real_data_ready)(struct sock *sk, int space); + void (*ftp_real_state_change)(struct sock *sk); + void (*ftp_real_write_space)(struct sock *sk); + void (*ftp_real_error_report)(struct sock *sk); + void (*ftp_real_create_child)(struct sock *sk, struct sock *newsk); + void (*ftp_real_destruct)(struct sock *sk); + +#ifdef CONFIG_TUX_EXTENDED_LOG + unsigned long accept_timestamp; + unsigned long parse_timestamp; + unsigned long output_timestamp; + unsigned long flush_timestamp; +# define SET_TIMESTAMP(x) do { (x) = jiffies; } while (0) +#else +# define SET_TIMESTAMP(x) do { } while (0) +#endif + +}; + +extern void add_tux_atom (tux_req_t *req, atom_func_t *event_done); +extern void del_tux_atom (tux_req_t *req); +extern void tux_schedule_atom (tux_req_t *req, int cachemiss); +extern void add_req_to_workqueue (tux_req_t *req); + + +typedef struct iothread_s +{ + spinlock_t async_lock; + threadinfo_t *ti; + struct list_head async_queue; + wait_queue_head_t async_sleep; + unsigned int nr_async_pending; + unsigned int threads; + unsigned int shutdown; + wait_queue_head_t wait_shutdown; +} iothread_t; + +typedef struct tux_listen_s +{ + tux_proto_t *proto; + struct socket *sock; + unsigned int cloned; +} tux_listen_t; + +struct tux_threadinfo +{ + tux_req_t *userspace_req; + unsigned int started; + struct task_struct *thread; + iothread_t *iot; + wait_queue_t wait_event [CONFIG_TUX_NUMSOCKETS]; + wait_queue_t stop; + unsigned int pid; + + struct page *header_cache; + unsigned int header_offset; + + unsigned int nr_requests; + struct list_head all_requests; + + unsigned int nr_free_requests; + spinlock_t free_requests_lock; + struct list_head free_requests; + + spinlock_t work_lock; + struct list_head work_pending; + struct list_head lru; + unsigned int nr_lru; + + unsigned int listen_error; + tux_listen_t listen[CONFIG_TUX_NUMSOCKETS]; + + struct semaphore gzip_sem; + z_stream gzip_state; + + unsigned int cpu; + unsigned int __padding[16]; +}; + +typedef enum special_mimetypes { + NORMAL_MIME_TYPE, + MIME_TYPE_REDIRECT, + MIME_TYPE_CGI, + MIME_TYPE_MODULE, +} special_mimetypes_t; + +#ifdef CONFIG_TUX_DEBUG +#if 0 +extern inline void url_hist_hit (int size) +{ + unsigned int idx = size/1024; + + if (idx >= URL_HIST_SIZE) + idx = URL_HIST_SIZE-1; + kstat.url_hist_hits[idx]++; +} +extern inline void url_hist_miss (int size) +{ + unsigned int idx = size/1024; + + if (idx >= URL_HIST_SIZE) + idx = URL_HIST_SIZE-1; + kstat.url_hist_misses[idx]++; +} +#endif +extern void __check_req_list (tux_req_t *req, struct list_head *list); +# define check_req_list __check_req_list +#else +# define check_req_list(req, list) do { } while (0) +#endif + +#define url_hist_hit(size) do { } while (0) +#define url_hist_miss(size) do { } while (0) + +extern char tux_common_docroot[200]; +extern char tux_http_subdocroot[200]; +extern char tux_ftp_subdocroot[200]; +extern char tux_logfile[200]; +extern char tux_cgiroot[200]; +extern char tux_404_page[200]; +extern char tux_default_vhost[200]; +extern char tux_extra_html_header[600]; +extern unsigned int tux_extra_html_header_size; +extern int tux_cgi_uid; +extern int tux_cgi_gid; +extern unsigned int tux_clientport; +extern unsigned int tux_logging; +extern unsigned int tux_threads; +extern unsigned int tux_keepalive_timeout; +extern unsigned int tux_max_output_bandwidth; +extern unsigned int tux_max_backlog; +extern unsigned int tux_max_connect; +extern unsigned int tux_mode_forbidden; +extern unsigned int tux_mode_allowed; +extern unsigned int tux_logentry_align_order; +extern unsigned int tux_nonagle; +extern unsigned int tux_ack_pingpong; +extern unsigned int tux_push_all; +extern unsigned int tux_zerocopy_parse; +extern unsigned int tux_generate_etags; +extern unsigned int tux_generate_last_mod; +extern unsigned int tux_generate_cache_control; +extern unsigned int tux_ip_logging; +extern unsigned int tux_ftp_wait_close; +extern unsigned int tux_ftp_log_retr_only; +extern unsigned int tux_hide_unreadable; + +typedef enum virtual_server { + TUX_VHOST_NONE, + TUX_VHOST_HOST, + TUX_VHOST_IP, + TUX_VHOST_IP_HOST, +} virtual_server_t; + +extern unsigned int tux_virtual_server; +extern unsigned int mass_hosting_hash; +extern unsigned int strip_host_tail; +extern unsigned int tux_ftp_virtual_server; + +extern unsigned int tux_max_object_size; +extern unsigned int tux_max_free_requests; +extern unsigned int tux_defer_accept; + +extern struct socket * start_listening(tux_socket_t *listen, int nr); +extern void stop_listening(struct socket **sock); +extern void start_sysctl(void); +extern void end_sysctl(void); +extern void flush_request (tux_req_t *req, int cachemiss); +extern void unlink_tux_socket (tux_req_t *req); +extern void unlink_tux_data_socket (tux_req_t *req); +extern void unlink_tux_listen_socket (tux_req_t *req); +extern void link_tux_ftp_accept_socket (tux_req_t *req, struct socket *sock); +extern void link_tux_data_socket (tux_req_t *req, struct socket *sock); +extern void tux_push_req (tux_req_t *req); +extern int send_sync_buf (tux_req_t *req, struct socket *sock, const char *buf, const size_t length, unsigned long flags); +extern void __send_async_message (tux_req_t *req, const char *message, int status, unsigned int size, int push); +#define send_async_message(req,str,status,push) \ + __send_async_message(req,str,status,strlen(str),push) + +extern void send_success (tux_req_t *req, struct socket *sock); +extern void send_async_err_not_found (tux_req_t *req); +extern void send_async_timed_out (tux_req_t *req); + +extern void kfree_req (tux_req_t *req); +extern int accept_requests (threadinfo_t *ti); +extern int process_requests (threadinfo_t *ti, tux_req_t **user_req); +extern int flush_freequeue (threadinfo_t * ti); +extern int tux_flush_workqueue (threadinfo_t *ti); +extern tux_req_t * pick_userspace_req (threadinfo_t *ti); +extern atom_func_t redirect_request; +extern atom_func_t parse_request; +extern void queue_cachemiss (tux_req_t *req); +extern int start_cachemiss_threads (threadinfo_t *ti); +extern void stop_cachemiss_threads (threadinfo_t *ti); +struct file * tux_open_file(char *filename, int mode); +extern void start_log_thread (void); +extern void stop_log_thread (void); +extern void add_mimetype (char *new_ext, char *new_type, char *new_expire); +extern void free_mimetypes (void); +extern int lookup_object (tux_req_t *req, const unsigned int flag); +extern int handle_gzip_req (tux_req_t *req, unsigned int flags); +extern struct dentry * tux_lookup (tux_req_t *req, const char *filename, const unsigned int flag, struct vfsmount **mnt); +extern tcapi_template_t * lookup_tuxmodule (const char *filename); +extern int register_tuxmodule (tcapi_template_t *tcapi); +extern tcapi_template_t * unregister_tuxmodule (char *vfs_name); +extern tcapi_template_t * get_first_usermodule (void); +extern int user_register_module (user_req_t *u_info); +extern int user_unregister_module (user_req_t *u_info); +extern void unregister_all_tuxmodules (void); + +typedef struct exec_param_s { + char *command; + char **argv; + char **envp; + unsigned int pipe_fds; +} exec_param_t; + +extern pid_t tux_exec_process (char *command, char **argv, char **envp, int pipe_fds, exec_param_t *param, int wait); + +extern void start_external_cgi (tux_req_t *req); +extern tcapi_template_t extcgi_tcapi; + +extern void queue_output_req (tux_req_t *req, threadinfo_t *ti); +extern void queue_userspace_req (tux_req_t *req, threadinfo_t *ti); + + +extern void __log_request (tux_req_t *req); +extern inline void log_request (tux_req_t *req) +{ + if (tux_logging) + __log_request(req); +} + +extern int __connection_too_fast (tux_req_t *req); + +#define connection_too_fast(req) \ + ({ \ + int __ret = 1; \ + if (unlikely(tux_max_output_bandwidth)) \ + __ret = __connection_too_fast(req); \ + __ret; \ + }) + +extern void trunc_headers (tux_req_t *req); +extern int generic_send_file (tux_req_t *req, struct socket *sock, int cachemiss); +extern int tux_fetch_file (tux_req_t *req, int nonblock); + +extern void postpone_request (tux_req_t *req); +extern int continue_request (int fd); +extern void tux_push_pending (struct sock *sk); +extern void zap_request (tux_req_t *req, int cachemiss); +extern int add_output_space_event (tux_req_t *req, struct socket *sock); + +extern void reap_kids (void); +extern void unuse_frag (struct sk_buff *skb, skb_frag_t *frag); +extern skb_frag_t * build_dynbuf_frag (tux_req_t *req, unsigned int size); +extern int tux_permission (struct inode *inode); +extern void flush_all_signals (void); + +#define D() Dprintk("{%s:%d}\n", __FILE__, __LINE__) + +extern int nr_async_io_pending (void); + +extern void __add_keepalive_timer (tux_req_t *req); +#define add_keepalive_timer(req) \ +do { \ + if (tux_keepalive_timeout) { \ + Dprintk("add_keepalive_timer(%p).\n", (req)); \ + __add_keepalive_timer(req); \ + } \ +} while (0) +extern void __del_keepalive_timer (tux_req_t *req); +#define del_keepalive_timer(req) \ +do { \ + if (tux_keepalive_timeout) { \ + Dprintk("del_keepalive_timer(%p).\n", (req)); \ + __del_keepalive_timer(req); \ + } \ +} while (0) + +extern void del_output_timer (tux_req_t *req); +extern void output_timeout (tux_req_t *req); + +extern void print_req (tux_req_t *req); + +extern char tux_date [DATE_LEN]; + + +extern int nr_async_io_pending (void); +extern void tux_exit (void); +extern char * get_abuf (tux_req_t *req, unsigned int max_size); +extern void send_abuf (tux_req_t *req, unsigned int size, unsigned long flags); + + +extern int idle_event (tux_req_t *req); +extern int output_space_event (tux_req_t *req); +extern cpumask_t tux_log_cpu_mask; +extern unsigned int tux_compression; +extern unsigned int tux_noid; +extern unsigned int tux_cgi_inherit_cpu; +extern unsigned int tux_zerocopy_header; +extern unsigned int tux_zerocopy_sendfile; +extern cpumask_t tux_cgi_cpu_mask; +extern tux_proto_t tux_proto_http; +extern tux_proto_t tux_proto_ftp; +extern unsigned int tux_all_userspace; +extern unsigned int tux_ignore_query; +extern unsigned int tux_redirect_logging; +extern unsigned int tux_referer_logging; +extern unsigned int tux_log_incomplete; +extern unsigned int tux_max_header_len; +extern unsigned int tux_cpu_offset; +extern unsigned int tux_ftp_login_message; + +extern void drop_permissions (void); +extern int query_extcgi (tux_req_t *req); +extern int tux_chroot (char *dir); + +extern void install_req_dentry (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt); +extern void release_req_dentry (tux_req_t *req); +extern void unidle_req (tux_req_t *req); +extern int nr_requests_used (void); + +#define req_err(req) do { (req)->error = 1; Dprintk("request %p error at %s:%d.\n", req, __FILE__, __LINE__); } while (0) + +#define enough_wspace(sk) (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) +#define clear_keepalive(req) do { (req)->keep_alive = 0; Dprintk("keepalive cleared for req %p.\n", req); } while (0) + +extern int print_all_requests (threadinfo_t *ti); +extern unsigned int tux_max_keepalives; +extern int time_unix2ls (time_t zulu, char *buf); +extern void last_mod_time(char * curr, const time_t t); +extern int mdtm_time(char * curr, const time_t t); +extern time_t parse_time(const char *str, const int str_len); + +extern unsigned int nr_tux_threads; +extern threadinfo_t threadinfo[CONFIG_TUX_NUMTHREADS]; + +#define switch_docroot(req) do { if (((req)->docroot_dentry != current->fs->root) || ((req)->docroot_mnt != current->fs->rootmnt)) __switch_docroot(req); } while (0) +extern void __switch_docroot(tux_req_t *req); +extern void list_directory (tux_req_t *req, int cachemiss); +extern char * tux_print_path (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt, char *buf, unsigned int max_len); + +extern unsigned int tux_http_dir_indexing; + +int tux_gzip_compress (tux_req_t *req, unsigned char *data_in, unsigned char *data_out, __u32 *in_len, __u32 *out_len); + +struct dentry * __tux_lookup (tux_req_t *req, const char *filename, + struct nameidata *base, struct vfsmount **mnt); + +/* error codes for req->error */ +#define TUX_ERROR_REDIRECT 1 +#define TUX_ERROR_UNUSED 2 +#define TUX_ERROR_CONN_CLOSE 3 +#define TUX_ERROR_CONN_TIMEOUT 4 + +extern void __put_data_sock (tux_req_t *req); + +static inline void put_data_sock (tux_req_t *req) +{ + if (req->data_sock) + __put_data_sock(req); +} + +#define socket_input(sock) \ + (!skb_queue_empty(&(sock)->sk->sk_receive_queue) || \ + !skb_queue_empty(&(sock)->sk->sk_error_queue)) + +#define tux_kmalloc(size) \ +({ \ + void *__ptr; \ + \ + while (!(__ptr = kmalloc(size, GFP_KERNEL))) { \ + if (net_ratelimit()) \ + printk(KERN_WARNING "tux: OOM at %s:%d (%d bytes).\n", \ + __FILE__, __LINE__, size); \ + current->state = TASK_UNINTERRUPTIBLE; \ + schedule_timeout(1); \ + } \ + __ptr; \ +}) + +#define tux_close(fd) sys_close(fd) + +extern int init_tux_request_slabs(void); +extern void free_tux_request_slabs(void); + +#endif Index: latest/include/net/tux_u.h =================================================================== --- /dev/null +++ latest/include/net/tux_u.h @@ -0,0 +1,163 @@ +#ifndef _NET_TUX_U_H +#define _NET_TUX_U_H + +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * tux_u.h: HTTP module API - HTTP interface to user-space + */ + +/* + * Different major versions are not compatible. + * Different minor versions are only downward compatible. + * Different patchlevel versions are downward and upward compatible. + */ +#define TUX_MAJOR_VERSION 3 +#define TUX_MINOR_VERSION 0 +#define TUX_PATCHLEVEL_VERSION 0 + +#define __KERNEL_SYSCALLS__ + +typedef enum http_versions { + HTTP_1_0, + HTTP_1_1 +} http_version_t; + +/* + * Request methods known to HTTP: + */ +typedef enum http_methods { + METHOD_NONE, + METHOD_GET, + METHOD_HEAD, + METHOD_POST, + METHOD_PUT, + NR_METHODS +} http_method_t; + +enum user_req { + TUX_ACTION_STARTUP = 1, + TUX_ACTION_SHUTDOWN = 2, + TUX_ACTION_STARTTHREAD = 3, + TUX_ACTION_STOPTHREAD = 4, + TUX_ACTION_EVENTLOOP = 5, + TUX_ACTION_GET_OBJECT = 6, + TUX_ACTION_SEND_OBJECT = 7, + TUX_ACTION_READ_OBJECT = 8, + TUX_ACTION_FINISH_REQ = 9, + TUX_ACTION_FINISH_CLOSE_REQ = 10, + TUX_ACTION_REGISTER_MODULE = 11, + TUX_ACTION_UNREGISTER_MODULE = 12, + TUX_ACTION_CURRENT_DATE = 13, + TUX_ACTION_REGISTER_MIMETYPE = 14, + TUX_ACTION_READ_HEADERS = 15, + TUX_ACTION_POSTPONE_REQ = 16, + TUX_ACTION_CONTINUE_REQ = 17, + TUX_ACTION_REDIRECT_REQ = 18, + TUX_ACTION_READ_POST_DATA = 19, + TUX_ACTION_SEND_BUFFER = 20, + TUX_ACTION_WATCH_PROXY_SOCKET = 21, + TUX_ACTION_WAIT_PROXY_SOCKET = 22, + TUX_ACTION_QUERY_VERSION = 23, + MAX_TUX_ACTION +}; + +enum tux_ret { + TUX_ERROR = -1, + TUX_RETURN_USERSPACE_REQUEST = 0, + TUX_RETURN_EXIT = 1, + TUX_RETURN_SIGNAL = 2, + TUX_CONTINUE_EVENTLOOP = 3, +}; + +#define MAX_URI_LEN 256 +#define MAX_COOKIE_LEN 128 +#define MAX_FIELD_LEN 64 +#define DATE_LEN 30 + +typedef struct user_req_s { + u32 version_major; + u32 version_minor; + u32 version_patch; + u32 http_version; + u32 http_method; + u32 http_status; + + u32 sock; + u32 event; + u32 error; + u32 thread_nr; + u32 bytes_sent; + u32 client_host; + u32 objectlen; + u32 module_index; + u32 keep_alive; + u32 cookies_len; + + u64 id; + u64 priv; + u64 object_addr; + + u8 query[MAX_URI_LEN]; + u8 objectname[MAX_URI_LEN]; + u8 cookies[MAX_COOKIE_LEN]; + u8 content_type[MAX_FIELD_LEN]; + u8 user_agent[MAX_FIELD_LEN]; + u8 accept[MAX_FIELD_LEN]; + u8 accept_charset[MAX_FIELD_LEN]; + u8 accept_encoding[MAX_FIELD_LEN]; + u8 accept_language[MAX_FIELD_LEN]; + u8 cache_control[MAX_FIELD_LEN]; + u8 if_modified_since[MAX_FIELD_LEN]; + u8 negotiate[MAX_FIELD_LEN]; + u8 pragma[MAX_FIELD_LEN]; + u8 referer[MAX_FIELD_LEN]; + u8 new_date[DATE_LEN]; + u8 pad[2]; + +} user_req_t; + +typedef enum ftp_commands { + FTP_COMM_NONE, + FTP_COMM_USER, + FTP_COMM_PASS, + FTP_COMM_ACCT, + FTP_COMM_CWD, + FTP_COMM_CDUP, + FTP_COMM_SMNT, + FTP_COMM_QUIT, + FTP_COMM_REIN, + FTP_COMM_PORT, + FTP_COMM_PASV, + FTP_COMM_TYPE, + FTP_COMM_STRU, + FTP_COMM_MODE, + FTP_COMM_RETR, + FTP_COMM_SIZE, + FTP_COMM_MDTM, + FTP_COMM_STOR, + FTP_COMM_STOU, + FTP_COMM_APPE, + FTP_COMM_ALLO, + FTP_COMM_REST, + FTP_COMM_RNFR, + FTP_COMM_RNTO, + FTP_COMM_ABOR, + FTP_COMM_DELE, + FTP_COMM_RMD, + FTP_COMM_MKD, + FTP_COMM_PWD, + FTP_COMM_LIST, + FTP_COMM_NLST, + FTP_COMM_SITE, + FTP_COMM_SYST, + FTP_COMM_STAT, + FTP_COMM_HELP, + FTP_COMM_NOOP, + FTP_COMM_FEAT, + FTP_COMM_CLNT, +} ftp_command_t; + +#endif Index: latest/kernel/exit.c =================================================================== --- latest.orig/kernel/exit.c +++ latest/kernel/exit.c @@ -811,6 +811,15 @@ fastcall NORET_TYPE void do_exit(long co hrtimer_cancel(&tsk->signal->real_timer); exit_itimers(tsk->signal); } + + if (current->tux_info) { +#ifdef CONFIG_TUX_DEBUG + printk("Possibly unexpected TUX-thread exit(%ld) at %p?\n", + code, __builtin_return_address(0)); +#endif + current->tux_exit(); + } + acct_collect(code, group_dead); if (unlikely(tsk->robust_list)) exit_robust_list(tsk); Index: latest/kernel/fork.c =================================================================== --- latest.orig/kernel/fork.c +++ latest/kernel/fork.c @@ -974,6 +974,8 @@ static struct task_struct *copy_process( if (!p) goto fork_out; + p->tux_info = NULL; + #ifdef CONFIG_TRACE_IRQFLAGS DEBUG_LOCKS_WARN_ON(!p->hardirqs_enabled); DEBUG_LOCKS_WARN_ON(!p->softirqs_enabled); Index: latest/kernel/kmod.c =================================================================== --- latest.orig/kernel/kmod.c +++ latest/kernel/kmod.c @@ -127,14 +127,14 @@ struct subprocess_info { /* * This is the task which runs the usermode application */ -static int ____call_usermodehelper(void *data) +int +__exec_usermodehelper(char *path, char **argv, char **envp, struct key *ring) { - struct subprocess_info *sub_info = data; struct key *new_session, *old_session; int retval; /* Unblock all signals and set the session keyring. */ - new_session = key_get(sub_info->ring); + new_session = key_get(ring); flush_signals(current); spin_lock_irq(¤t->sighand->siglock); old_session = __install_session_keyring(current, new_session); @@ -145,12 +145,28 @@ static int ____call_usermodehelper(void key_put(old_session); + retval = -EPERM; + if (current->fs->root) + retval = execve(path, argv, envp); + + return retval; +} + +EXPORT_SYMBOL_GPL(__exec_usermodehelper); + +/* + * This is the task which runs the usermode application + */ +static int ____call_usermodehelper(void *data) +{ + struct subprocess_info *sub_info = data; + int retval; + /* We can run anywhere, unlike our parent keventd(). */ set_cpus_allowed(current, CPU_MASK_ALL); - retval = -EPERM; - if (current->fs->root) - retval = execve(sub_info->path, sub_info->argv,sub_info->envp); + retval = __exec_usermodehelper(sub_info->path, + sub_info->argv, sub_info->envp, sub_info->ring); /* Exec failed? */ sub_info->retval = retval; Index: latest/kernel/signal.c =================================================================== --- latest.orig/kernel/signal.c +++ latest/kernel/signal.c @@ -335,6 +335,7 @@ flush_signal_handlers(struct task_struct } } +EXPORT_SYMBOL_GPL(flush_signal_handlers); /* Notify the system that a driver wants to block all signals for this * process, and wants to be notified if any signals at all were to be Index: latest/mm/filemap.c =================================================================== --- latest.orig/mm/filemap.c +++ latest/mm/filemap.c @@ -874,7 +874,8 @@ void do_generic_mapping_read(struct addr struct file *filp, loff_t *ppos, read_descriptor_t *desc, - read_actor_t actor) + read_actor_t actor, + int nonblock) { struct inode *inode = mapping->host; unsigned long index; @@ -924,11 +925,21 @@ void do_generic_mapping_read(struct addr find_page: page = find_get_page(mapping, index); if (unlikely(page == NULL)) { + if (nonblock) { + desc->error = -EWOULDBLOCKIO; + break; + } handle_ra_miss(mapping, &ra, index); goto no_cached_page; } - if (!PageUptodate(page)) + if (!PageUptodate(page)) { + if (nonblock) { + page_cache_release(page); + desc->error = -EWOULDBLOCKIO; + break; + } goto page_not_up_to_date; + } page_ok: /* If users can be writing to this page using arbitrary @@ -1195,7 +1206,7 @@ __generic_file_aio_read(struct kiocb *io if (desc.count == 0) continue; desc.error = 0; - do_generic_file_read(filp,ppos,&desc,file_read_actor); + do_generic_file_read(filp,ppos,&desc,file_read_actor,0); retval += desc.written; if (desc.error) { retval = retval ?: desc.error; @@ -1266,7 +1277,7 @@ ssize_t generic_file_sendfile(struct fil desc.arg.data = target; desc.error = 0; - do_generic_file_read(in_file, ppos, &desc, actor); + do_generic_file_read(in_file, ppos, &desc, actor, 0); if (desc.written) return desc.written; return desc.error; Index: latest/mm/truncate.c =================================================================== --- latest.orig/mm/truncate.c +++ latest/mm/truncate.c @@ -266,6 +266,8 @@ unlock: return ret; } +EXPORT_SYMBOL_GPL(invalidate_mapping_pages); + unsigned long invalidate_inode_pages(struct address_space *mapping) { return invalidate_mapping_pages(mapping, 0, ~0UL); Index: latest/net/core/sock.c =================================================================== --- latest.orig/net/core/sock.c +++ latest/net/core/sock.c @@ -904,7 +904,7 @@ void sk_free(struct sock *sk) module_put(owner); } -struct sock *sk_clone(const struct sock *sk, const gfp_t priority) +struct sock *sk_clone(struct sock *sk, const gfp_t priority) { struct sock *newsk = sk_alloc(sk->sk_family, priority, sk->sk_prot, 0); @@ -946,6 +946,9 @@ struct sock *sk_clone(const struct sock if (filter != NULL) sk_filter_charge(newsk, filter); + if (sk->sk_create_child) + sk->sk_create_child(sk, newsk); + if (unlikely(xfrm_sk_clone_policy(newsk))) { /* It is still raw copy of parent, so invalidate * destructor and make plain sk_free() */ Index: latest/net/ipv4/tcp.c =================================================================== --- latest.orig/net/ipv4/tcp.c +++ latest/net/ipv4/tcp.c @@ -2349,3 +2349,4 @@ EXPORT_SYMBOL(tcp_sendpage); EXPORT_SYMBOL(tcp_setsockopt); EXPORT_SYMBOL(tcp_shutdown); EXPORT_SYMBOL(tcp_statistics); +EXPORT_SYMBOL_GPL(tcp_cleanup_rbuf); Index: latest/net/ipv4/tcp_input.c =================================================================== --- latest.orig/net/ipv4/tcp_input.c +++ latest/net/ipv4/tcp_input.c @@ -3534,6 +3534,7 @@ static int tcp_prune_queue(struct sock * return -1; } +EXPORT_SYMBOL_GPL(tcp_cwnd_application_limited); /* RFC2861, slow part. Adjust cwnd, after it was not full during one rto. * As additional protections, we do not touch cwnd in retransmission phases, Index: latest/net/ipv4/tcp_output.c =================================================================== --- latest.orig/net/ipv4/tcp_output.c +++ latest/net/ipv4/tcp_output.c @@ -858,6 +858,8 @@ unsigned int tcp_current_mss(struct sock return mss_now; } +EXPORT_SYMBOL_GPL(tcp_current_mss); + /* Congestion window validation. (RFC2861) */ static void tcp_cwnd_validate(struct sock *sk, struct tcp_sock *tp) @@ -1371,6 +1373,7 @@ void __tcp_push_pending_frames(struct so tcp_check_probe_timer(sk, tp); } } +EXPORT_SYMBOL_GPL(__tcp_push_pending_frames); /* Send _single_ skb sitting at the send head. This function requires * true push pending frames to setup probe timer etc. Index: latest/net/Kconfig =================================================================== --- latest.orig/net/Kconfig +++ latest/net/Kconfig @@ -245,6 +245,7 @@ source "net/ax25/Kconfig" source "net/irda/Kconfig" source "net/bluetooth/Kconfig" source "net/ieee80211/Kconfig" +source "net/tux/Kconfig" config WIRELESS_EXT bool Index: latest/net/Makefile =================================================================== --- latest.orig/net/Makefile +++ latest/net/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_UNIX) += unix/ ifneq ($(CONFIG_IPV6),) obj-y += ipv6/ endif +obj-$(CONFIG_TUX) += tux/ obj-$(CONFIG_PACKET) += packet/ obj-$(CONFIG_NET_KEY) += key/ obj-$(CONFIG_NET_SCHED) += sched/ Index: latest/net/socket.c =================================================================== --- latest.orig/net/socket.c +++ latest/net/socket.c @@ -67,6 +67,7 @@ #include <linux/netdevice.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <net/tux.h> #include <linux/mutex.h> #include <linux/wanrouter.h> #include <linux/if_bridge.h> @@ -123,7 +124,7 @@ static ssize_t sock_sendpage(struct file * in the operation structures but are done directly via the socketcall() multiplexor. */ -static struct file_operations socket_file_ops = { +struct file_operations socket_file_ops = { .owner = THIS_MODULE, .llseek = no_llseek, .aio_read = sock_aio_read, @@ -511,7 +512,7 @@ static struct socket *sockfd_lookup_ligh * NULL is returned. */ -static struct socket *sock_alloc(void) +struct socket *sock_alloc(void) { struct inode * inode; struct socket * sock; @@ -531,6 +532,8 @@ static struct socket *sock_alloc(void) return sock; } +EXPORT_SYMBOL_GPL(sock_alloc); + /* * In theory you can't get an open on this inode, but /proc provides * a back door. Remember to keep it shut otherwise you'll let the @@ -1090,6 +1093,8 @@ static int sock_fasync(int fd, struct fi } out: + if (sock->sk != sk) + BUG(); release_sock(sock->sk); return 0; } @@ -2130,6 +2135,51 @@ static int __init sock_init(void) core_initcall(sock_init); /* early initcall */ +int tux_Dprintk; +int tux_TDprintk; + +struct module *tux_module = NULL; + +#ifdef CONFIG_TUX_MODULE + +asmlinkage long (*sys_tux_ptr) (unsigned int action, user_req_t *u_info) = NULL; +spinlock_t tux_module_lock = SPIN_LOCK_UNLOCKED; + +asmlinkage long sys_tux (unsigned int action, user_req_t *u_info) +{ + int ret; + + if (current->tux_info) + return sys_tux_ptr(action, u_info); + + ret = -ENOSYS; + spin_lock(&tux_module_lock); + if (!tux_module) + goto out_unlock; + if (!try_module_get(tux_module)) + goto out_unlock; + spin_unlock(&tux_module_lock); + + if (!sys_tux_ptr) + TUX_BUG(); + ret = sys_tux_ptr(action, u_info); + + spin_lock(&tux_module_lock); + module_put(tux_module); +out_unlock: + spin_unlock(&tux_module_lock); + + return ret; +} + +EXPORT_SYMBOL_GPL(tux_module); +EXPORT_SYMBOL_GPL(tux_module_lock); +EXPORT_SYMBOL_GPL(sys_tux_ptr); + +EXPORT_SYMBOL_GPL(tux_Dprintk); +EXPORT_SYMBOL_GPL(tux_TDprintk); + +#endif #ifdef CONFIG_PROC_FS void socket_seq_show(struct seq_file *seq) { Index: latest/net/tux/abuf.c =================================================================== --- /dev/null +++ latest/net/tux/abuf.c @@ -0,0 +1,190 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * abuf.c: async buffer-sending + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +char * get_abuf (tux_req_t *req, unsigned int max_size) +{ + threadinfo_t *ti = req->ti; + struct page *page; + char *buf; + unsigned int offset; + unsigned int left; + + if (req->abuf.page || req->abuf.buf || req->abuf.size) + TUX_BUG(); + + if (max_size > PAGE_SIZE) + BUG(); + offset = ti->header_offset; + if (offset > PAGE_SIZE) + TUX_BUG(); + left = PAGE_SIZE - offset; + if (!max_size) + BUG(); + page = ti->header_cache; + if ((left < max_size) || !page) { + while (!(page = alloc_pages(GFP_KERNEL, 0))) { + if (net_ratelimit()) + printk(KERN_WARNING "tux: OOM in get_abuf()!\n"); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1); + } + + if (ti->header_cache) + __free_page(ti->header_cache); + ti->header_cache = page; + ti->header_offset = 0; + offset = 0; + } + buf = page_address(page) + offset; + + if (!page) + BUG(); + req->abuf.page = page; + req->abuf.buf = buf; + req->abuf.size = 0; + req->abuf.offset = offset; + req->abuf.flags = 0; + get_page(req->abuf.page); + + return buf; +} + +static void do_send_abuf (tux_req_t *req, int cachemiss); + +void send_abuf (tux_req_t *req, unsigned int size, unsigned long flags) +{ + threadinfo_t *ti = req->ti; + + Dprintk("send_abuf(req: %p, sock: %p): %p(%p), size:%d, off:%d, flags:%08lx\n", req, req->sock, req->abuf.page, req->abuf.buf, size, req->abuf.offset, flags); + + ti->header_offset += size; + if (ti->header_offset > PAGE_SIZE) + TUX_BUG(); + if (req->abuf.offset + req->abuf.size > PAGE_SIZE) + TUX_BUG(); + + req->abuf.flags = flags | MSG_NOSIGNAL; + req->abuf.size = size; + + add_tux_atom(req, do_send_abuf); +} + +static void do_send_abuf (tux_req_t *req, int cachemiss) +{ + int ret; + + if (req->magic != TUX_MAGIC) + TUX_BUG(); + if (!req->sock) + TUX_BUG(); + tcp_sk(req->sock->sk)->nonagle = 2; + +repeat: + Dprintk("do_send_abuf(%p,%d): %p(%p), size:%d, off:%d, flags:%08lx\n", + req, cachemiss, + req->abuf.page, req->abuf.buf, req->abuf.size, + req->abuf.offset, req->abuf.flags); + + if (tux_zerocopy_header) + ret = tcp_sendpage(req->sock, req->abuf.page, + req->abuf.offset, req->abuf.size, req->abuf.flags); + else { + mm_segment_t oldmm; + oldmm = get_fs(); set_fs(KERNEL_DS); + ret = send_sync_buf(req, req->sock, req->abuf.buf, + req->abuf.size, req->abuf.flags); + set_fs(oldmm); + } + + + Dprintk("do_send_abuf: ret: %d\n", ret); + if (!ret) + TUX_BUG(); + + if (ret < 0) { + if (ret != -EAGAIN) { + TDprintk("ret: %d, req->error = TUX_ERROR_CONN_CLOSE.\n", ret); + req->error = TUX_ERROR_CONN_CLOSE; + req->atom_idx = 0; + req->in_file->f_pos = 0; + __free_page(req->abuf.page); + memset(&req->abuf, 0, sizeof(req->abuf)); + zap_request(req, cachemiss); + return; + } + add_tux_atom(req, do_send_abuf); + if (add_output_space_event(req, req->sock)) { + del_tux_atom(req); + goto repeat; + } + return; + } + + req->abuf.buf += ret; + req->abuf.offset += ret; + req->abuf.size -= ret; + + if ((int)req->abuf.size < 0) + TUX_BUG(); + if (req->abuf.size > 0) + goto repeat; + + Dprintk("DONE do_send_abuf: %p(%p), size:%d, off:%d, flags:%08lx\n", + req->abuf.page, req->abuf.buf, req->abuf.size, + req->abuf.offset, req->abuf.flags); + + if (req->abuf.page) + __free_page(req->abuf.page); + else + if (printk_ratelimit()) + WARN_ON(1); + + memset(&req->abuf, 0, sizeof(req->abuf)); + + add_req_to_workqueue(req); +} + +void __send_async_message (tux_req_t *req, const char *message, + int status, unsigned int size, int push) +{ + unsigned int flags; + char *buf; + + Dprintk("TUX: sending %d reply (%d bytes)!\n", status, size); + Dprintk("request %p, reply: %s\n", req, message); + if (!size) + TUX_BUG(); + buf = get_abuf(req, size); + memcpy(buf, message, size); + + req->status = status; + flags = MSG_DONTWAIT; + if (!push) + flags |= MSG_MORE; + send_abuf(req, size, flags); + add_req_to_workqueue(req); +} Index: latest/net/tux/accept.c =================================================================== --- /dev/null +++ latest/net/tux/accept.c @@ -0,0 +1,863 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * accept.c: accept new connections, allocate requests + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +unsigned int tux_ack_pingpong = 1; +unsigned int tux_push_all = 0; +unsigned int tux_zerocopy_parse = 1; + +static int __idle_event (tux_req_t *req); +static int __output_space_event (tux_req_t *req); + +struct socket * start_listening(tux_socket_t *listen, int nr) +{ + struct sockaddr_in sin; + struct socket *sock = NULL; + struct sock *sk; + struct tcp_sock *tp; + struct inet_connection_sock *icsk; + int err; + u16 port = listen->port; + u32 addr = listen->ip; + tux_proto_t *proto = listen->proto; + + /* Create a listening socket: */ + + err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock); + if (err) { + printk(KERN_ERR "TUX: error %d creating socket.\n", err); + goto error; + } + + /* Bind the socket: */ + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(addr); + sin.sin_port = htons(port); + + sk = sock->sk; + icsk = inet_csk(sk); + sk->sk_reuse = 1; + sock_set_flag(sk, SOCK_URGINLINE); + + err = sock->ops->bind(sock, (struct sockaddr*)&sin, sizeof(sin)); + if (err) { + printk(KERN_ERR "TUX: error %d binding socket. This means that probably some other process is (or was a short time ago) using addr %s://%d.%d.%d.%d:%d.\n", + err, proto->name, HIPQUAD(addr), port); + goto error; + } + + tp = tcp_sk(sk); + Dprintk("listen sk accept_queue: %d.\n", + !reqsk_queue_empty(&icsk->icsk_accept_queue)); + icsk->icsk_ack.pingpong = tux_ack_pingpong; + + sock_reset_flag(sk, SOCK_LINGER); + sk->sk_lingertime = 0; + tp->linger2 = tux_keepalive_timeout * HZ; + + if (proto->defer_accept && !tux_keepalive_timeout && tux_defer_accept) + icsk->icsk_accept_queue.rskq_defer_accept = 1; + + /* Now, start listening on the socket */ + + err = sock->ops->listen(sock, tux_max_backlog); + if (err) { + printk(KERN_ERR "TUX: error %d listening on socket.\n", err); + goto error; + } + + printk(KERN_NOTICE "TUX: thread %d listens on %s://%d.%d.%d.%d:%d.\n", + nr, proto->name, HIPQUAD(addr), port); + return sock; + +error: + if (sock) + sock_release(sock); + return NULL; +} + +static inline void __kfree_req (tux_req_t *req, threadinfo_t * ti) +{ + list_del(&req->all); + DEBUG_DEL_LIST(&req->all); + ti->nr_requests--; + kfree(req); +} + +int flush_freequeue (threadinfo_t * ti) +{ + struct list_head *tmp; + unsigned long flags; + tux_req_t *req; + int count = 0; + + spin_lock_irqsave(&ti->free_requests_lock,flags); + while (ti->nr_free_requests) { + ti->nr_free_requests--; + tmp = ti->free_requests.next; + req = list_entry(tmp, tux_req_t, free); + list_del(tmp); + DEBUG_DEL_LIST(tmp); + DEC_STAT(nr_free_pending); + __kfree_req(req, ti); + count++; + } + spin_unlock_irqrestore(&ti->free_requests_lock,flags); + + return count; +} + +static tux_req_t * kmalloc_req (threadinfo_t * ti) +{ + struct list_head *tmp; + unsigned long flags; + tux_req_t *req; + + spin_lock_irqsave(&ti->free_requests_lock, flags); + if (ti->nr_free_requests) { + ti->nr_free_requests--; + tmp = ti->free_requests.next; + req = list_entry(tmp, tux_req_t, free); + list_del(tmp); + DEBUG_DEL_LIST(tmp); + DEC_STAT(nr_free_pending); + req->magic = TUX_MAGIC; + spin_unlock_irqrestore(&ti->free_requests_lock, flags); + } else { + spin_unlock_irqrestore(&ti->free_requests_lock, flags); + req = tux_kmalloc(sizeof(*req)); + ti->nr_requests++; + memset (req, 0, sizeof(*req)); + list_add(&req->all, &ti->all_requests); + } + req->magic = TUX_MAGIC; + INC_STAT(nr_allocated); + init_waitqueue_entry(&req->sleep, current); + init_waitqueue_entry(&req->ftp_sleep, current); + INIT_LIST_HEAD(&req->work); + INIT_LIST_HEAD(&req->free); + INIT_LIST_HEAD(&req->lru); + req->ti = ti; + req->total_bytes = 0; + SET_TIMESTAMP(req->accept_timestamp); + req->first_timestamp = jiffies; + req->fd = -1; + init_timer(&req->keepalive_timer); + init_timer(&req->output_timer); + + Dprintk("allocated NEW req %p.\n", req); + return req; +} + +void kfree_req (tux_req_t *req) +{ + threadinfo_t * ti = req->ti; + unsigned long flags; + + Dprintk("freeing req %p.\n", req); + + if (req->magic != TUX_MAGIC) + TUX_BUG(); + spin_lock_irqsave(&ti->free_requests_lock,flags); + req->magic = 0; + DEC_STAT(nr_allocated); + if (req->sock || req->dentry || req->private) + TUX_BUG(); + if (ti->nr_free_requests > tux_max_free_requests) + __kfree_req(req, ti); + else { + req->error = 0; + ti->nr_free_requests++; + + // the free requests queue is LIFO + list_add(&req->free, &ti->free_requests); + INC_STAT(nr_free_pending); + } + spin_unlock_irqrestore(&ti->free_requests_lock,flags); +} + +static void __add_req_to_workqueue (tux_req_t *req) +{ + threadinfo_t *ti = req->ti; + + if (!list_empty(&req->work)) + TUX_BUG(); + Dprintk("work-queueing request %p at %p/%p.\n", req, __builtin_return_address(0), __builtin_return_address(1)); + if (connection_too_fast(req)) + list_add_tail(&req->work, &ti->work_pending); + else + list_add(&req->work, &ti->work_pending); + INC_STAT(nr_work_pending); + wake_up_process(ti->thread); + return; +} + +void add_req_to_workqueue (tux_req_t *req) +{ + unsigned long flags; + threadinfo_t *ti = req->ti; + + spin_lock_irqsave(&ti->work_lock, flags); + __add_req_to_workqueue(req); + spin_unlock_irqrestore(&ti->work_lock, flags); +} + +void del_output_timer (tux_req_t *req) +{ +#ifdef CONFIG_SMP + if (!spin_is_locked(&req->ti->work_lock)) + TUX_BUG(); +#endif + if (!list_empty(&req->lru)) { + list_del(&req->lru); + DEBUG_DEL_LIST(&req->lru); + req->ti->nr_lru--; + } + Dprintk("del output timeout for req %p.\n", req); + del_timer(&req->output_timer); +} + +static void output_timeout_fn (unsigned long data); + +#define OUTPUT_TIMEOUT HZ + +static void add_output_timer (tux_req_t *req) +{ + struct timer_list *timer = &req->output_timer; + + timer->data = (unsigned long) req; + timer->function = &output_timeout_fn; + mod_timer(timer, jiffies + OUTPUT_TIMEOUT); +} + +static void output_timeout_fn (unsigned long data) +{ + tux_req_t *req = (tux_req_t *)data; + + if (connection_too_fast(req)) { + add_output_timer(req); +// mod_timer(&req->output_timer, jiffies + OUTPUT_TIMEOUT); + return; + } + output_space_event(req); +} + +void output_timeout (tux_req_t *req) +{ + Dprintk("output timeout for req %p.\n", req); + if (test_and_set_bit(0, &req->wait_output_space)) + TUX_BUG(); + INC_STAT(nr_output_space_pending); + add_output_timer(req); +} + +void __del_keepalive_timer (tux_req_t *req) +{ +#ifdef CONFIG_SMP + if (!spin_is_locked(&req->ti->work_lock)) + TUX_BUG(); +#endif + if (!list_empty(&req->lru)) { + list_del(&req->lru); + DEBUG_DEL_LIST(&req->lru); + req->ti->nr_lru--; + } + Dprintk("del keepalive timeout for req %p.\n", req); + del_timer(&req->keepalive_timer); +} + +static void keepalive_timeout_fn (unsigned long data) +{ + tux_req_t *req = (tux_req_t *)data; + +#ifdef CONFIG_TUX_DEBUG + Dprintk("req %p timed out after %d sec!\n", req, tux_keepalive_timeout); + if (tux_Dprintk) + print_req(req); +#endif + Dprintk("req->error = TUX_ERROR_CONN_TIMEOUT!\n"); + req->error = TUX_ERROR_CONN_TIMEOUT; + if (!idle_event(req)) + output_space_event(req); +} + +void __add_keepalive_timer (tux_req_t *req) +{ + struct timer_list *timer = &req->keepalive_timer; + + if (!tux_keepalive_timeout) + TUX_BUG(); +#ifdef CONFIG_SMP + if (!spin_is_locked(&req->ti->work_lock)) + TUX_BUG(); +#endif + + if (!list_empty(&req->lru)) + TUX_BUG(); + if (req->ti->nr_lru > tux_max_keepalives) { + struct list_head *head, *last; + tux_req_t *last_req; + + head = &req->ti->lru; + last = head->prev; + if (last == head) + TUX_BUG(); + last_req = list_entry(last, tux_req_t, lru); + list_del(last); + DEBUG_DEL_LIST(last); + req->ti->nr_lru--; + + Dprintk("LRU-aging req %p!\n", last_req); + last_req->error = TUX_ERROR_CONN_TIMEOUT; + if (!__idle_event(last_req)) + __output_space_event(last_req); + } + list_add(&req->lru, &req->ti->lru); + req->ti->nr_lru++; + + timer->expires = jiffies + tux_keepalive_timeout * HZ; + timer->data = (unsigned long) req; + timer->function = &keepalive_timeout_fn; + add_timer(timer); +} + +static int __output_space_event (tux_req_t *req) +{ + if (!req || (req->magic != TUX_MAGIC)) + TUX_BUG(); + + if (!test_and_clear_bit(0, &req->wait_output_space)) { + Dprintk("output space ready event at <%p>, on non-idle %p.\n", __builtin_return_address(0), req); + return 0; + } + + Dprintk("output space ready event at <%p>, %p was waiting!\n", __builtin_return_address(0), req); + DEC_STAT(nr_output_space_pending); + + del_keepalive_timer(req); + del_output_timer(req); + + __add_req_to_workqueue(req); + return 1; +} + +int output_space_event (tux_req_t *req) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&req->ti->work_lock, flags); + ret = __output_space_event(req); + spin_unlock_irqrestore(&req->ti->work_lock, flags); + + return ret; +} + +static int __idle_event (tux_req_t *req) +{ + struct inet_connection_sock *icsk; + threadinfo_t *ti; + + if (!req || (req->magic != TUX_MAGIC)) + TUX_BUG(); + ti = req->ti; + + if (!test_and_clear_bit(0, &req->idle_input)) { + Dprintk("data ready event at <%p>, on non-idle %p.\n", __builtin_return_address(0), req); + return 0; + } + + Dprintk("data ready event at <%p>, %p was idle!\n", __builtin_return_address(0), req); + del_keepalive_timer(req); + del_output_timer(req); + DEC_STAT(nr_idle_input_pending); + + icsk = inet_csk(req->sock->sk); + + icsk->icsk_ack.pingpong = tux_ack_pingpong; + SET_TIMESTAMP(req->accept_timestamp); + + __add_req_to_workqueue(req); + + return 1; +} + +int idle_event (tux_req_t *req) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&req->ti->work_lock, flags); + ret = __idle_event(req); + spin_unlock_irqrestore(&req->ti->work_lock, flags); + + return ret; +} + +#define HANDLE_CALLBACK_1(callback, tux_name, real_name, param...) \ + tux_req_t *req; \ + \ + read_lock(&sk->sk_callback_lock); \ + req = sk->sk_user_data; \ + \ + Dprintk("callback "#callback"(%p) req %p.\n", \ + sk->sk_##callback, req); \ + \ + if (!req) { \ + if (sk->sk_##callback == tux_name) { \ + printk("BUG: "#callback" "#tux_name" "#real_name" no req!"); \ + TUX_BUG(); \ + } \ + read_unlock(&sk->sk_callback_lock); \ + if (sk->sk_##callback) \ + sk->sk_##callback(param); \ + return; \ + } \ + +#define HANDLE_CALLBACK_2(callback, tux_name, real_name, param...) \ + Dprintk(#tux_name"() on %p.\n", req); \ + if (req->magic != TUX_MAGIC) \ + TUX_BUG(); \ + if (req->real_name) \ + req->real_name(param); + +#define HANDLE_CALLBACK(callback, tux_name, real_name, param...) \ + HANDLE_CALLBACK_1(callback,tux_name,real_name,param) \ + HANDLE_CALLBACK_2(callback,tux_name,real_name,param) + +static void tux_data_ready (struct sock *sk, int len) +{ + HANDLE_CALLBACK_1(data_ready, tux_data_ready, real_data_ready, sk, len); + + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_write_space (struct sock *sk) +{ + HANDLE_CALLBACK(write_space, tux_write_space, real_write_space, sk); + + Dprintk("sk->sk_wmem_queued: %d, sk->sk_sndbuf: %d.\n", + sk->sk_wmem_queued, sk->sk_sndbuf); + + if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) { + clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + if (!idle_event(req)) + output_space_event(req); + } + read_unlock(&sk->sk_callback_lock); +} + +static void tux_error_report (struct sock *sk) +{ + HANDLE_CALLBACK(error_report, tux_error_report, real_error_report, sk); + + req->error = TUX_ERROR_CONN_CLOSE; + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_state_change (struct sock *sk) +{ + HANDLE_CALLBACK(state_change, tux_state_change, real_state_change, sk); + + if (req->sock && req->sock->sk && + (req->sock->sk->sk_state > TCP_ESTABLISHED)) { + Dprintk("req %p changed to TCP non-established!\n", req); + Dprintk("req->sock: %p\n", req->sock); + if (req->sock) + Dprintk("req->sock->sk: %p\n", req->sock->sk); + if (req->sock && req->sock->sk) + Dprintk("TCP state: %d\n", req->sock->sk->sk_state); + Dprintk("req->error = TUX_ERROR_CONN_CLOSE!\n"); + req->error = TUX_ERROR_CONN_CLOSE; + } + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_destruct (struct sock *sk) +{ + BUG(); +} + +static void tux_ftp_data_ready (struct sock *sk, int len) +{ + HANDLE_CALLBACK_1(data_ready, tux_ftp_data_ready, + ftp_real_data_ready, sk, len); + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_ftp_write_space (struct sock *sk) +{ + HANDLE_CALLBACK_1(write_space, tux_ftp_write_space, + ftp_real_write_space, sk); + + Dprintk("sk->sk_wmem_queued: %d, sk->sk_sndbuf: %d.\n", + sk->sk_wmem_queued, sk->sk_sndbuf); + + if (sk_stream_wspace(sk) >= sk->sk_sndbuf/10*8) { + clear_bit(SOCK_NOSPACE, &sk->sk_socket->flags); + if (!idle_event(req)) + output_space_event(req); + } + read_unlock(&sk->sk_callback_lock); +} + +static void tux_ftp_error_report (struct sock *sk) +{ + HANDLE_CALLBACK(error_report, tux_ftp_error_report, + ftp_real_error_report, sk); + + TDprintk("req %p sock %p got TCP errors on FTP data connection!\n", req, sk); + TDprintk("req->error = TUX_ERROR_CONN_CLOSE!\n"); + req->error = TUX_ERROR_CONN_CLOSE; + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_ftp_state_change (struct sock *sk) +{ + HANDLE_CALLBACK(state_change, tux_ftp_state_change, + ftp_real_state_change, sk); + + if (req->sock && req->sock->sk && + (req->sock->sk->sk_state > TCP_ESTABLISHED)) { + Dprintk("req %p FTP control sock changed to TCP non-established!\n", req); + Dprintk("req->sock: %p\n", req->sock); + TDprintk("req->error = TUX_ERROR_CONN_CLOSE!\n"); + + req->error = TUX_ERROR_CONN_CLOSE; + } + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_ftp_create_child (struct sock *sk, struct sock *newsk) +{ + HANDLE_CALLBACK(create_child, tux_ftp_create_child, + ftp_real_create_child, sk, newsk); + + newsk->sk_user_data = NULL; + newsk->sk_data_ready = req->ftp_real_data_ready; + newsk->sk_state_change = req->ftp_real_state_change; + newsk->sk_write_space = req->ftp_real_write_space; + newsk->sk_error_report = req->ftp_real_error_report; + newsk->sk_create_child = req->ftp_real_create_child; + newsk->sk_destruct = req->ftp_real_destruct; + + if (!idle_event(req)) + output_space_event(req); + read_unlock(&sk->sk_callback_lock); +} + +static void tux_ftp_destruct (struct sock *sk) +{ + BUG(); +} + +static void link_tux_socket (tux_req_t *req, struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (req->sock) + TUX_BUG(); + if (sk->sk_destruct == tux_destruct) + TUX_BUG(); + /* + * (No need to lock the socket, we just want to + * make sure that events from now on go through + * tux_data_ready()) + */ + write_lock_irq(&sk->sk_callback_lock); + + req->sock = sock; + sk->sk_user_data = req; + + req->real_data_ready = sk->sk_data_ready; + req->real_state_change = sk->sk_state_change; + req->real_write_space = sk->sk_write_space; + req->real_error_report = sk->sk_error_report; + req->real_destruct = sk->sk_destruct; + + sk->sk_data_ready = tux_data_ready; + sk->sk_state_change = tux_state_change; + sk->sk_write_space = tux_write_space; + sk->sk_error_report = tux_error_report; + sk->sk_destruct = tux_destruct; + + write_unlock_irq(&sk->sk_callback_lock); + + if (req->real_destruct == tux_destruct) + TUX_BUG(); + req->client_addr = inet_sk(sk)->daddr; + req->client_port = inet_sk(sk)->dport; + + add_wait_queue(sk->sk_sleep, &req->sleep); +} + +void __link_data_socket (tux_req_t *req, struct socket *sock, + struct sock *sk) +{ + /* + * (No need to lock the socket, we just want to + * make sure that events from now on go through + * tux_data_ready()) + */ + write_lock_irq(&sk->sk_callback_lock); + + req->data_sock = sock; + sk->sk_user_data = req; + + req->ftp_real_data_ready = sk->sk_data_ready; + req->ftp_real_state_change = sk->sk_state_change; + req->ftp_real_write_space = sk->sk_write_space; + req->ftp_real_error_report = sk->sk_error_report; + req->ftp_real_create_child = sk->sk_create_child; + req->ftp_real_destruct = sk->sk_destruct; + + sk->sk_data_ready = tux_ftp_data_ready; + sk->sk_state_change = tux_ftp_state_change; + sk->sk_write_space = tux_ftp_write_space; + sk->sk_error_report = tux_ftp_error_report; + sk->sk_create_child = tux_ftp_create_child; + sk->sk_destruct = tux_ftp_destruct; + + if (req->ftp_real_destruct == tux_ftp_destruct) + TUX_BUG(); + + write_unlock_irq(&sk->sk_callback_lock); + + add_wait_queue(sk->sk_sleep, &req->ftp_sleep); +} + +void link_tux_data_socket (tux_req_t *req, struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (req->data_sock) + TUX_BUG(); + if (sk->sk_destruct == tux_ftp_destruct) + TUX_BUG(); + __link_data_socket(req, sock, sk); +} + +void unlink_tux_socket (tux_req_t *req) +{ + struct sock *sk; + + if (!req->sock || !req->sock->sk) + return; + sk = req->sock->sk; + + write_lock_irq(&sk->sk_callback_lock); + if (!sk->sk_user_data) + TUX_BUG(); + if (req->real_destruct == tux_destruct) + TUX_BUG(); + + sk->sk_user_data = NULL; + + sk->sk_data_ready = req->real_data_ready; + sk->sk_state_change = req->real_state_change; + sk->sk_write_space = req->real_write_space; + sk->sk_error_report = req->real_error_report; + sk->sk_destruct = req->real_destruct; + + if (sk->sk_destruct == tux_destruct) + TUX_BUG(); + + req->real_data_ready = NULL; + req->real_state_change = NULL; + req->real_write_space = NULL; + req->real_error_report = NULL; + req->real_destruct = NULL; + + write_unlock_irq(&sk->sk_callback_lock); + + remove_wait_queue(sk->sk_sleep, &req->sleep); +} + +void unlink_tux_data_socket (tux_req_t *req) +{ + struct sock *sk; + + if (!req->data_sock || !req->data_sock->sk) + return; + sk = req->data_sock->sk; + + write_lock_irq(&sk->sk_callback_lock); + + if (req->real_destruct == tux_ftp_destruct) + TUX_BUG(); + + sk->sk_user_data = NULL; + sk->sk_data_ready = req->ftp_real_data_ready; + sk->sk_state_change = req->ftp_real_state_change; + sk->sk_write_space = req->ftp_real_write_space; + sk->sk_error_report = req->ftp_real_error_report; + sk->sk_create_child = req->ftp_real_create_child; + sk->sk_destruct = req->ftp_real_destruct; + + req->ftp_real_data_ready = NULL; + req->ftp_real_state_change = NULL; + req->ftp_real_write_space = NULL; + req->ftp_real_error_report = NULL; + req->ftp_real_create_child = NULL; + req->ftp_real_destruct = NULL; + + write_unlock_irq(&sk->sk_callback_lock); + + if (sk->sk_destruct == tux_ftp_destruct) + TUX_BUG(); + + remove_wait_queue(sk->sk_sleep, &req->ftp_sleep); +} + +void add_tux_atom (tux_req_t *req, atom_func_t *atom) +{ + Dprintk("adding TUX atom %p to req %p, atom_idx: %d, at %p/%p.\n", + atom, req, req->atom_idx, __builtin_return_address(0), __builtin_return_address(1)); + if (req->atom_idx == MAX_TUX_ATOMS) + TUX_BUG(); + req->atoms[req->atom_idx] = atom; + req->atom_idx++; +} + +void del_tux_atom (tux_req_t *req) +{ + if (!req->atom_idx) + TUX_BUG(); + req->atom_idx--; + Dprintk("removing TUX atom %p to req %p, atom_idx: %d, at %p.\n", + req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0)); +} + +void tux_schedule_atom (tux_req_t *req, int cachemiss) +{ + if (!list_empty(&req->work)) + TUX_BUG(); + if (!req->atom_idx) + TUX_BUG(); + req->atom_idx--; + Dprintk("DOING TUX atom %p, req %p, atom_idx: %d, at %p.\n", + req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0)); + might_sleep(); + req->atoms[req->atom_idx](req, cachemiss); + might_sleep(); + Dprintk("DONE TUX atom %p, req %p, atom_idx: %d, at %p.\n", + req->atoms[req->atom_idx], req, req->atom_idx, __builtin_return_address(0)); +} + +/* + * Puts newly accepted connections into the inputqueue. This is the + * first step in the life of a TUX request. + */ +int accept_requests (threadinfo_t *ti) +{ + int count = 0, last_count = 0, error, socknr = 0; + struct socket *sock, *new_sock; + struct tcp_sock *tp2; + struct inet_connection_sock *icsk1, *icsk2; + tux_req_t *req; + + if (ti->nr_requests > tux_max_connect) + goto out; + +repeat: + for (socknr = 0; socknr < CONFIG_TUX_NUMSOCKETS; socknr++) { + tux_listen_t *tux_listen; + + tux_listen = ti->listen + socknr; + sock = tux_listen->sock; + if (!sock) + break; + if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) + break; + + icsk1 = inet_csk(sock->sk); + /* + * Quick test to see if there are connections on the queue. + * This is cheaper than accept() itself because this saves us + * the allocation of a new socket. (Which doesn't seem to be + * used anyway) + */ + if (!reqsk_queue_empty(&icsk1->icsk_accept_queue)) { + tux_proto_t *proto; + + if (!count++) + __set_task_state(current, TASK_RUNNING); + + new_sock = sock_alloc(); + if (!new_sock) + goto out; + + new_sock->type = sock->type; + new_sock->ops = sock->ops; + + error = sock->ops->accept(sock, new_sock, O_NONBLOCK); + if (error < 0) + goto err; + if (new_sock->sk->sk_state != TCP_ESTABLISHED) + goto err; + + tp2 = tcp_sk(new_sock->sk); + icsk2 = inet_csk(new_sock->sk); + tp2->nonagle = 2; + icsk2->icsk_ack.pingpong = tux_ack_pingpong; + new_sock->sk->sk_reuse = 1; + sock_set_flag(new_sock->sk, SOCK_URGINLINE); + + /* Allocate a request-entry for the connection */ + req = kmalloc_req(ti); + if (!req) + BUG(); + link_tux_socket(req, new_sock); + + proto = req->proto = tux_listen->proto; + + proto->got_request(req); + } + } + if (count != last_count) { + last_count = count; + goto repeat; + } +out: + return count; +err: + sock_release(new_sock); + goto out; +} + Index: latest/net/tux/cachemiss.c =================================================================== --- /dev/null +++ latest/net/tux/cachemiss.c @@ -0,0 +1,265 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * cachemiss.c: handle the 'slow IO path' by queueing not-yet-cached + * requests to the IO-thread pool. Dynamic load balancing is done + * between IO threads, based on the number of requests they have pending. + */ + +#include <net/tux.h> +#include <linux/delay.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +void queue_cachemiss (tux_req_t *req) +{ + iothread_t *iot = req->ti->iot; + + Dprintk("queueing_cachemiss(req:%p) (req->cwd_dentry: %p) at %p:%p.\n", + req, req->cwd_dentry, __builtin_return_address(0), __builtin_return_address(1)); + if (req->idle_input || req->wait_output_space) + TUX_BUG(); + req->had_cachemiss = 1; + if (!list_empty(&req->work)) + TUX_BUG(); + spin_lock(&iot->async_lock); + if (connection_too_fast(req)) + list_add_tail(&req->work, &iot->async_queue); + else + list_add(&req->work, &iot->async_queue); + iot->nr_async_pending++; + INC_STAT(nr_cachemiss_pending); + spin_unlock(&iot->async_lock); + + wake_up(&iot->async_sleep); +} + +static tux_req_t * get_cachemiss (iothread_t *iot) +{ + struct list_head *tmp; + tux_req_t *req = NULL; + + spin_lock(&iot->async_lock); + if (!list_empty(&iot->async_queue)) { + + tmp = iot->async_queue.next; + req = list_entry(tmp, tux_req_t, work); + + Dprintk("get_cachemiss(%p): got req %p.\n", iot, req); + list_del(tmp); + DEBUG_DEL_LIST(tmp); + iot->nr_async_pending--; + DEC_STAT(nr_cachemiss_pending); + + if (req->ti->iot != iot) + TUX_BUG(); + } + spin_unlock(&iot->async_lock); + return req; +} + +struct file * tux_open_file (char *filename, int mode) +{ + struct file *filp; + + if (!filename) + TUX_BUG(); + + /* Rule no. 3 -- Does the file exist ? */ + + filp = filp_open(filename, mode, 0600); + + if (IS_ERR(filp) || !filp || !filp->f_dentry) + goto err; + +out: + return filp; +err: + Dprintk("filp_open() error: %d.\n", (int)filp); + filp = NULL; + goto out; +} + +static int cachemiss_thread (void *data) +{ + tux_req_t *req; + struct k_sigaction *ka; + DECLARE_WAITQUEUE(wait, current); + iothread_t *iot = data; + int nr = iot->ti->cpu, wake_up; + + Dprintk("iot %p/%p got started.\n", iot, current); + drop_permissions(); + + spin_lock(&iot->async_lock); + iot->threads++; + sprintf(current->comm, "async IO %d/%d", nr, iot->threads); + + + spin_lock_irq(¤t->sighand->siglock); + ka = current->sighand->action + SIGCHLD-1; + ka->sa.sa_handler = SIG_IGN; + siginitsetinv(¤t->blocked, sigmask(SIGCHLD)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + spin_unlock(&iot->async_lock); +#ifdef CONFIG_SMP + { + cpumask_t mask; + + if (cpu_isset(nr, cpu_online_map)) { + cpus_clear(mask); + cpu_set(nr, mask); + set_cpus_allowed(current, mask); + } + + } +#endif + + add_wait_queue_exclusive(&iot->async_sleep, &wait); + + for (;;) { + while (!list_empty(&iot->async_queue) && + (req = get_cachemiss(iot))) { + + if (!req->atom_idx) { + add_tux_atom(req, flush_request); + add_req_to_workqueue(req); + continue; + } + tux_schedule_atom(req, 1); + if (signal_pending(current)) + flush_all_signals(); + } + if (signal_pending(current)) + flush_all_signals(); + if (!list_empty(&iot->async_queue)) + continue; + if (iot->shutdown) { + Dprintk("iot %p/%p got shutdown!\n", iot, current); + break; + } + __set_current_state(TASK_INTERRUPTIBLE); + if (list_empty(&iot->async_queue)) { + Dprintk("iot %p/%p going to sleep.\n", iot, current); + schedule(); + Dprintk("iot %p/%p got woken up.\n", iot, current); + } + __set_current_state(TASK_RUNNING); + } + + remove_wait_queue(&iot->async_sleep, &wait); + + wake_up = 0; + spin_lock(&iot->async_lock); + if (!--iot->threads) + wake_up = 1; + spin_unlock(&iot->async_lock); + Dprintk("iot %p/%p has finished shutdown!\n", iot, current); + if (wake_up) { + Dprintk("iot %p/%p waking up master.\n", iot, current); + wake_up(&iot->wait_shutdown); + } + + return 0; +} + +static void __stop_cachemiss_threads (iothread_t *iot) +{ + DECLARE_WAITQUEUE(wait, current); + + __set_current_state(TASK_UNINTERRUPTIBLE); + + Dprintk("stopping async IO threads %p.\n", iot); + add_wait_queue(&iot->wait_shutdown, &wait); + + spin_lock(&iot->async_lock); + if (iot->shutdown) + TUX_BUG(); + if (!iot->threads) + TUX_BUG(); + iot->shutdown = 1; + wake_up_all(&iot->async_sleep); + spin_unlock(&iot->async_lock); + + Dprintk("waiting for async IO threads %p to exit.\n", iot); + schedule(); + remove_wait_queue(&iot->wait_shutdown, &wait); + + if (iot->threads) + TUX_BUG(); + if (iot->nr_async_pending) + TUX_BUG(); + Dprintk("stopped async IO threads %p.\n", iot); +} + +void stop_cachemiss_threads (threadinfo_t *ti) +{ + iothread_t *iot = ti->iot; + + if (!iot) + TUX_BUG(); + if (iot->nr_async_pending) + TUX_BUG(); + __stop_cachemiss_threads(iot); + ti->iot = NULL; + kfree(iot); +} + +int start_cachemiss_threads (threadinfo_t *ti) +{ + int i, pid; + + iothread_t *iot; + + iot = kmalloc(sizeof(*iot), GFP_KERNEL); + if (!iot) + return -ENOMEM; + memset(iot, 0, sizeof(*iot)); + + iot->ti = ti; + spin_lock_init(&iot->async_lock); + iot->nr_async_pending = 0; + INIT_LIST_HEAD(&iot->async_queue); + init_waitqueue_head(&iot->async_sleep); + init_waitqueue_head(&iot->wait_shutdown); + + for (i = 0; i < NR_IO_THREADS; i++) { + pid = kernel_thread(cachemiss_thread, (void *)iot, 0); + if (pid < 0) { + printk(KERN_ERR "TUX: error %d creating IO thread!\n", + pid); + __stop_cachemiss_threads(iot); + kfree(iot); + return pid; + } + } + ti->iot = iot; + /* + * Wait for all cachemiss threads to start up: + */ + while (iot->threads != NR_IO_THREADS) { + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + } + return 0; +} + Index: latest/net/tux/cgi.c =================================================================== --- /dev/null +++ latest/net/tux/cgi.c @@ -0,0 +1,171 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * cgi.c: user-space CGI (and other) code execution. + */ + +#define __KERNEL_SYSCALLS__ +#define __KERNEL_SYSCALLS_NO_ERRNO__ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +static int exec_usermode(char *program_path, char *argv[], char *envp[]) +{ + struct files_struct *files = current->files; + struct fdtable *fdt; + int i, err; + + err = tux_chroot(tux_cgiroot); + if (err) { + printk(KERN_ERR "TUX: CGI chroot returned %d, /proc/sys/net/tux/cgiroot is probably set up incorrectly! Aborting CGI execution.\n", err); + return err; + } + + /* Allow execve args to be in kernel space. */ + set_fs(KERNEL_DS); + + // TODO: is this RCU-safe? + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + spin_unlock(&files->file_lock); + + for (i = 3; i < fdt->max_fds; i++ ) + if (fdt->fd[i]) + tux_close(i); + + err = __exec_usermodehelper(program_path, argv, envp, NULL); + if (err < 0) + return err; + return 0; +} + +static inline long tux_dup(unsigned int fildes) +{ + int ret = -EBADF; + struct file * file = fget(fildes); + + if (file) + ret = dupfd(file, 0); + return ret; +} + +static int exec_helper (void * data) +{ + exec_param_t *param = data; + char **tmp; + int ret; + + sprintf(current->comm,"doexec - %d", current->pid); +#ifdef CONFIG_SMP + if (!tux_cgi_inherit_cpu) { + cpumask_t map; + + cpus_and(map, cpu_online_map, tux_cgi_cpu_mask); + + if (!(cpus_empty(map))) + set_cpus_allowed(current, map); + else + set_cpus_allowed(current, cpu_online_map); + } +#endif + + if (!param) + TUX_BUG(); + Dprintk("doing exec(%s).\n", param->command); + + Dprintk("argv: "); + tmp = param->argv; + while (*tmp) { + Dprintk("{%s} ", *tmp); + tmp++; + } + Dprintk("\n"); + Dprintk("envp: "); + tmp = param->envp; + while (*tmp) { + Dprintk("{%s} ", *tmp); + tmp++; + } + Dprintk("\n"); + /* + * Set up stdin, stdout and stderr of the external + * CGI application. + */ + if (param->pipe_fds) { + struct files_struct *files = current->files; + struct fdtable *fdt; + + tux_close(1); + tux_close(2); + tux_close(4); + if (tux_dup(3) != 1) + TUX_BUG(); + if (tux_dup(5) != 2) + TUX_BUG(); + tux_close(3); + tux_close(5); + // do not close on exec. + spin_lock(&files->file_lock); + fdt = files_fdtable(files); + FD_CLR(0, fdt->close_on_exec); + FD_CLR(1, fdt->close_on_exec); + FD_CLR(2, fdt->close_on_exec); + spin_unlock(&files->file_lock); + } + ret = exec_usermode(param->command, param->argv, param->envp); + if (ret < 0) + Dprintk("bug: exec() returned %d.\n", ret); + else + Dprintk("exec()-ed successfully!\n"); + return 0; +} + +pid_t tux_exec_process (char *command, char **argv, + char **envp, int pipe_fds, + exec_param_t *param, int wait) +{ + exec_param_t param_local; + pid_t pid; + struct k_sigaction *ka; + + ka = current->sighand->action + SIGCHLD-1; + ka->sa.sa_handler = SIG_IGN; + + if (!param && wait) + param = ¶m_local; + + param->command = command; + param->argv = argv; + param->envp = envp; + param->pipe_fds = pipe_fds; + +repeat_fork: + pid = kernel_thread(exec_helper, (void*) param, CLONE_SIGHAND|SIGCHLD); + Dprintk("kernel thread created PID %d.\n", pid); + if (pid < 0) { + printk(KERN_ERR "TUX: could not create new CGI kernel thread due to %d... retrying.\n", pid); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + goto repeat_fork; + } + return pid; +} Index: latest/net/tux/directory.c =================================================================== --- /dev/null +++ latest/net/tux/directory.c @@ -0,0 +1,302 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * directory.c: directory listing support + */ + +#define __KERNEL_SYSCALLS__ +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +char * tux_print_path (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt, char *buf, unsigned int max_len) +{ + char *res; + struct dentry *cwd, *root; + struct vfsmount *cwd_mnt, *rootmnt; + + cwd = dget(dentry); + cwd_mnt = mntget(mnt); + root = dget(req->docroot_dentry); + rootmnt = mntget(req->docroot_mnt); + + spin_lock(&dcache_lock); + res = __d_path(cwd, cwd_mnt, root, rootmnt, buf, max_len); + spin_unlock(&dcache_lock); + + dput(cwd); + mntput(cwd_mnt); + dput(root); + mntput(rootmnt); + + return res; +} + +/* + * There are filesystems that do not fill in ->d_type correctly. + * Determine file-type. + */ +static int get_d_type (struct dentry *dentry) +{ + unsigned int mode = dentry->d_inode->i_mode; + + if (S_ISREG(mode)) + return DT_REG; + if (S_ISDIR(mode)) + return DT_DIR; + if (S_ISLNK(mode)) + return DT_LNK; + if (S_ISFIFO(mode)) + return DT_FIFO; + if (S_ISSOCK(mode)) + return DT_SOCK; + if (S_ISCHR(mode)) + return DT_CHR; + if (S_ISBLK(mode)) + return DT_BLK; + return 0; +} + +static void do_dir_line (tux_req_t *req, int cachemiss) +{ + struct linux_dirent64 *dirp, *dirp0; + char string0[MAX_OBJECTNAME_LEN+200], *tmp; + int len, curroff, total, str_len = 0; + int err, flag = cachemiss ? 0 : LOOKUP_ATOMIC; + struct nameidata base = { }; + struct dentry *dentry = NULL; + struct inode *inode = NULL; + struct vfsmount *mnt = NULL; + + if (req->proto->check_req_err(req, cachemiss)) + return; + + tmp = NULL; + dirp0 = req->dirp0; + curroff = req->curroff; + total = req->total; + + dirp = (struct linux_dirent64 *)((char *)dirp0 + curroff); + if (!dirp->d_name || !dirp->d_name[0]) + goto next_dir; + /* + * Hide .xxxxx files: + */ + if (dirp->d_name[0] == '.') + goto next_dir; + Dprintk("<%s T:%d (off:%Ld) (len:%d)>\n", dirp->d_name, dirp->d_type, dirp->d_off, dirp->d_reclen); + if (tux_hide_unreadable) { + switch (dirp->d_type) { + default: + goto next_dir; + case DT_UNKNOWN: + case DT_REG: + case DT_DIR: + case DT_LNK: + /* valid entries - fall through. */ + ; + } + } + len = strlen(dirp->d_name); + if (len >= MAX_OBJECTNAME_LEN) { + dirp->d_name[MAX_OBJECTNAME_LEN] = 0; + len = MAX_OBJECTNAME_LEN-1; + } + + if (!req->dentry) + TUX_BUG(); + + base.flags = flag; + base.last_type = LAST_ROOT; + base.dentry = dget(req->dentry); + base.mnt = mntget(req->cwd_mnt); + + switch_docroot(req); + err = path_walk(dirp->d_name, &base); + + Dprintk("path_walk() returned %d.\n", err); + + if (err) { + if (err == -EWOULDBLOCKIO) { + add_tux_atom(req, do_dir_line); + queue_cachemiss(req); + return; + } + goto next_dir; + } + + dentry = base.dentry; + mnt = base.mnt; + if (!dentry) + TUX_BUG(); + if (IS_ERR(dentry)) + TUX_BUG(); + inode = dentry->d_inode; + if (!inode) + TUX_BUG(); + if (!dirp->d_type) + dirp->d_type = get_d_type(dentry); + if (tux_hide_unreadable) { + umode_t mode; + + mode = inode->i_mode; + if (mode & tux_mode_forbidden) + goto out_dput; + if (!(mode & tux_mode_allowed)) + goto out_dput; + + err = permission(inode, MAY_READ, NULL); + if (err) + goto out_dput; + if (dirp->d_type == DT_DIR) { + err = permission(inode, MAY_EXEC, NULL); + if (err) + goto out_dput; + } + } + + tmp = req->proto->print_dir_line(req, string0, dirp->d_name, len, dirp->d_type, dentry, inode); + if (tmp) + str_len = tmp-string0; +out_dput: + dput(dentry); + mntput(mnt); +next_dir: + curroff += dirp->d_reclen; + + if (tmp && (tmp != string0)) + Dprintk("writing line (len: %d): <%s>\n", strlen(string0), string0); + + if (curroff < total) { + req->dirp0 = dirp0; + req->curroff = curroff; + add_tux_atom(req, do_dir_line); + } else { + kfree(dirp0); + req->dirp0 = NULL; + req->curroff = 0; + // falls back to the list_directory atom + } + if (tmp && (tmp != string0)) + __send_async_message(req, string0, 200, str_len, 0); + else + add_req_to_workqueue(req); +} + +#define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) +#define ROUND_UP(x) (((x)+sizeof(long)-1) & ~(sizeof(long)-1)) +#define ROUND_UP64(x) (((x)+sizeof(u64)-1) & ~(sizeof(u64)-1)) + +static int filldir64(void * __buf, const char * name, int namlen, loff_t offset, + u64 ino, unsigned int d_type) +{ + struct linux_dirent64 * dirent, d; + struct getdents_callback64 * buf = (struct getdents_callback64 *) __buf; + int reclen = ROUND_UP64(NAME_OFFSET(dirent) + namlen + 1); + int err; + + buf->error = -EINVAL; /* only used if we fail.. */ + if (reclen > buf->count) + return -EINVAL; + dirent = buf->previous; + if (dirent) { + d.d_off = offset; + err = copy_to_user(&dirent->d_off, &d.d_off, sizeof(d.d_off)); + BUG_ON(err); + } + dirent = buf->current_dir; + buf->previous = dirent; + memset(&d, 0, NAME_OFFSET(&d)); + d.d_ino = ino; + d.d_reclen = reclen; + d.d_type = d_type; + err = copy_to_user(dirent, &d, NAME_OFFSET(&d)); + BUG_ON(err); + err = copy_to_user(dirent->d_name, name, namlen); + BUG_ON(err); + err = put_user(0, dirent->d_name + namlen); + BUG_ON(err); + dirent = (void *)dirent + reclen; + buf->current_dir = dirent; + buf->count -= reclen; + return 0; +} +#define DIRENT_SIZE 3000 + +void list_directory (tux_req_t *req, int cachemiss) +{ + struct getdents_callback64 buf; + struct linux_dirent64 *dirp0; + mm_segment_t oldmm; + int total; + + Dprintk("list_directory(%p, %d), dentry: %p.\n", req, cachemiss, req->dentry); + if (!req->cwd_dentry) + TUX_BUG(); + + if (!cachemiss) { + add_tux_atom(req, list_directory); + queue_cachemiss(req); + return; + } + + dirp0 = tux_kmalloc(DIRENT_SIZE); + + buf.current_dir = dirp0; + buf.previous = NULL; + buf.count = DIRENT_SIZE; + buf.error = 0; + + oldmm = get_fs(); set_fs(KERNEL_DS); + set_fs(KERNEL_DS); + total = vfs_readdir(req->in_file, filldir64, &buf); + set_fs(oldmm); + + if (buf.previous) + total = DIRENT_SIZE - buf.count; + + Dprintk("total: %d (buf.error: %d, buf.previous %p)\n", + total, buf.error, buf.previous); + + if (total < 0) { + kfree(dirp0); + req_err(req); + add_req_to_workqueue(req); + return; + } + if (!total) { + kfree(dirp0); + req->in_file->f_pos = 0; + add_req_to_workqueue(req); + return; + } + + if (!req->cwd_dentry) + TUX_BUG(); + add_tux_atom(req, list_directory); + + req->dirp0 = dirp0; + req->curroff = 0; + req->total = total; + add_tux_atom(req, do_dir_line); + + add_req_to_workqueue(req); +} + Index: latest/net/tux/extcgi.c =================================================================== --- /dev/null +++ latest/net/tux/extcgi.c @@ -0,0 +1,329 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * extcgi.c: dynamic TUX module which forks and starts an external CGI + */ + +#define __KERNEL_SYSCALLS__ +#define __KERNEL_SYSCALLS_NO_ERRNO__ + +#include <net/tux.h> +#include "parser.h" + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +#define MAX_ENVLEN 1000 +#define MAX_CGI_METAVARIABLES 32 +#define CGI_CHUNK_SIZE 1024 +#define MAX_CGI_COMMAND_LEN 256 + +#ifdef CONFIG_TUX_DEBUG +#define PRINT_MESSAGE_LEFT \ + Dprintk("CGI message left at %s:%d:\n--->{%s}<---\n", \ + __FILE__, __LINE__, curr) +#else +#define PRINT_MESSAGE_LEFT do {} while(0) +#endif + +#define GOTO_INCOMPLETE do { Dprintk("invalid CGI reply at %s:%d.\n", __FILE__, __LINE__); goto invalid; } while (0) + +/* + * Please acknowledge our hard work by not changing this define, or + * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in + * the ID string. Thanks! :-) + */ +#define CGI_SUCCESS2 "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: TUX/2.0 (Linux)\r\n" + +static int handle_cgi_reply (tux_req_t *req) +{ + int first = 1; + int len, left, total; + char *buf, *tmp; + mm_segment_t oldmm; + + buf = tux_kmalloc(CGI_CHUNK_SIZE+1); + tux_close(3); + tux_close(4); + tux_close(5); + oldmm = get_fs(); set_fs(KERNEL_DS); + send_sync_buf(NULL, req->sock, CGI_SUCCESS2, sizeof(CGI_SUCCESS2)-1, MSG_MORE); + set_fs(oldmm); + + req->bytes_sent = 0; + /* + * The new process is the new owner of the socket, it will + * close it. + */ +repeat: + left = CGI_CHUNK_SIZE; + len = 0; + total = 0; + tmp = buf; + do { + mm_segment_t oldmm; + + tmp += len; + total += len; + left -= len; + if (!left) + break; +repeat_read: + Dprintk("reading %d bytes via read().\n", left); + oldmm = get_fs(); set_fs(KERNEL_DS); + len = sys_read(2, tmp, left); + set_fs(oldmm); + Dprintk("got %d bytes from read() (total: %d).\n", len, total); + if (len > 0) + tmp[len] = 0; + Dprintk("CGI reply: (%d bytes, total %d).\n", len, total); + if (len == -ERESTARTSYS) { + flush_all_signals(); + goto repeat_read; + } + } while (len > 0); + if (total > CGI_CHUNK_SIZE) { + printk(KERN_ERR "TUX: CGI weirdness. total: %d, len: %d, left: %d.\n", total, len, left); + TUX_BUG(); + } + Dprintk("CGI done reply chunk: (%d bytes last, total %d).\n", len, total); + if (total) { + mm_segment_t oldmm; + + oldmm = get_fs(); set_fs(KERNEL_DS); + if (!len) + send_sync_buf(NULL, req->sock, buf, total, 0); + else + send_sync_buf(NULL, req->sock, buf, total, MSG_MORE); + set_fs(oldmm); + req->bytes_sent += total; + } + + Dprintk("bytes_sent: %d\n", req->bytes_sent); + if ((total > 0) && first) { + first = 0; + + if (buf[total]) + TUX_BUG(); + tmp = strstr(buf, "\n\n"); + if (tmp) { + req->bytes_sent -= (tmp-buf) + 2; + Dprintk("new bytes_sent: %d\n", req->bytes_sent); + } else { + req->bytes_sent = 0; + req_err(req); + } + } + if (len < 0) + Dprintk("sys_read returned with %d.\n", len); + else { + if (total > 0) + goto repeat; + } + tux_close(2); + + req->status = 200; + add_req_to_workqueue(req); + kfree(buf); + + return -1; +} + +static int exec_external_cgi (void *data) +{ + exec_param_t param; + tux_req_t *req = data; + char *envp[MAX_CGI_METAVARIABLES+1], **envp_p; + char *argv[] = { "extcgi", NULL}; + char *envstr, *tmp; + unsigned int host; + struct k_sigaction *ka; + int in_pipe_fds[2], out_pipe_fds[2], err_pipe_fds[2], len, err; + char *command; + pid_t pid; + + len = strlen(tux_common_docroot); + if (req->objectname_len + len + 12 > MAX_CGI_COMMAND_LEN) + return -ENOMEM; + sprintf(current->comm,"cgimain - %d", current->pid); + host = inet_sk(req->sock->sk)->daddr; + + envstr = tux_kmalloc(MAX_ENVLEN); + command = tux_kmalloc(MAX_CGI_COMMAND_LEN); + + tmp = envstr; + envp_p = envp; + +#define WRITE_ENV(str...) \ + if (envp_p >= envp + MAX_CGI_METAVARIABLES) \ + TUX_BUG(); \ + len = sprintf(tmp, str); \ + *envp_p++ = tmp; \ + tmp += len + 1; \ + if (tmp >= envstr + MAX_ENVLEN) \ + TUX_BUG(); + + #define WRITE_ENV_STR(str,field,len) \ + do { \ + int offset; \ + \ + offset = sizeof(str)-1; \ + err = -EFAULT; \ + if (tmp - envstr + offset + len >= MAX_ENVLEN) \ + goto out; \ + if (envp_p >= envp + MAX_CGI_METAVARIABLES) \ + TUX_BUG(); \ + memcpy(tmp, str, offset); \ + memcpy(tmp + offset, field, len); \ + offset += len; \ + tmp[offset] = 0; \ + *envp_p++ = tmp; \ + tmp += offset + 1; \ + } while (0) + + WRITE_ENV("GATEWAY_INTERFACE=CGI/1.1"); + WRITE_ENV("CONTENT_LENGTH=%d", req->post_data_len); + WRITE_ENV("REMOTE_ADDR=%d.%d.%d.%d", NIPQUAD(host)); + WRITE_ENV("SERVER_PORT=%d", 80); + WRITE_ENV("SERVER_SOFTWARE=TUX/2.0 (Linux)"); + +#if 1 + WRITE_ENV("DOCUMENT_ROOT=/"); + WRITE_ENV("PATH_INFO=/"); +#else + WRITE_ENV_STR("DOCUMENT_ROOT=", tux_common_docroot, len); + WRITE_ENV_STR("PATH_INFO=", tux_common_docroot, len); +#endif + WRITE_ENV_STR("QUERY_STRING=", req->query_str, req->query_len); + WRITE_ENV_STR("REQUEST_METHOD=", req->method_str, req->method_len); + WRITE_ENV_STR("SCRIPT_NAME=", req->objectname, req->objectname_len); + WRITE_ENV_STR("SERVER_PROTOCOL=", req->version_str, req->version_len); + + if (req->content_type_len) + WRITE_ENV_STR("CONTENT_TYPE=", + req->content_type_str, req->content_type_len); + if (req->cookies_len) + WRITE_ENV_STR("HTTP_COOKIE=", + req->cookies_str, req->cookies_len); + + if (req->host_len) + WRITE_ENV_STR("SERVER_NAME=", req->host, req->host_len); + else { + const char *host = "localhost"; + WRITE_ENV_STR("SERVER_NAME=", host, strlen(host)); + } + + *envp_p = NULL; + + spin_lock_irq(¤t->sighand->siglock); + ka = current->sighand->action + SIGPIPE-1; + ka->sa.sa_handler = SIG_IGN; + siginitsetinv(¤t->blocked, sigmask(SIGCHLD)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + tux_close(0); tux_close(1); + tux_close(2); tux_close(3); + tux_close(4); tux_close(5); + + in_pipe_fds[0] = in_pipe_fds[1] = -1; + out_pipe_fds[0] = out_pipe_fds[1] = -1; + err_pipe_fds[0] = err_pipe_fds[1] = -1; + + err = -ENFILE; + if (do_pipe(in_pipe_fds)) + goto out; + if (do_pipe(out_pipe_fds)) + goto out; + if (do_pipe(err_pipe_fds)) + goto out; + + if (in_pipe_fds[0] != 0) TUX_BUG(); + if (in_pipe_fds[1] != 1) TUX_BUG(); + if (out_pipe_fds[0] != 2) TUX_BUG(); + if (out_pipe_fds[1] != 3) TUX_BUG(); + if (err_pipe_fds[0] != 4) TUX_BUG(); + if (err_pipe_fds[1] != 5) TUX_BUG(); + + if (req->virtual && req->host_len) + sprintf(command, "/%s/cgi-bin/%s", req->host, req->objectname); + else + sprintf(command, "/cgi-bin/%s", req->objectname); + Dprintk("before CGI exec.\n"); + pid = tux_exec_process(command, argv, envp, 1, ¶m, 0); + Dprintk("after CGI exec.\n"); + + if (req->post_data_len) { + mm_segment_t oldmm; + int ret; + + Dprintk("POST data to CGI:\n"); + oldmm = get_fs(); set_fs(KERNEL_DS); + ret = sys_write(1, req->post_data_str, req->post_data_len); + set_fs(oldmm); + Dprintk("write() returned: %d.\n", ret); + if (ret != req->post_data_len) + Dprintk("write() returned: %d.\n", ret); + } + + tux_close(0); + tux_close(1); + + handle_cgi_reply(req); + err = 0; + +out: + kfree(envstr); + kfree(command); + + return err; +} + +void start_external_cgi (tux_req_t *req) +{ + int pid; + +repeat: + pid = kernel_thread(exec_external_cgi, (void*) req, SIGCHLD); + if (pid == -1) + return; + if (pid < 0) { + printk(KERN_INFO "TUX: Could not fork external CGI process due to %d, retrying!\n", pid); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ); + goto repeat; + } +} + +int query_extcgi (tux_req_t *req) +{ + clear_keepalive(req); + start_external_cgi(req); + return -1; +} + +#define EXTCGI_INVALID_HEADER \ + "HTTP/1.1 503 Service Unavailable\r\n" \ + "Content-Length: 23\r\n\r\n" + +#define EXTCGI_INVALID_BODY \ + "TUX: invalid CGI reply." + +#define EXTCGI_INVALID EXTCGI_INVALID_HEADER EXTCGI_INVALID_BODY + Index: latest/net/tux/gzip.c =================================================================== --- /dev/null +++ latest/net/tux/gzip.c @@ -0,0 +1,40 @@ +/* $Id: zlib.h,v 1.2 1997/12/23 10:47:44 paulus Exp $ */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/zlib.h> +#include <net/tux.h> + +#define STREAM_END_SPACE 12 + +int tux_gzip_compress (tux_req_t *req, unsigned char *data_in, unsigned char *data_out, __u32 *in_len, __u32 *out_len) +{ + z_stream *s = &req->ti->gzip_state; + int ret, left; + + down(&req->ti->gzip_sem); + if (zlib_deflateReset(s) != Z_OK) + BUG(); + + s->next_in = data_in; + s->next_out = data_out; + s->avail_in = *in_len; + s->avail_out = *out_len; + + Dprintk("calling zlib_deflate with avail_in %d, avail_out %d\n", s->avail_in, s->avail_out); + ret = zlib_deflate(s, Z_FINISH); + Dprintk("deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", s->avail_in, s->avail_out, s->total_in, s->total_out); + + if (ret != Z_STREAM_END) { + printk("bad: deflate returned with %d! avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", ret, s->avail_in, s->avail_out, s->total_in, s->total_out); + BUG(); + } + *in_len = s->avail_in; + *out_len = s->avail_out; + left = s->avail_in; + + up(&req->ti->gzip_sem); + + return left; +} + Index: latest/net/tux/input.c =================================================================== --- /dev/null +++ latest/net/tux/input.c @@ -0,0 +1,641 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * input.c: handle requests arriving on accepted connections + */ + +#include <net/tux.h> +#include <linux/kmod.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +void zap_request (tux_req_t *req, int cachemiss) +{ + if (!req->error) + TUX_BUG(); + if (req->error == TUX_ERROR_CONN_TIMEOUT) { + if (req->proto->request_timeout) { + clear_keepalive(req); + req->proto->request_timeout(req, cachemiss); + } else { + clear_keepalive(req); + if (!cachemiss) + flush_request(req, 0); + else { + add_tux_atom(req, flush_request); + add_req_to_workqueue(req); + } + } + return; + } + + if (!cachemiss && (req->error == TUX_ERROR_CONN_CLOSE)) { + /* + * Zap connection as fast as possible, there is + * no valid client connection anymore: + */ + clear_keepalive(req); + flush_request(req, 0); + } else { + if (req->error == TUX_ERROR_CONN_CLOSE) { + clear_keepalive(req); + add_tux_atom(req, flush_request); + } else + /* + * Potentially redirect to the secondary server: + */ + add_tux_atom(req, redirect_request); + add_req_to_workqueue(req); + } +} + +void __switch_docroot(tux_req_t *req) +{ + if (!req->docroot_dentry || !req->docroot_mnt) + TUX_BUG(); + set_fs_root(current->fs, req->docroot_mnt, req->docroot_dentry); +} + +struct dentry * __tux_lookup (tux_req_t *req, const char *filename, + struct nameidata *base, struct vfsmount **mnt) +{ + int err; + + err = path_walk(filename, base); + if (err) { + Dprintk("path_walk() returned with %d!\n", err); + return ERR_PTR(err); + } + if (*mnt) + TUX_BUG(); + *mnt = base->mnt; + + return base->dentry; +} + +int tux_permission (struct inode *inode) +{ + umode_t mode; + int err; + + mode = inode->i_mode; + Dprintk("URL inode mode: %08x.\n", mode); + + if (mode & tux_mode_forbidden) + return -2; + /* + * at least one bit in the 'allowed' set has to + * be present to allow access. + */ + if (!(mode & tux_mode_allowed)) + return -3; + err = permission(inode,MAY_READ,NULL); + return err; +} + +struct dentry * tux_lookup (tux_req_t *req, const char *filename, + const unsigned int flag, struct vfsmount **mnt) +{ + struct dentry *dentry; + struct nameidata base = { }; + + Dprintk("tux_lookup(%p, %s, %d, virtual: %d, host: %s (%d).)\n", req, filename, flag, req->virtual, req->host, req->host_len); + + base.flags = LOOKUP_FOLLOW|flag; + base.last_type = LAST_ROOT; + if (req->objectname[0] == '/') { + base.dentry = dget(req->docroot_dentry); + base.mnt = mntget(req->docroot_mnt); + } else { + if (!req->cwd_dentry) { + req->cwd_dentry = dget(req->docroot_dentry); + req->cwd_mnt = mntget(req->docroot_mnt); + } + base.dentry = req->cwd_dentry; + dget(base.dentry); + base.mnt = mntget(req->cwd_mnt); + } + + switch_docroot(req); + dentry = __tux_lookup (req, filename, &base, mnt); + + Dprintk("looked up {%s} == dentry %p.\n", filename, dentry); + + if (dentry && !IS_ERR(dentry) && !dentry->d_inode) + TUX_BUG(); + return dentry; +} + +int lookup_object (tux_req_t *req, const unsigned int flag) +{ + struct vfsmount *mnt = NULL; + struct dentry *dentry = NULL; + int perm; + + dentry = tux_lookup(req, req->objectname, flag, &mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) + goto cachemiss; + goto abort; + } + perm = tux_permission(dentry->d_inode); + /* + * Only regular files allowed. + */ + if ((perm < 0) || !S_ISREG(dentry->d_inode->i_mode)) { + req->status = 403; + goto abort; + } + req->total_file_len = dentry->d_inode->i_size; +out: + install_req_dentry(req, dentry, mnt); + return 0; +cachemiss: + return 1; +abort: + if (dentry) { + if (!IS_ERR(dentry)) + dput(dentry); + dentry = NULL; + } + if (mnt) { + if (!IS_ERR(mnt)) + mntput(mnt); + mnt = NULL; + } + req_err(req); + goto out; +} + +void install_req_dentry (tux_req_t *req, struct dentry *dentry, struct vfsmount *mnt) +{ + if (req->dentry) + TUX_BUG(); + req->dentry = dentry; + if (req->mnt) + TUX_BUG(); + req->mnt = mnt; + if (req->in_file && req->in_file->f_dentry) + TUX_BUG(); + if (dentry) + req->in_file = dentry_open(dget(dentry), NULL, O_RDONLY); +} + +void release_req_dentry (tux_req_t *req) +{ + if (!req->dentry) { + if (req->in_file && req->in_file->f_dentry) + TUX_BUG(); + return; + } + + fput(req->in_file); + req->in_file = NULL; + dput(req->dentry); + req->dentry = NULL; + mntput(req->mnt); + req->mnt = NULL; +} + +int __connection_too_fast (tux_req_t *req) +{ + unsigned long curr_bw, delta, bytes; + + bytes = req->total_bytes + req->bytes_sent; + if (!bytes) + return 1; + + delta = jiffies - req->first_timestamp; + if (!delta) + delta++; + curr_bw = bytes * HZ / delta; + + if (curr_bw > tux_max_output_bandwidth) + return 2; + return 0; +} + +void unidle_req (tux_req_t *req) +{ + threadinfo_t *ti = req->ti; + + Dprintk("UNIDLE req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status); + spin_lock_irq(&ti->work_lock); + if (req->magic != TUX_MAGIC) + TUX_BUG(); + if (!test_and_clear_bit(0, &req->idle_input)) { + Dprintk("unidling %p, wasnt idle!\n", req); + if (list_empty(&req->work)) + TUX_BUG(); + list_del(&req->work); + DEBUG_DEL_LIST(&req->work); + DEC_STAT(nr_work_pending); + } else { + del_keepalive_timer(req); + DEC_STAT(nr_idle_input_pending); + Dprintk("unidled %p.\n", req); + } + if (req->idle_input) + TUX_BUG(); + spin_unlock_irq(&ti->work_lock); +} + +#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete; } while (0) +#define GOTO_REDIRECT do { TDprintk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect; } while (0) +#define GOTO_REDIRECT_NONIDLE do { TDprintk("redirect at %s:%d.\n", __FILE__, __LINE__); goto redirect_nonidle; } while (0) + +static int read_request (struct socket *sock, char *buf, int max_size) +{ + mm_segment_t oldmm; + struct kiocb iocb; + struct msghdr msg; + struct iovec iov; + + int len; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + msg.msg_iov->iov_base = buf; + msg.msg_iov->iov_len = max_size; + + oldmm = get_fs(); set_fs(KERNEL_DS); + +read_again: + init_sync_kiocb(&iocb, NULL); + len = sock->sk->sk_prot->recvmsg(&iocb, sock->sk, &msg, max_size, + MSG_DONTWAIT, MSG_PEEK, NULL); + if (-EIOCBQUEUED == len) + len = wait_on_sync_kiocb(&iocb); + + /* + * We must not get a signal inbetween + */ + if ((len == -EAGAIN) || (len == -ERESTARTSYS)) { + if (!signal_pending(current)) { + len = 0; + goto out; + } + flush_all_signals(); + goto read_again; + } +out: + set_fs(oldmm); + return len; +} + +/* + * We inline URG data so it's at the head of the normal receive queue. + */ +static int zap_urg_data (struct socket *sock) +{ + mm_segment_t oldmm; + struct msghdr msg; + struct iovec iov; + struct kiocb iocb; + int len; + char buf[10]; + + oldmm = get_fs(); set_fs(KERNEL_DS); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = 0; + + msg.msg_iov->iov_base = buf; + msg.msg_iov->iov_len = 2; + +read_again: + init_sync_kiocb(&iocb, NULL); + len = sock->sk->sk_prot->recvmsg(&iocb, sock->sk, &msg, 2, + MSG_DONTWAIT, 0, NULL); + if (-EIOCBQUEUED == len) + len = wait_on_sync_kiocb(&iocb); + Dprintk("recvmsg(MSG_OOB) returned %d.\n", len); + + /* + * We must not get a signal inbetween + */ + if ((len == -EAGAIN) || (len == -ERESTARTSYS)) { + if (!signal_pending(current)) { + len = 0; + goto out; + } + flush_all_signals(); + goto read_again; + } +out: + set_fs(oldmm); + + Dprintk("in out:.. and will return %d.!\n", len); + + return len; +} + +void trunc_headers (tux_req_t *req) +{ + struct sock *sk = req->sock->sk; + int len, addr_len = 0; + struct kiocb iocb; + + if (!req->parsed_len) + TUX_BUG(); +repeat_trunc: + init_sync_kiocb(&iocb, NULL); + len = sk->sk_prot->recvmsg(&iocb, sk, NULL, req->parsed_len, 1, MSG_TRUNC, &addr_len); + if (-EIOCBQUEUED == len) + len = wait_on_sync_kiocb(&iocb); + if ((len == -ERESTARTSYS) || (len == -EAGAIN)) { + flush_all_signals(); + goto repeat_trunc; + } + Dprintk("truncated (TRUNC) %d bytes at %p. (wanted: %d.)\n", len, __builtin_return_address(0), req->parsed_len); + + + + req->parsed_len = 0; +} + +void print_req (tux_req_t *req) +{ + struct sock *sk; + + printk("PRINT req %p <%p>, sock %p\n", + req, __builtin_return_address(0), req->sock); + printk("... idx: %d\n", req->atom_idx); + if (req->sock) { + sk = req->sock->sk; + printk("... sock %p, sk %p, sk->state: %d, sk->err: %d\n", req->sock, sk, sk->sk_state, sk->sk_err); + printk("... write_queue: %d, receive_queue: %d, error_queue: %d, keepalive: %d, status: %d\n", !skb_queue_empty(&sk->sk_write_queue), !skb_queue_empty(&sk->sk_receive_queue), !skb_queue_empty(&sk->sk_error_queue), req->keep_alive, req->status); + printk("...tp->send_head: %p\n", sk->sk_send_head); + printk("...tp->snd_una: %08x\n", tcp_sk(sk)->snd_una); + printk("...tp->snd_nxt: %08x\n", tcp_sk(sk)->snd_nxt); + printk("...tp->packets_out: %08x\n", tcp_sk(sk)->packets_out); + } + printk("... meth:{%s}, uri:{%s}, query:{%s}, ver:{%s}\n", req->method_str ? req->method_str : "<null>", req->uri_str ? req->uri_str : "<null>", req->query_str ? req->query_str : "<null>", req->version_str ? req->version_str : "<null>"); + printk("... post_data:{%s}(%d).\n", req->post_data_str, req->post_data_len); + printk("... headers: {%s}\n", req->headers); +} +/* + * parse_request() reads all available TCP/IP data and prepares + * the request if the TUX request is complete. (we can get TUX + * requests in several packets.) Invalid requests are redirected + * to the secondary server. + */ + +void parse_request (tux_req_t *req, int cachemiss) +{ + int len, parsed_len; + struct sock *sk = req->sock->sk; + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); + int was_keepalive = req->keep_alive; + + if (req->magic != TUX_MAGIC) + TUX_BUG(); + + SET_TIMESTAMP(req->parse_timestamp); + + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + INC_STAT(nr_idle_input_pending); + spin_unlock_irq(&req->ti->work_lock); + + Dprintk("idled request %p.\n", req); + +restart: + + if (tp->urg_data && !(tp->urg_data & TCP_URG_READ)) { + len = zap_urg_data(req->sock); + if (tp->urg_data && !(tp->urg_data & TCP_URG_READ)) { + req->error = TUX_ERROR_CONN_CLOSE; + goto redirect_error; + } + } + + INC_STAT(input_slowpath); + + if (!req->headers) + req->headers = tux_kmalloc(tux_max_header_len); + + /* First, read the data */ + len = read_request(req->sock, (char *)req->headers, tux_max_header_len-1); + if (len < 0) { + req->error = TUX_ERROR_CONN_CLOSE; + goto redirect_error; + } + if (!len) + GOTO_INCOMPLETE; + + /* + * Make it a zero-delimited string to automatically get + * protection against various buffer overflow situations. + * Then pass it to the TUX application protocol stack. + */ + ((char *)req->headers)[len] = 0; + req->headers_len = len; + + parsed_len = req->proto->parse_message(req, len); + + /* + * Is the request fully read? (or is there any error) + */ + if (parsed_len < 0) + GOTO_REDIRECT; + if (!parsed_len) { + /* + * Push pending ACK which was delayed due to the + * pingpong optimization: + */ + if (was_keepalive) { + lock_sock(sk); + icsk->icsk_ack.pingpong = 0; + icsk->icsk_ack.pending |= ICSK_ACK_PUSHED; + tcp_cleanup_rbuf(sk, 1); + release_sock(sk); + } + if (len >= tux_max_header_len-1) + GOTO_REDIRECT; + GOTO_INCOMPLETE; + } + unidle_req(req); + + tp->nonagle = 2; + + add_req_to_workqueue(req); + return; + +redirect: + TDprintk("req %p will be redirected!\n", req); + req_err(req); + +redirect_error: + unidle_req(req); + + if (len < 0) + req->parsed_len = 0; + else + req->parsed_len = len; + + INC_STAT(parse_static_redirect); + if (req->headers) + kfree(req->headers); + req->headers = NULL; + if (req->error) + zap_request(req, cachemiss); + return; + +incomplete: + if (req->error) + goto redirect_error; + if (tp->urg_data && !(tp->urg_data & TCP_URG_READ)) + goto restart; + + add_tux_atom(req, parse_request); + INC_STAT(parse_static_incomplete); + tux_push_req(req); +} + +int process_requests (threadinfo_t *ti, tux_req_t **user_req) +{ + struct list_head *head, *curr; + int count = 0; + tux_req_t *req; + + *user_req = NULL; + +restart_loop: + spin_lock_irq(&ti->work_lock); + head = &ti->work_pending; + curr = head->next; + + if (curr != head) { + int i; + + req = list_entry(curr, tux_req_t, work); + Dprintk("PROCESS req %p <%p>.\n", + req, __builtin_return_address(0)); + for (i = 0; i < req->atom_idx; i++) + Dprintk("... atom %d: %p\n", i, req->atoms[i]); + + if (req->ti != ti) + TUX_BUG(); + if (req->magic != TUX_MAGIC) + TUX_BUG(); + + if (list_empty(&req->work)) + TUX_BUG(); + list_del(curr); + DEBUG_DEL_LIST(&req->work); + spin_unlock_irq(&ti->work_lock); + + if (!req->atom_idx) { + if (req->usermode) { + *user_req = req; + return count; + } + /* + * idx == 0 requests are flushed automatically. + */ + flush_request(req, 0); + } else + tux_schedule_atom(req, 0); + count++; + goto restart_loop; + } + spin_unlock_irq(&ti->work_lock); + + return count; +} + +int tux_flush_workqueue (threadinfo_t *ti) +{ + struct list_head *head, *curr, *next; + tux_req_t *req; + int count = 0; + +restart: + spin_lock_irq(&ti->work_lock); + head = &ti->work_pending; + curr = head->next; + + if (curr != head) { + req = list_entry(curr, tux_req_t, work); + next = curr->next; + clear_bit(0, &req->idle_input); + clear_bit(0, &req->wait_output_space); + if (list_empty(&req->work)) + TUX_BUG(); + list_del(curr); + DEBUG_DEL_LIST(curr); + DEC_STAT(nr_input_pending); + spin_unlock_irq(&ti->work_lock); +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + req->in_file->f_pos = 0; + req->atom_idx = 0; + clear_keepalive(req); + req->status = -1; + if (req->usermode) { + req->usermode = 0; + req->private = 0; + } + flush_request(req, 0); + count++; + goto restart; + } + spin_unlock_irq(&ti->work_lock); + + return count; +} + +int print_all_requests (threadinfo_t *ti) +{ + struct list_head *head, *curr; + tux_req_t *req; + int count = 0; + + spin_lock_irq(&ti->work_lock); + head = &ti->all_requests; + curr = head->next; + + while (curr != head) { + req = list_entry(curr, tux_req_t, all); + curr = curr->next; + print_req(req); + count++; + } + spin_unlock_irq(&ti->work_lock); + + return count; +} + Index: latest/net/tux/Kconfig =================================================================== --- /dev/null +++ latest/net/tux/Kconfig @@ -0,0 +1,25 @@ + +config TUX + tristate "TUX: Threaded linUX application protocol accelerator layer" + default y if INET=y + select ZLIB_DEFLATE + help + This is the TUX content-accelerator/server + +menu "TUX options" + depends on TUX + +config TUX_EXTCGI + bool "External CGI module" + default y + +config TUX_EXTENDED_LOG + bool "extended TUX logging format" + default n + +config TUX_DEBUG + bool "debug TUX" + default n + +endmenu + Index: latest/net/tux/logger.c =================================================================== --- /dev/null +++ latest/net/tux/logger.c @@ -0,0 +1,841 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * Cleaned up logger output for Alpha. + * -- Phil Ezolt (Phillip.Ezolt@compaq.com) & Bill Carr (wcarr92@yahoo.com) + * + * logger.c: log requests finished by TUX. + */ + +#define __KERNEL_SYSCALLS__ +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +static DEFINE_SPINLOCK(log_lock); +static unsigned int log_head, log_tail; +static char * log_buffer = NULL; +static DECLARE_WAIT_QUEUE_HEAD(log_wait); +static DECLARE_WAIT_QUEUE_HEAD(log_full); +static int logger_pid = 0; + +/* + * High-speed TUX logging architecture: + * + * All fast threads share a common log-ringbuffer. (default size 1MB) + * Log entries are binary and are padded to be cacheline aligned, this + * ensures that there is no cache-pingpong between fast threads. + * + * The logger thread writes out pending log entries within 1 second + * (buffer-cache writes data out within 5 seconds). The logger thread + * gets activated once we have more than 25% of the log ringbuffer + * filled - or the 1 second log timeout expires. Fast threads block + * if if more than 95% of the ringbuffer is filled and unblock only + * if used logbuffer space drops below 90%. + * + * This architecture guarantees that 1) logging is reliable (no + * log entry is ever lost), 2) timely (touches disk within 6 seconds), + * 3) in the log-contention case the saturation behavior is still + * write-clustered, but 4) if the logger thread can keep up then + * the coupling is completely asynchron and parallel. + * + * The binary log format gives us about 50% saved IO/memory bandwith + * and 50% less on-disk used log space than the traditional W3C ASCII + * format. + * + * (We might switch to raw IO though to write the logfile.) + */ + +#define SOFT_LIMIT (LOG_LEN*25/100) +#define HARD_LIMIT (LOG_LEN*95/100) +#define HARD_RELAX_LIMIT (LOG_LEN*90/100) + +unsigned int tux_logentry_align_order = 5; + +#if SMP_CACHE_BYTES == 8 +# define TUX_LOGENTRY_ALIGN 3 +#else +#if SMP_CACHE_BYTES == 16 +# define TUX_LOGENTRY_ALIGN 4 +#else +#if SMP_CACHE_BYTES == 32 +# define TUX_LOGENTRY_ALIGN 5 +#else +#if SMP_CACHE_BYTES == 64 +# define TUX_LOGENTRY_ALIGN 6 +#else +#if SMP_CACHE_BYTES == 128 +# define TUX_LOGENTRY_ALIGN 7 +#else +#if SMP_CACHE_BYTES == 256 +# define TUX_LOGENTRY_ALIGN 8 +#else +#error Add entry! +#endif +#endif +#endif +#endif +#endif +#endif + +#define ROUND_UP(x) (((((x)-1) >> TUX_LOGENTRY_ALIGN) + 1) \ + << TUX_LOGENTRY_ALIGN) + +static void __throttle_logging (void) +{ + DECLARE_WAITQUEUE(wait, current); + int pending; + + add_wait_queue(&log_full, &wait); + for (;;) { + static unsigned long last_warning = 0; + + if (jiffies - last_warning > 10*HZ) { + last_warning = jiffies; + printk(KERN_NOTICE "TUX: log buffer overflow, have to throttle TUX thread!\n"); + } + + current->state = TASK_INTERRUPTIBLE; + + spin_lock(&log_lock); + pending = log_head-log_tail; + spin_unlock(&log_lock); + + if ((pending % LOG_LEN) < HARD_LIMIT) + break; + + schedule(); + } + current->state = TASK_RUNNING; + remove_wait_queue(&log_full, &wait); +} + +#ifdef CONFIG_TUX_DEBUG +#define CHECK_LOGPTR(ptr) \ +do { \ + if ((ptr < log_buffer) || (ptr > log_buffer + LOG_LEN)) { \ + printk(KERN_ERR "TUX: ouch: log ptr %p > %p + %ld!\n", \ + ptr, log_buffer, LOG_LEN); \ + TUX_BUG(); \ + } \ +} while (0) +#else +#define CHECK_LOGPTR(ptr) do { } while (0) +#endif + +void __log_request (tux_req_t *req) +{ + char *str, *next; + const char *uri_str; + unsigned int inc, len, uri_len, pending, next_head, def_vhost_len = 0; + unsigned long flags; + + if (req->proto->pre_log) + req->proto->pre_log(req); + /* + * Log the reply status (success, or type of failure) + */ + if (!tux_log_incomplete && (!req->status || (req->bytes_sent == -1))) { + + Dprintk("not logging req %p: {%s} [%d/%d]\n", req, req->uri_str, req->status, req->bytes_sent); + return; + } + Dprintk("uri: {%s} [%d]\n", req->uri_str, req->uri_len); + +#define NO_URI "<none>" + if (req->uri_len) { + uri_len = req->uri_len; + uri_str = req->uri_str; + } else { + uri_str = NO_URI; + uri_len = sizeof(NO_URI)-1; + } + len = uri_len + 1; + + if (req->virtual) { + if (req->host_len) + len += req->host_len; + else { + def_vhost_len = strlen(tux_default_vhost); + len += def_vhost_len; + } + } + + Dprintk("method_str: {%s} [%d]\n", req->method_str, req->method_len); + len += req->method_len + 1; + + Dprintk("version_str: {%s} [%d]\n", req->version_str, req->version_len); + len += req->version_len + 1; + +#ifdef CONFIG_TUX_EXTENDED_LOG + Dprintk("user_agent_str: {%s} [%d]\n", req->user_agent_str, req->user_agent_len); + len += req->user_agent_len + 1; +#endif + if (tux_referer_logging) { + Dprintk("referer_str: {%s} [%d]\n", req->referer_str, req->referer_len); + len += req->referer_len; + } + len++; + + inc = 5*sizeof(u32) + len; +#ifdef CONFIG_TUX_EXTENDED_LOG + inc += 7*sizeof(u32); +#endif + + spin_lock_irqsave(&log_lock, flags); + + next_head = ROUND_UP(log_head + inc); + + if (next_head < LOG_LEN) { + str = log_buffer + log_head; + if (str > log_buffer + LOG_LEN) + TUX_BUG(); + log_head = next_head; + } else { + if (log_head < LOG_LEN) + memset(log_buffer+log_head, 0, LOG_LEN-log_head); + str = log_buffer; + log_head = ROUND_UP(inc); + } + + if (str < log_buffer || str+inc >= log_buffer+LOG_LEN) + TUX_BUG(); + + /* + * Log record signature - this makes finding the next entry + * easier (since record length is variable), and makes the + * binary logfile more robust against potential data corruption + * and other damage. The signature also servers as a log format + * version identifier. + */ +#ifdef CONFIG_TUX_EXTENDED_LOG + *(u32 *)str = 0x2223beef; +#else + *(u32 *)str = 0x1112beef; +#endif + str += sizeof(u32); + CHECK_LOGPTR(str); + + *(u32 *)str = 0; + /* + * Log the client IP address: + */ + if (tux_ip_logging) + *(u32 *)str = req->client_addr; + str += sizeof(u32); + CHECK_LOGPTR(str); + +#ifdef CONFIG_TUX_EXTENDED_LOG + /* + * Log the client port number: + */ + *(u32 *)str = 0; + if (tux_ip_logging) + *(u32 *)str = req->client_port; + str += sizeof(u32); + CHECK_LOGPTR(str); +#endif + + /* + * Log the request timestamp, in units of 'seconds since 1970'. + */ + *(u32 *)str = CURRENT_TIME.tv_sec; + str += sizeof(u32); + CHECK_LOGPTR(str); + +#ifdef CONFIG_TUX_EXTENDED_LOG + *(u32 *)str = req->accept_timestamp; str += sizeof(u32); + *(u32 *)str = req->parse_timestamp; str += sizeof(u32); + *(u32 *)str = req->output_timestamp; str += sizeof(u32); + *(u32 *)str = req->flush_timestamp; str += sizeof(u32); + *(u32 *)str = req->had_cachemiss; str += sizeof(u32); + *(u32 *)str = req->keep_alive; str += sizeof(u32); +#endif + /* + * Log the requested file size (in fact, log actual bytes sent.) + */ + *(u32 *)str = req->bytes_sent; + str += sizeof(u32); + CHECK_LOGPTR(str); + + *(u32 *)str = req->status; + str += sizeof(u32); + CHECK_LOGPTR(str); + + /* + * Zero-terminated method, (base) URI, query and version string. + */ + if (req->method_len) { + memcpy(str, req->method_str, req->method_len); + str += req->method_len; + CHECK_LOGPTR(str); + } + *str++ = 0; + + if (req->virtual) { + if (req->host_len) { + memcpy(str, req->host, req->host_len); + str += req->host_len; + } else { + memcpy(str, tux_default_vhost, def_vhost_len); + str += def_vhost_len; + } + CHECK_LOGPTR(str); + } + + memcpy(str, uri_str, uri_len); + str += uri_len; + *str++ = 0; + + CHECK_LOGPTR(str); + + if (req->version_len) { + memcpy(str, req->version_str, req->version_len); + str += req->version_len; + CHECK_LOGPTR(str); + } + *str++ = 0; +#ifdef CONFIG_TUX_EXTENDED_LOG + if (req->user_agent_len) { + memcpy(str, req->user_agent_str, req->user_agent_len); + str += req->user_agent_len; + CHECK_LOGPTR(str); + } + *str++ = 0; +#endif + CHECK_LOGPTR(str); + + if (tux_referer_logging && req->referer_len) { + memcpy(str, req->referer_str, req->referer_len); + str += req->referer_len; + CHECK_LOGPTR(str); + } + *str++ = 0; + CHECK_LOGPTR(str); + /* + * pad with spaces to next cacheline, with an ending newline. + * (not needed for the user-space log utility, but results in + * a more readable binary log file, and reduces the amount + * of cache pingpong.) + */ + next = (char *)ROUND_UP((unsigned long)str); + + CHECK_LOGPTR(next); + len = next-str; + memset(str, ' ', len); + + pending = (log_head-log_tail) % LOG_LEN; + spin_unlock_irqrestore(&log_lock, flags); + + if (pending >= SOFT_LIMIT) + wake_up(&log_wait); + + if (pending >= HARD_LIMIT) + __throttle_logging(); +} + +void tux_push_pending (struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + struct inet_connection_sock *icsk = inet_csk(sk); + + Dprintk("pushing pending frames on sock %p.\n", sk); + lock_sock(sk); + if ((sk->sk_state == TCP_ESTABLISHED) && !sk->sk_err) { + icsk->icsk_ack.pingpong = tux_ack_pingpong; + tp->nonagle = 1; + __tcp_push_pending_frames(sk, tp, tcp_current_mss(sk, 0), TCP_NAGLE_OFF); + } + release_sock(sk); +} + +inline void tux_push_req (tux_req_t *req) +{ + if (req->sock) + tux_push_pending(req->sock->sk); + if (req->data_sock) + tux_push_pending(req->data_sock->sk); +} + +void __put_data_sock (tux_req_t *req) +{ + unlink_tux_data_socket(req); + if (req->data_sock->file) + fput(req->data_sock->file); + else + sock_release(req->data_sock); + req->data_sock = NULL; +} + +void flush_request (tux_req_t *req, int cachemiss) +{ + struct socket *sock; + struct sock *sk; + int keep_alive; + + if (cachemiss) + TUX_BUG(); + __set_task_state(current, TASK_RUNNING); + + if (req->magic != TUX_MAGIC) + TUX_BUG(); + if (req->ti->thread != current) + TUX_BUG(); +#ifdef CONFIG_TUX_DEBUG + if (req->bytes_expected && (req->bytes_sent != req->bytes_expected)) { + printk("hm, bytes_expected: %d != bytes_sent: %d!\n", + req->bytes_expected, req->bytes_sent); + TUX_BUG(); + } +#endif + SET_TIMESTAMP(req->flush_timestamp); + + log_request(req); + sock = req->sock; + sk = NULL; + if (sock) + sk = sock->sk; + Dprintk("FLUSHING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), sock, sk, req->keep_alive, req->status); + if (req->in_file->f_pos) + /*TUX_BUG()*/; + release_req_dentry(req); + req->private = 0; + + if (req->docroot_dentry) { + dput(req->docroot_dentry); + req->docroot_dentry = NULL; + if (!req->docroot_mnt) + TUX_BUG(); + } + if (req->docroot_mnt) { + mntput(req->docroot_mnt); + req->docroot_mnt = NULL; + } + + req->offset_start = 0; + req->offset_end = 0; + req->output_len = 0; + req->total_file_len = 0; + req->lendigits = 0; + req->mtime = 0; + req->etaglen = 0; + req->etag[0] = 0; + req->ftp_command = 0; + + if (req->postponed) + TUX_BUG(); + if (test_bit(0, &req->idle_input)) + TUX_BUG(); + if (test_bit(0, &req->wait_output_space)) + TUX_BUG(); + if (req->parsed_len) + trunc_headers(req); + if (req->parsed_len) + TUX_BUG(); + req->attr = NULL; + req->usermode = 0; + req->usermodule_idx = 0; + req->atom_idx = 0; + if (req->module_dentry) { + dput(req->module_dentry); + req->module_dentry = NULL; + } + if (req->headers) + kfree(req->headers); + req->headers = NULL; + req->headers_len = 0; + + req->method = METHOD_NONE; + req->method_len = 0; + req->method_str = NULL; + req->version = 0; + req->version_str = NULL; + req->version_len = 0; + + req->uri_str = NULL; + req->uri_len = 0; + + req->objectname[0] = 0; + req->objectname_len = 0; + + req->query_str = NULL; + req->query_len = 0; + + req->cookies_str = NULL; + req->cookies_len = 0; + req->parse_cookies = 0; + + req->contentlen_str = NULL; + req->contentlen_len = 0; + req->content_len = 0; + + req->user_agent_str = NULL; + req->user_agent_len = 0; + + req->may_send_gzip = 0; + req->content_gzipped = 0; + + req->content_type_str = NULL; + req->content_type_len = 0; + + req->accept_str = NULL; + req->accept_len = 0; + + req->accept_charset_str = NULL; + req->accept_charset_len = 0; + + req->accept_encoding_str = NULL; + req->accept_encoding_len = 0; + + req->accept_language_str = NULL; + req->accept_language_len = 0; + + req->cache_control_str = NULL; + req->cache_control_len = 0; + + req->if_modified_since_str = NULL; + req->if_modified_since_len = 0; + + req->if_none_match_str = NULL; + req->if_none_match_len = 0; + + req->if_range_str = NULL; + req->if_range_len = 0; + + req->negotiate_str = NULL; + req->negotiate_len = 0; + + req->pragma_str = NULL; + req->pragma_len = 0; + + req->referer_str = NULL; + req->referer_len = 0; + + req->post_data_str = NULL; + req->post_data_len = 0; + + SET_TIMESTAMP(req->accept_timestamp); +#ifdef CONFIG_TUX_EXTENDED_LOG + req->parse_timestamp = 0; + req->output_timestamp = 0; + req->flush_timestamp = 0; +#endif + req->status = 0; + + req->total_bytes += req->bytes_sent; + req->bytes_sent = 0; +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + req->body_len = 0; + keep_alive = req->keep_alive; + clear_keepalive(req); + req->had_cachemiss = 0; + // first_timestamp and total_bytes is kept! + req->event = 0; + req->lookup_dir = 0; + req->lookup_404 = 0; + + req->error = 0; + req->user_error = 0; + + if (req->abuf.page) + __free_page(req->abuf.page); + memset(&req->abuf, 0, sizeof(req->abuf)); + + if (sk && keep_alive) { + add_tux_atom(req, parse_request); + if (skb_queue_empty(&sk->sk_receive_queue)) { + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + /* + * Avoid the race with the event callback: + */ + if (skb_queue_empty(&sk->sk_receive_queue) || + !test_and_clear_bit(0, &req->idle_input)) { + INC_STAT(nr_idle_input_pending); + spin_unlock_irq(&req->ti->work_lock); + tux_push_req(req); + goto out; + } + del_keepalive_timer(req); + spin_unlock_irq(&req->ti->work_lock); + } + Dprintk("KEEPALIVE PENDING req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status); + add_req_to_workqueue(req); + INC_STAT(nr_keepalive_optimized); + goto out; + } + + del_timer_sync(&req->keepalive_timer); + del_timer_sync(&req->output_timer); + + if (timer_pending(&req->keepalive_timer)) + TUX_BUG(); + if (timer_pending(&req->output_timer)) + TUX_BUG(); + if (!list_empty(&req->lru)) + TUX_BUG(); + req->nr_keepalives = 0; + req->client_addr = 0; + req->client_port = 0; + req->virtual = 0; + req->ftp_offset_start = 0; + + req->host[0] = 0; + req->host_len = 0; + + if (req->cwd_dentry) { + dput(req->cwd_dentry); + req->cwd_dentry = NULL; + if (!req->cwd_mnt) + TUX_BUG(); + } + if (req->cwd_mnt) { + mntput(req->cwd_mnt); + req->cwd_mnt = NULL; + } + put_data_sock(req); + req->prev_pos = 0; + req->curroff = 0; + req->total = 0; + if (req->dirp0) { + kfree(req->dirp0); + req->dirp0 = NULL; + } + + if (sk) + unlink_tux_socket(req); + req->sock = NULL; + /* + * Close potential user-space file descriptors. + */ + { + int fd = req->fd, ret; + + if (fd != -1) { + Dprintk("closing req->fd: %d\n", fd); + req->fd = -1; + ret = tux_close(fd); + if (ret) + TUX_BUG(); + } else + if (sock) + sock_release(sock); + } + kfree_req(req); +out: + ; +} + +static int warn_once = 1; + +static loff_t log_filp_last_index; + +static unsigned int writeout_log (void) +{ + unsigned int len, pending, next_log_tail; + mm_segment_t oldmm = get_fs(); + struct file *log_filp; + char * str; + unsigned int ret; + struct inode *inode; + struct address_space *mapping; + + if (tux_logging) + Dprintk("TUX logger: opening log file {%s}.\n", tux_logfile); + log_filp = tux_open_file(tux_logfile, O_CREAT|O_APPEND|O_WRONLY|O_LARGEFILE); + if (!log_filp) { + if (warn_once) { + printk(KERN_ERR "TUX: could not open log file {%s}!\n", + tux_logfile); + warn_once = 0; + } + __set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + return 0; + } + spin_lock(&log_lock); + str = log_buffer + log_tail; + if (log_head < log_tail) { + len = LOG_LEN-log_tail; + next_log_tail = 0; + } else { + len = log_head-log_tail; + next_log_tail = log_head; + } + if (!len) + goto out; + spin_unlock(&log_lock); + + set_fs(KERNEL_DS); + ret = log_filp->f_op->write(log_filp, str, len, &log_filp->f_pos); + set_fs(oldmm); + + if (len != ret) { + if (ret == -ENOSPC) { + printk(KERN_ERR "TUX: trying to write TUX logfile %s, but filesystem is full! Lost %d bytes of log data.\n", tux_logfile, len); + } else { + printk(KERN_ERR "TUX: log write %d != %d.\n", ret, len); + printk(KERN_ERR "TUX: log_filp: %p, str: %p, len: %d str[len-1]: %d.\n", log_filp, str, len, str[len-1]); + } + goto out_lock; + } + + /* + * Sync log data to disk: + */ + inode = log_filp->f_dentry->d_inode; + mapping = inode->i_mapping; + if (mapping->nrpages > 256) { /* batch stuff up */ + mutex_lock(&inode->i_mutex); + filemap_fdatawrite(inode->i_mapping); + + /* + * Now nuke old pagecache up to the place where we just + * started the I/O. There's no point in trying to invalidate + * pages after that, because they're currently in-flight. + */ + invalidate_mapping_pages(mapping, 0, log_filp_last_index); + log_filp_last_index = log_filp->f_pos >> PAGE_CACHE_SHIFT; + mutex_unlock(&inode->i_mutex); + } + +out_lock: + spin_lock(&log_lock); +out: + log_tail = next_log_tail; + pending = (log_head-log_tail) % LOG_LEN; + spin_unlock(&log_lock); + + if (pending < HARD_LIMIT) + wake_up(&log_full); + + fput(log_filp); + return pending; +} + +static DECLARE_WAIT_QUEUE_HEAD(stop_logger_wait); +static int stop_logger = 0; + +static int logger_thread (void *data) +{ + DECLARE_WAITQUEUE(wait, current); + mm_segment_t oldmm; + + daemonize("TUX logger"); + + oldmm = get_fs(); + set_fs(KERNEL_DS); + printk(KERN_NOTICE "TUX: logger thread started.\n"); +#ifdef CONFIG_SMP + { + cpumask_t map; + + cpus_and(map, cpu_online_map, tux_log_cpu_mask); + if (!(cpus_empty(map))) + set_cpus_allowed(current, map); + + } +#endif + + + spin_lock_irq(¤t->sighand->siglock); + siginitsetinv(¤t->blocked, 0); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (log_buffer) + TUX_BUG(); + log_buffer = vmalloc(LOG_LEN); + if (!log_buffer) { + TUX_BUG(); + goto out; + } + memset(log_buffer, 0, LOG_LEN); + log_head = log_tail = 0; + + current->signal->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; + + add_wait_queue(&log_wait, &wait); + for (;;) { + if (tux_logging) + Dprintk("logger does writeout - stop:%d.\n", stop_logger); + + while (writeout_log() >= SOFT_LIMIT) { + if (stop_logger) + break; + } + if (stop_logger) + break; + /* nothing */; + + if (tux_logging) + Dprintk("logger does sleep - stop:%d.\n", stop_logger); + __set_current_state(TASK_INTERRUPTIBLE); + if (log_head != log_tail) { + __set_current_state(TASK_RUNNING); + continue; + } + schedule_timeout(HZ); + if (tux_logging) + Dprintk("logger back from sleep - stop:%d.\n", stop_logger); + if (signal_pending(current)) + flush_all_signals(); + } + remove_wait_queue(&log_wait, &wait); + + vfree(log_buffer); + log_buffer = NULL; + stop_logger = 0; + wake_up(&stop_logger_wait); +out: + set_fs(oldmm); + + return 0; +} + +void start_log_thread (void) +{ + warn_once = 1; + + logger_pid = kernel_thread(logger_thread, NULL, 0); + if (logger_pid < 0) + TUX_BUG(); +} + +void stop_log_thread (void) +{ + DECLARE_WAITQUEUE(wait, current); + + Dprintk("stopping logger thread %d ...\n", logger_pid); + + __set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&stop_logger_wait, &wait); + stop_logger = 1; + wake_up(&log_wait); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&stop_logger_wait, &wait); + + Dprintk("logger thread stopped!\n"); +} Index: latest/net/tux/main.c =================================================================== --- /dev/null +++ latest/net/tux/main.c @@ -0,0 +1,1417 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * main.c: main management and initialization routines + */ + +#define __KERNEL_SYSCALLS__ +#define __KERNEL_SYSCALLS_NO_ERRNO__ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +/* + * Threads information. + */ +unsigned int nr_tux_threads; +static atomic_t nr_tux_threads_running = ATOMIC_INIT(0); +static int stop_threads = 0; + +threadinfo_t threadinfo[CONFIG_TUX_NUMTHREADS]; + +static void flush_all_requests (threadinfo_t *ti); + +void flush_all_signals (void) +{ + flush_signals(current); + spin_lock_irq(¤t->sighand->siglock); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); +} + +int nr_requests_used (void) +{ + unsigned int i, nr = 0; + + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + nr += ti->nr_requests - ti->nr_free_requests; + } + + return nr; +} + +static inline int accept_pending (threadinfo_t *ti) +{ + int j; + + for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) { + if (!ti->listen[j].proto) + break; + if (!ti->listen[j].sock) + break; + if (!reqsk_queue_empty(&inet_csk(ti->listen[j].sock->sk)->icsk_accept_queue)) + return 1; + } + return 0; +} + +static inline int requests_pending (threadinfo_t *ti) +{ + if (!list_empty(&ti->work_pending)) + return 1; + return 0; +} + +static int event_loop (threadinfo_t *ti) +{ + tux_req_t *req; + int work_done; + +repeat_accept: + if (ti->thread != current) + TUX_BUG(); + + /* + * Any (relevant) event on the socket will change this + * thread to TASK_RUNNING because we add it to both + * the main listening and the connection request socket + * waitqueues. Thus we can do 'lazy checking' of work + * to be done and schedule away only if the thread is + * still TASK_INTERRUPTIBLE. This makes TUX fully + * event driven. + */ + set_task_state(current, TASK_INTERRUPTIBLE); + current->flags |= PF_MEMALLOC; + work_done = 0; + if (accept_pending(ti)) + work_done = accept_requests(ti); + + if (requests_pending(ti)) { + work_done = process_requests(ti, &req); + if (req) + goto handle_userspace_req; + } + + /* + * Be nice to other processes: + */ + if (unlikely(test_thread_flag(TIF_NEED_RESCHED))) { + __set_task_state(current, TASK_RUNNING); + schedule(); + goto repeat_accept; + } + + if (ti->userspace_req) + TUX_BUG(); + if (unlikely(stop_threads)) + goto handle_stop; + + /* Any signals? */ + if (unlikely(signal_pending(current))) + goto handle_signal; + + if (work_done) + goto repeat_accept; + /* + * Any socket event either on the listen socket + * or on the request sockets will wake us up: + */ + if ((current->state != TASK_RUNNING) && + !requests_pending(ti) && !accept_pending(ti)) { + Dprintk("fast thread: no work to be done, sleeping.\n"); + schedule(); + Dprintk("fast thread: back from sleep!\n"); + goto repeat_accept; + } + goto repeat_accept; + +handle_userspace_req: + if (req->attr) + TUX_BUG(); + switch_docroot(req); + ti->userspace_req = req; + __set_task_state(current, TASK_RUNNING); + return TUX_RETURN_USERSPACE_REQUEST; + +handle_signal: + __set_task_state(current, TASK_RUNNING); + return TUX_RETURN_SIGNAL; + +handle_stop: + __set_task_state(current, TASK_RUNNING); + return TUX_RETURN_EXIT; +} + +static int init_queues (int nr_tux_threads) +{ + int i; + + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + + INIT_LIST_HEAD(&ti->all_requests); + + spin_lock_init(&ti->free_requests_lock); + INIT_LIST_HEAD(&ti->free_requests); + + spin_lock_init(&ti->work_lock); + INIT_LIST_HEAD(&ti->work_pending); + INIT_LIST_HEAD(&ti->lru); + + } + return 0; +} + +int tux_chroot (char *dir) +{ + kernel_cap_t saved_cap = current->cap_effective; + mm_segment_t oldmm; + int err; + + /* Allow chroot dir to be in kernel space. */ + oldmm = get_fs(); set_fs(KERNEL_DS); + set_fs(KERNEL_DS); + cap_raise (current->cap_effective, CAP_SYS_CHROOT); + + err = sys_chroot(dir); + if (!err) + sys_chdir("/"); + + current->cap_effective = saved_cap; + set_fs(oldmm); + + return err; +} + +/* + * Right now this is not fully SMP-safe against multiple TUX + * managers. It's just a rudimentary protection against typical + * mistakes. + */ +static int initialized = 0; + +#define MAX_DOCROOTLEN 500 + +static int lookup_docroot(struct nameidata *docroot, const char *name) +{ + int err; + + docroot->mnt = mntget(current->fs->rootmnt); + docroot->dentry = dget(current->fs->root); + docroot->last.len = 0; + docroot->flags = LOOKUP_FOLLOW; + + err = path_walk(name, docroot); + if (err) { + mntput(docroot->mnt); + docroot->mnt = NULL; + return err; + } + return 0; +} + +static int user_req_startup (void) +{ + char name[MAX_DOCROOTLEN]; + struct nameidata *docroot; + unsigned int i; + int err; + + if (initialized) + return -EINVAL; + initialized = 1; + + /* + * Look up the HTTP and FTP document root. + * (typically they are shared, but can be + * different directories.) + */ + docroot = &tux_proto_http.main_docroot; + if (docroot->mnt) + TUX_BUG(); + strcpy(name, tux_common_docroot); + strcat(name, tux_http_subdocroot); + + err = lookup_docroot(docroot, name); + if (err) { + initialized = 0; + printk(KERN_ERR "TUX: could not look up HTTP documentroot: \"%s\"\n", name); + return err; + } + + docroot = &tux_proto_ftp.main_docroot; + if (docroot->mnt) + TUX_BUG(); + strcpy(name, tux_common_docroot); + strcat(name, tux_ftp_subdocroot); + + err = lookup_docroot(docroot, name); + if (err) { +abort: + docroot = &tux_proto_http.main_docroot; + path_release(docroot); + memset(docroot, 0, sizeof(*docroot)); + initialized = 0; + printk(KERN_ERR "TUX: could not look up FTP documentroot: \"%s\"\n", name); + return err; + } + + /* + * Start up the logger thread. (which opens the logfile) + */ + start_log_thread(); + + nr_tux_threads = tux_threads; + if (nr_tux_threads < 1) + nr_tux_threads = 1; + if (nr_tux_threads > CONFIG_TUX_NUMTHREADS) + nr_tux_threads = CONFIG_TUX_NUMTHREADS; + tux_threads = nr_tux_threads; + + /* + * Set up per-thread work-queues: + */ + memset(threadinfo, 0, CONFIG_TUX_NUMTHREADS*sizeof(threadinfo_t)); + init_queues(nr_tux_threads); + + /* + * Prepare the worker thread structures. + */ + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + ti->cpu = i; + ti->gzip_state.workspace = + vmalloc(zlib_deflate_workspacesize()); + if (!ti->gzip_state.workspace || + (zlib_deflateInit(&ti->gzip_state, 6) != Z_OK)) { + stop_log_thread(); + goto abort; + } + init_MUTEX(&ti->gzip_sem); + } + + __module_get(tux_module); + + return 0; +} + +static DECLARE_WAIT_QUEUE_HEAD(wait_stop); +static DECLARE_WAIT_QUEUE_HEAD(thread_stopped); + +static int user_req_shutdown (void) +{ + DECLARE_WAITQUEUE(wait, current); + struct nameidata *docroot; + int i, err = -EINVAL; + + lock_kernel(); + if (!initialized) { + Dprintk("TUX is not up - cannot shut down.\n"); + goto err; + } + initialized = 0; + stop_threads = 1; + add_wait_queue(&thread_stopped, &wait); + +wait_more: + /* + * Wake up all the worker threads so they notice + * that we are being stopped. + */ + set_task_state(current, TASK_UNINTERRUPTIBLE); + if (atomic_read(&nr_tux_threads_running)) { + Dprintk("TUX: shutdown, %d threads still running.\n", + atomic_read(&nr_tux_threads_running)); + wake_up(&wait_stop); + schedule(); + goto wait_more; + } + set_task_state(current, TASK_RUNNING); + stop_threads = 0; + remove_wait_queue(&thread_stopped, &wait); + + if (nr_async_io_pending()) + TUX_BUG(); + + stop_log_thread(); + + docroot = &tux_proto_http.main_docroot; + path_release(docroot); + memset(docroot, 0, sizeof(*docroot)); + docroot = &tux_proto_ftp.main_docroot; + path_release(docroot); + memset(docroot, 0, sizeof(*docroot)); + err = 0; + + flush_dentry_attributes(); + free_mimetypes(); + unregister_all_tuxmodules(); + + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + vfree(ti->gzip_state.workspace); + } + + module_put(tux_module); + +err: + unlock_kernel(); + return err; +} + +void drop_permissions (void) +{ + /* + * Userspace drops privileges already, and group + * membership is important to keep. + */ + /* Give the new process no privileges.. */ + current->uid = current->euid = + current->suid = current->fsuid = tux_cgi_uid; + current->gid = current->egid = + current->sgid = current->fsgid = tux_cgi_gid; + cap_clear(current->cap_permitted); + cap_clear(current->cap_inheritable); + cap_clear(current->cap_effective); +} + +static int wait_for_others (void) +{ + threadinfo_t *ti; + unsigned int cpu; + +repeat: + if (signal_pending(current)) + return -1; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/10); + + for (cpu = 0; cpu < nr_tux_threads; cpu++) { + ti = threadinfo + cpu; + if (ti->listen_error) + return -1; + if (!ti->started) + goto repeat; + } + /* ok, all threads have started up. */ + return 0; +} + +static void zap_listen_sockets (threadinfo_t *ti) +{ + struct socket *sock; + int i; + + for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) { + if (!ti->listen[i].proto) + break; + sock = ti->listen[i].sock; + if (!ti->listen[i].cloned && sock) { + while (waitqueue_active(sock->sk->sk_sleep)) + yield(); + sock_release(sock); + } + ti->listen[i].sock = NULL; + ti->listen[i].proto = NULL; + ti->listen[i].cloned = 0; + } +} + +static DECLARE_MUTEX(serialize_startup); + +static int user_req_start_thread (threadinfo_t *ti) +{ + unsigned int err, cpu, i, j, k; + struct k_sigaction *ka; + + cpu = ti->cpu; +#ifdef CONFIG_SMP + { + unsigned int home_cpu; + cpumask_t map; + + home_cpu = (cpu + tux_cpu_offset) % num_online_cpus(); + map = cpumask_of_cpu(home_cpu); + + cpus_and(map, map, cpu_online_map); + if (!(cpus_empty(map))) + set_cpus_allowed(current, map); + } +#endif + ti->thread = current; + atomic_inc(&nr_tux_threads_running); + + err = start_cachemiss_threads(ti); + if (err) + goto out; + + init_waitqueue_entry(&ti->stop, current); + for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) + init_waitqueue_entry(ti->wait_event + j, current); + + ka = current->sighand->action + SIGCHLD-1; + ka->sa.sa_handler = SIG_IGN; + + /* Block all signals except SIGKILL, SIGSTOP, SIGHUP and SIGCHLD */ + spin_lock_irq(¤t->sighand->siglock); + siginitsetinv(¤t->blocked, sigmask(SIGKILL) | + sigmask(SIGSTOP)| sigmask(SIGHUP) | sigmask(SIGCHLD)); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + if (!tux_listen[cpu][0].proto) { + printk(KERN_ERR "no listen socket specified for TUX thread %d, in /proc/net/tux/%d/listen/, aborting.\n", cpu, cpu); + goto error; + } + + /* + * Serialize startup so that listen sockets can be + * created race-free. + */ + down(&serialize_startup); + + Dprintk("thread %d initializing sockets.\n", cpu); + + for (k = 0; k < CONFIG_TUX_NUMSOCKETS; k++) { + tux_socket_t *e1, *e2; + + e1 = tux_listen[cpu] + k; + if (!e1->proto) + break; + for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++) { + if (i == cpu) + continue; + for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) { + e2 = tux_listen[i] + j; + if (!e2->proto) + continue; + if ((e1->ip == e2->ip) && (e1->port == e2->port) && (e1->proto == e2->proto) && threadinfo[i].listen[j].proto) { + ti->listen[k] = threadinfo[i].listen[j]; + ti->listen[k].cloned = 1; + Dprintk("cloned socket %d from thread %d's socket %d.\n", k, i, j); + goto next_socket; + } + } + } + + ti->listen[k].sock = start_listening(tux_listen[cpu] + k, cpu); + if (!ti->listen[k].sock) + goto error_unlock; + ti->listen[k].cloned = 0; + ti->listen[k].proto = tux_listen[cpu][k].proto; + Dprintk("thread %d got sock %p (%d), proto %s.\n", cpu, ti->listen[k].sock, k, ti->listen[k].proto->name); +next_socket: + ; + } + Dprintk("thread %d done initializing sockets.\n", cpu); + up(&serialize_startup); + + if (wait_for_others()) + goto error_nomsg; + + if (!ti->listen[0].proto) { + printk("hm, socket 0 has no protocol.\n"); + goto error; + } + + add_wait_queue(&wait_stop, &ti->stop); + for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) + if (ti->listen[j].proto) + add_wait_queue_exclusive(ti->listen[j].sock->sk->sk_sleep, + ti->wait_event + j); + drop_permissions(); + + __module_get(tux_module); + return 0; + +error_unlock: + up(&serialize_startup); +error: + printk(KERN_NOTICE "TUX: could not start worker thread %d.\n", ti->cpu); + +error_nomsg: + ti->listen_error = 1; + ti->started = 0; + + zap_listen_sockets(ti); + flush_all_requests(ti); + stop_cachemiss_threads(ti); + + err = -EINVAL; + +out: + /* + * Last thread close the door: + */ + if (atomic_dec_and_test(&nr_tux_threads_running)) + user_req_shutdown(); + + return -err; +} + +static int flush_idleinput (threadinfo_t * ti) +{ + struct list_head *head, *tmp; + tux_req_t *req; + int count = 0; + + head = &ti->all_requests; + tmp = head->next; + + while (tmp != head) { + req = list_entry(tmp, tux_req_t, all); + tmp = tmp->next; + if (test_bit(0, &req->idle_input)) { + idle_event(req); + count++; + } + } + return count; +} + +static int flush_waitoutput (threadinfo_t * ti) +{ + struct list_head *head, *tmp; + tux_req_t *req; + int count = 0; + + head = &ti->all_requests; + tmp = head->next; + + while (tmp != head) { + req = list_entry(tmp, tux_req_t, all); + tmp = tmp->next; + if (test_bit(0, &req->wait_output_space)) { + output_space_event(req); + count++; + } + } + return count; +} + +static void flush_all_requests (threadinfo_t *ti) +{ + for (;;) { + int count; + + count = flush_idleinput(ti); + count += flush_waitoutput(ti); + count += tux_flush_workqueue(ti); + count += flush_freequeue(ti); + if (!ti->nr_requests) + break; + /* + * Go through again if we advanced: + */ + if (count) + continue; + Dprintk("flush_all_requests: %d requests still waiting.\n", ti->nr_requests); +#ifdef CONFIG_TUX_DEBUG + count = print_all_requests(ti); + Dprintk("flush_all_requests: printed %d requests.\n", count); +#endif + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(HZ/10); + } +} + +int nr_async_io_pending (void) +{ + unsigned int i, sum = 0; + + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + if (ti->iot) + sum += ti->iot->nr_async_pending; + } + return sum; +} + +static int user_req_stop_thread (threadinfo_t *ti) +{ + int j; + + printk(KERN_NOTICE "TUX: thread %d stopping ...\n", + (int)(ti-threadinfo)); + + if (!ti->started) + TUX_BUG(); + for (j = 0; j < CONFIG_TUX_NUMSOCKETS; j++) + if (ti->listen[j].proto) + remove_wait_queue(ti->listen[j].sock->sk->sk_sleep, + ti->wait_event + j); + remove_wait_queue(&wait_stop, &ti->stop); + + Dprintk(KERN_NOTICE "TUX: thread %d waiting for sockets to go inactive ...\n", (int)(ti-threadinfo)); + zap_listen_sockets(ti); + + Dprintk(KERN_NOTICE "TUX: thread %d has all sockets inactive.\n", (int)(ti-threadinfo)); + + flush_all_requests(ti); + stop_cachemiss_threads(ti); + + if (ti->nr_requests) + TUX_BUG(); + ti->started = 0; + + printk(KERN_INFO "TUX: thread %d stopped.\n", ti->cpu); + + ti->thread = NULL; + current->tux_info = NULL; + current->tux_exit = NULL; + atomic_dec(&nr_tux_threads_running); + wake_up(&thread_stopped); + + module_put(tux_module); + + return 0; +} + +#define COPY_INT(u_field, k_field) \ +do { \ + if (__copy_to_user(&u_info->u_field, &req->k_field, \ + sizeof(req->k_field))) \ + return_EFAULT; \ +} while (0) + +#define GETLEN(k_field, maxlen) \ + ((req->k_field##_len < maxlen) ? \ + req->k_field##_len : maxlen-1) + +#define COPY_STR(u_field, k_field, maxlen) \ +do { \ + if (__copy_to_user(u_info->u_field, req->k_field##_str, \ + GETLEN(k_field, maxlen))) \ + return_EFAULT; \ +} while (0) + +#define COPY_COND_STR(u_field,k_field,maxlen) \ +do { \ + if (req->k_field##_len) \ + COPY_STR(u_field, k_field, maxlen); \ + if (__put_user((char)0, u_info->u_field + \ + GETLEN(k_field, maxlen))) \ + return_EFAULT; \ +} while (0) + +static void finish_userspace_req (tux_req_t *req) +{ + threadinfo_t *ti = req->ti; + + ti->userspace_req = NULL; + req->usermode = 0; + req->private = 0; + req->error = 0; + DEC_STAT(nr_userspace_pending); + flush_request(req, 0); +} + +static void zap_userspace_req (tux_req_t *req) +{ + clear_keepalive(req); + finish_userspace_req(req); +} + +/* + * Fills in the user-space request structure: + */ +static int prepare_userspace_req (threadinfo_t *ti, user_req_t *u_info) +{ + u64 u_req; + tux_req_t *req = ti->userspace_req; + unsigned int tmp; + int filelen; + int fd; + + Dprintk("prepare_userspace_req(%p).\n", req); + if (!req) + TUX_BUG(); + if (req->error) { + TDprintk("userspace request has error %d.\n", req->error); + return -1; + } + fd = req->fd; + if (fd == -1) { + fd = sock_map_fd(req->sock); + Dprintk("sock_map_fd(%p) :%d.\n", req, fd); + if (fd < 0) { + Dprintk("sock_map_fd() returned %d.\n", fd); + return -EMFILE; + } + req->fd = fd; + } + +#define return_EFAULT do { Dprintk("-EFAULT at %d:%s.\n", __LINE__, __FILE__); return -EFAULT; } while (0) + + if (!access_ok(VERIFY_WRITE, u_info, sizeof(*u_info))) + return_EFAULT; + if (__copy_to_user(&u_info->sock, &fd, sizeof(fd))) + return_EFAULT; + if (req->attr) + TUX_BUG(); + + COPY_INT(module_index, usermodule_idx); + + COPY_COND_STR(query, query, MAX_URI_LEN); + + COPY_INT(event, event); + Dprintk("prepare userspace, user error: %d, event %d.\n", req->user_error, req->event); + COPY_INT(error, user_error); + req->user_error = 0; + + filelen = req->total_file_len; + if (filelen < 0) + filelen = 0; + if (__copy_to_user(&u_info->objectlen, &filelen, sizeof(filelen))) + return_EFAULT; + if ((req->method == METHOD_POST) && !filelen) + if (__copy_to_user(&u_info->objectlen, + &req->content_len, sizeof(filelen))) + return_EFAULT; + if (req->objectname_len) { + if (req->objectname[req->objectname_len]) + TUX_BUG(); + if (__copy_to_user(u_info->objectname, req->objectname, + req->objectname_len + 1)) + return_EFAULT; + } else + if (__put_user((char)0, u_info->objectname)) + return_EFAULT; + + COPY_INT(http_version, version); + COPY_INT(http_method, method); + COPY_INT(keep_alive, keep_alive); + + COPY_INT(cookies_len, cookies_len); + if (req->cookies_len) + COPY_STR(cookies, cookies, MAX_COOKIE_LEN); + if (__put_user((char)0, u_info->cookies + req->cookies_len)) + return_EFAULT; + + u_req = (u64)(unsigned long)req; + if (__copy_to_user(&u_info->id, &u_req, sizeof(u_req))) + return_EFAULT; + COPY_INT(priv, private); + COPY_INT(bytes_sent, bytes_sent); + + tmp = inet_sk(req->sock->sk)->daddr; + if (__copy_to_user(&u_info->client_host, &tmp, sizeof(tmp))) + return_EFAULT; + + COPY_COND_STR(content_type, content_type, MAX_FIELD_LEN); + COPY_COND_STR(user_agent, user_agent, MAX_FIELD_LEN); + COPY_COND_STR(accept, accept, MAX_FIELD_LEN); + COPY_COND_STR(accept_charset, accept_charset, MAX_FIELD_LEN); + COPY_COND_STR(accept_encoding, accept_encoding, MAX_FIELD_LEN); + COPY_COND_STR(accept_language, accept_language, MAX_FIELD_LEN); + COPY_COND_STR(cache_control, cache_control, MAX_FIELD_LEN); + COPY_COND_STR(if_modified_since, if_modified_since, MAX_FIELD_LEN); + COPY_COND_STR(negotiate, negotiate, MAX_FIELD_LEN); + COPY_COND_STR(pragma, pragma, MAX_FIELD_LEN); + COPY_COND_STR(referer, referer, MAX_FIELD_LEN); + + return TUX_RETURN_USERSPACE_REQUEST; +} + +#define GOTO_ERR_no_unlock do { Dprintk("sys_tux() ERR at %s:%d.\n", __FILE__, __LINE__); goto err_no_unlock; } while (0) +#define GOTO_ERR_unlock do { Dprintk("sys_tux() ERR at %s:%d.\n", __FILE__, __LINE__); goto err_unlock; } while (0) + +static int register_mimetype(user_req_t *u_info) +{ + char extension[MAX_URI_LEN], mimetype[MAX_URI_LEN], expires[MAX_URI_LEN]; + u64 u_addr; + char *addr; + int ret; + + ret = strncpy_from_user(extension, u_info->objectname, MAX_URI_LEN); + if (ret <= 0) + GOTO_ERR_no_unlock; + extension[ret] = 0; + Dprintk("got MIME extension: %s.\n", extension); + ret = copy_from_user(&u_addr, &u_info->object_addr, sizeof(u_addr)); + if (ret) + GOTO_ERR_no_unlock; + addr = (char *)(unsigned long)u_addr; + ret = strncpy_from_user(mimetype, addr, MAX_URI_LEN); + if (ret <= 0) + GOTO_ERR_no_unlock; + mimetype[ret] = 0; + Dprintk("got MIME type: %s.\n", mimetype); + ret = strncpy_from_user(expires, u_info->cache_control, MAX_URI_LEN); + if (ret >= 0) + expires[ret] = 0; + else + expires[0] = 0; + Dprintk("got expires header: %s.\n", expires); + + add_mimetype(extension, mimetype, expires); + ret = 0; +err_no_unlock: + return ret; +} + +void user_send_buffer (tux_req_t *req, int cachemiss) +{ + int ret; + + + SET_TIMESTAMP(req->output_timestamp); + +repeat: + ret = send_sync_buf(req, req->sock, req->userbuf, req->userlen, MSG_DONTWAIT | MSG_MORE); + switch (ret) { + case -EAGAIN: + add_tux_atom(req, user_send_buffer); + if (add_output_space_event(req, req->sock)) { + del_tux_atom(req); + goto repeat; + } + INC_STAT(user_sendbuf_write_misses); + break; + default: + if (ret <= 0) { + req_err(req); + req->usermode = 0; + req->private = 0; + add_req_to_workqueue(req); + break; + } + req->userbuf += ret; + req->userlen -= ret; + if ((int)req->userlen < 0) + TUX_BUG(); + if (req->userlen) + goto repeat; + add_req_to_workqueue(req); + break; + } +} + +void user_send_object (tux_req_t *req, int cachemiss) +{ + int ret; + + + SET_TIMESTAMP(req->output_timestamp); + +repeat: + ret = generic_send_file(req, req->sock, cachemiss); + switch (ret) { + case -5: + add_tux_atom(req, user_send_object); + output_timeout(req); + break; + case -4: + add_tux_atom(req, user_send_object); + if (add_output_space_event(req, req->sock)) { + del_tux_atom(req); + goto repeat; + } + INC_STAT(user_sendobject_write_misses); + break; + case -3: + INC_STAT(user_sendobject_cachemisses); + add_tux_atom(req, user_send_object); + queue_cachemiss(req); + break; + case -1: + break; + default: + req->in_file->f_pos = 0; + add_req_to_workqueue(req); + break; + } +} + +void user_get_object (tux_req_t *req, int cachemiss) +{ + int missed; + + if (!req->dentry) { + req->usermode = 0; + missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC); + if (req->usermode) + TUX_BUG(); + req->usermode = 1; + if (!missed && !req->dentry) { + req->error = 0; + req->user_error = -ENOENT; + add_req_to_workqueue(req); + return; + } + if (missed) { + if (cachemiss) + TUX_BUG(); + INC_STAT(user_lookup_cachemisses); +fetch_missed: + req->ti->userspace_req = NULL; + DEC_STAT(nr_userspace_pending); + add_tux_atom(req, user_get_object); + queue_cachemiss(req); + return; + } + } + req->total_file_len = req->dentry->d_inode->i_size; + if (!req->output_len) + req->output_len = req->total_file_len; + if (tux_fetch_file(req, !cachemiss)) { + INC_STAT(user_fetch_cachemisses); + goto fetch_missed; + } + req->in_file->f_pos = 0; + add_req_to_workqueue(req); +} + +asmlinkage long __sys_tux (unsigned int action, user_req_t *u_info) +{ + int ret = -1; + threadinfo_t *ti; + tux_req_t *req; + + if (action != TUX_ACTION_CURRENT_DATE) + Dprintk("got sys_tux(%d, %p).\n", action, u_info); + + if (action >= MAX_TUX_ACTION) + GOTO_ERR_no_unlock; + + ti = (threadinfo_t *) current->tux_info; + if (ti) + if (ti->thread != current) + TUX_BUG(); + + if (!capable(CAP_SYS_ADMIN) + && (action != TUX_ACTION_CONTINUE_REQ) && + (action != TUX_ACTION_STOPTHREAD)) + goto userspace_actions; + + switch (action) { + case TUX_ACTION_CONTINUE_REQ: + ret = continue_request((int)(long)u_info); + goto out; + + case TUX_ACTION_STARTUP: + lock_kernel(); + ret = user_req_startup(); + unlock_kernel(); + goto out; + + case TUX_ACTION_SHUTDOWN: + lock_kernel(); + ret = user_req_shutdown(); + unlock_kernel(); + goto out; + + case TUX_ACTION_REGISTER_MODULE: + ret = user_register_module(u_info); + goto out; + + case TUX_ACTION_UNREGISTER_MODULE: + ret = user_unregister_module(u_info); + goto out; + + case TUX_ACTION_STARTTHREAD: + { + unsigned int nr; + + ret = copy_from_user(&nr, &u_info->thread_nr, + sizeof(int)); + if (ret) + GOTO_ERR_no_unlock; + if (nr >= nr_tux_threads) + GOTO_ERR_no_unlock; + ti = threadinfo + nr; + if (ti->started) + GOTO_ERR_unlock; + ti->started = 1; + current->tux_info = ti; + current->tux_exit = tux_exit; + if (ti->thread) + TUX_BUG(); + Dprintk("TUX: current open files limit for TUX%d: %ld.\n", nr, current->signal->rlim[RLIMIT_NOFILE].rlim_cur); + lock_kernel(); + ret = user_req_start_thread(ti); + unlock_kernel(); + if (ret) { + current->tux_info = NULL; + current->tux_exit = NULL; + } else { + if (ti->thread != current) + TUX_BUG(); + } + goto out_userreq; + } + + case TUX_ACTION_STOPTHREAD: + if (!ti) + GOTO_ERR_no_unlock; + if (!ti->started) + GOTO_ERR_unlock; + req = ti->userspace_req; + if (req) + zap_userspace_req(req); + + lock_kernel(); + ret = user_req_stop_thread(ti); + unlock_kernel(); + goto out_userreq; + + case TUX_ACTION_CURRENT_DATE: + ret = strncpy_from_user(tux_date, u_info->new_date, + DATE_LEN); + if (ret <= 0) + GOTO_ERR_no_unlock; + goto out; + + case TUX_ACTION_REGISTER_MIMETYPE: + ret = register_mimetype(u_info); + if (ret) + GOTO_ERR_no_unlock; + goto out; + + case TUX_ACTION_QUERY_VERSION: + ret = (TUX_MAJOR_VERSION << 24) | (TUX_MINOR_VERSION << 16) | TUX_PATCHLEVEL_VERSION; + goto out; + default: + ; + } + +userspace_actions: + + if (!ti) + GOTO_ERR_no_unlock; + + if (!ti->started) + GOTO_ERR_unlock; + + req = ti->userspace_req; + if (!req) { + if (action == TUX_ACTION_EVENTLOOP) + goto eventloop; + GOTO_ERR_unlock; + } + if (!req->usermode) + TUX_BUG(); + + ret = copy_from_user(&req->event, &u_info->event, sizeof(int)); + if (ret) + GOTO_ERR_unlock; + ret = copy_from_user(&req->status, &u_info->http_status, sizeof(int)); + if (ret) + GOTO_ERR_unlock; + ret = copy_from_user(&req->bytes_sent, &u_info->bytes_sent, sizeof(int)); + if (ret) + GOTO_ERR_unlock; + ret = copy_from_user(&req->private, &u_info->priv, sizeof(req->private)); + if (ret) + GOTO_ERR_unlock; + + switch (action) { + + case TUX_ACTION_EVENTLOOP: +eventloop: + req = ti->userspace_req; + if (req) + zap_userspace_req(req); + ret = event_loop(ti); + goto out_userreq; + + /* + * Module forces keepalive off, server will close + * the connection. + */ + case TUX_ACTION_FINISH_CLOSE_REQ: + clear_keepalive(req); + + case TUX_ACTION_FINISH_REQ: + finish_userspace_req(req); + goto eventloop; + + case TUX_ACTION_REDIRECT_REQ: + + ti->userspace_req = NULL; + req->usermode = 0; + req->private = 0; + req->error = TUX_ERROR_REDIRECT; + DEC_STAT(nr_userspace_pending); + add_tux_atom(req, redirect_request); + add_req_to_workqueue(req); + + goto eventloop; + + case TUX_ACTION_POSTPONE_REQ: + + postpone_request(req); + ti->userspace_req = NULL; + ret = TUX_RETURN_USERSPACE_REQUEST; + break; + + case TUX_ACTION_GET_OBJECT: + release_req_dentry(req); + ret = strncpy_from_user(req->objectname, + u_info->objectname, MAX_URI_LEN-1); + if (ret <= 0) { + req->objectname[0] = 0; + req->objectname_len = 0; + GOTO_ERR_unlock; + } + req->objectname[ret] = 0; // string delimit + req->objectname_len = ret; + + Dprintk("got objectname {%s} (%d) from user-space req %p (req: %p).\n", req->objectname, req->objectname_len, u_info, req); + req->ti->userspace_req = NULL; + DEC_STAT(nr_userspace_pending); + user_get_object(req, 0); + goto eventloop; + + case TUX_ACTION_READ_OBJECT: + { + u64 u_addr; + char *addr; + loff_t ppos = 0; + struct file *filp; + + if (!req->dentry) + GOTO_ERR_unlock; + + ret = copy_from_user(&u_addr, &u_info->object_addr, + sizeof(u_addr)); + if (ret) + GOTO_ERR_unlock; + addr = (char *)(unsigned long)u_addr; + filp = dentry_open(req->dentry, NULL, O_RDONLY); + dget(req->dentry); + generic_file_read(filp, addr, req->total_file_len, &ppos); + fput(filp); + ret = TUX_RETURN_USERSPACE_REQUEST; + break; + } + + case TUX_ACTION_SEND_OBJECT: + if (!req->dentry) + GOTO_ERR_unlock; + req->ti->userspace_req = NULL; + DEC_STAT(nr_userspace_pending); + user_send_object(req, 0); + goto eventloop; + + case TUX_ACTION_SEND_BUFFER: + { + u64 u_addr; + char *addr; + unsigned int len; + + ret = copy_from_user(&u_addr, + &u_info->object_addr, sizeof(u_addr)); + if (ret) + GOTO_ERR_unlock; + addr = (char *)(unsigned long)u_addr; + ret = copy_from_user(&len, + &u_info->objectlen, sizeof(addr)); + if (ret) + GOTO_ERR_unlock; + if ((int)len <= 0) + GOTO_ERR_unlock; + + ret = -EFAULT; + if (!access_ok(VERIFY_READ, addr, len)) + GOTO_ERR_unlock; + req->userbuf = addr; + req->userlen = len; + + req->ti->userspace_req = NULL; + DEC_STAT(nr_userspace_pending); + user_send_buffer(req, 0); + ret = 0; + goto eventloop; + } + + case TUX_ACTION_READ_HEADERS: + { + char *addr; + u64 u_addr; + + ret = copy_from_user(&u_addr, &u_info->object_addr, + sizeof(u_addr)); + if (ret) + GOTO_ERR_unlock; + addr = (char *)(unsigned long)u_addr; + ret = copy_to_user(&u_info->objectlen, + &req->headers_len, sizeof(req->headers_len)); + if (ret) + GOTO_ERR_unlock; + ret = copy_to_user(addr,req->headers, req->headers_len); + if (ret) + GOTO_ERR_unlock; + break; + } + + case TUX_ACTION_READ_POST_DATA: + { + char *addr; + unsigned int size; + u64 u_addr; + + ret = copy_from_user(&u_addr, &u_info->object_addr, + sizeof(u_addr)); + if (ret) + GOTO_ERR_unlock; + addr = (char *)(unsigned long)u_addr; + + ret = copy_from_user(&size, &u_info->objectlen, + sizeof(size)); + if (ret) + GOTO_ERR_unlock; + Dprintk("READ_POST_DATA: got %p(%d).\n", addr, size); + if (req->post_data_len < size) + size = req->post_data_len; + Dprintk("READ_POST_DATA: writing %d.\n", size); + ret = copy_to_user(&u_info->objectlen, + &size, sizeof(size)); + if (ret) + GOTO_ERR_unlock; + ret = copy_to_user(addr, req->post_data_str, size); + if (ret) + GOTO_ERR_unlock; + goto out; + } + + case TUX_ACTION_WATCH_PROXY_SOCKET: + { + struct socket *sock; + int err; + long fd; + u64 u_addr; + + ret = copy_from_user(&u_addr, &u_info->object_addr, + sizeof(u_addr)); + if (ret) + GOTO_ERR_unlock; + fd = (int)(unsigned long)u_addr; + + sock = sockfd_lookup(fd, &err); + if (!sock) + GOTO_ERR_unlock; + put_data_sock(req); + link_tux_data_socket(req, sock); + + ret = 0; + goto out; + } + + case TUX_ACTION_WAIT_PROXY_SOCKET: + { + if (!req->data_sock) + GOTO_ERR_unlock; + if (socket_input(req->data_sock)) { + ret = TUX_RETURN_USERSPACE_REQUEST; + goto out_userreq; + } + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + spin_unlock_irq(&req->ti->work_lock); + if (socket_input(req->data_sock)) { + unidle_req(req); + ret = TUX_RETURN_USERSPACE_REQUEST; + goto out_userreq; + } + req->ti->userspace_req = NULL; + goto eventloop; + } + + default: + GOTO_ERR_unlock; + } + +out_userreq: + req = ti->userspace_req; + if (req) { + ret = prepare_userspace_req(ti, u_info); + if (ret < 0) { + TDprintk("hm, user req %p returned %d, zapping.\n", + req, ret); + zap_userspace_req(req); + goto eventloop; + } + } +out: + if (action != TUX_ACTION_CURRENT_DATE) + Dprintk("sys_tux(%d, %p) returning %d.\n", action, u_info, ret); + while (unlikely(test_thread_flag(TIF_NEED_RESCHED))) { + __set_task_state(current, TASK_RUNNING); + schedule(); + } + return ret; +err_unlock: +err_no_unlock: + Dprintk("sys_tux(%d, %p) returning -EINVAL (ret:%d)!\n", action, u_info, ret); + while (unlikely(test_thread_flag(TIF_NEED_RESCHED))) { + __set_task_state(current, TASK_RUNNING); + schedule(); + } + return -EINVAL; +} + +/* + * This gets called if a TUX thread does an exit(). + */ +void tux_exit (void) +{ + __sys_tux(TUX_ACTION_STOPTHREAD, NULL); +} + +int tux_init(void) +{ + if (init_tux_request_slabs()) + return -ENOMEM; + + start_sysctl(); + +#ifdef CONFIG_TUX_MODULE + spin_lock(&tux_module_lock); + sys_tux_ptr = __sys_tux; + tux_module = THIS_MODULE; + spin_unlock(&tux_module_lock); +#endif + + return 0; +} + +void tux_cleanup (void) +{ +#ifdef CONFIG_TUX_MODULE + spin_lock(&tux_module_lock); + tux_module = NULL; + sys_tux_ptr = NULL; + spin_unlock(&tux_module_lock); +#endif + end_sysctl(); + + free_tux_request_slabs(); +} + +module_init(tux_init) +module_exit(tux_cleanup) + +MODULE_LICENSE("GPL"); + Index: latest/net/tux/Makefile =================================================================== --- /dev/null +++ latest/net/tux/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for TUX +# + +obj-$(CONFIG_TUX) += tux.o + +tux-y := accept.o input.o userspace.o cachemiss.o output.o \ + redirect.o postpone.o logger.o proto_http.o proto_ftp.o \ + proc.o main.o mod.o abuf.o times.o directory.o gzip.o + +tux-$(subst m,y,$(CONFIG_TUX_EXTCGI)) += cgi.o extcgi.o + Index: latest/net/tux/mod.c =================================================================== --- /dev/null +++ latest/net/tux/mod.c @@ -0,0 +1,262 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * mod.c: loading/registering of dynamic TUX modules + */ + +#include <net/tux.h> +#include <linux/kmod.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +DEFINE_SPINLOCK(tuxmodules_lock); +static LIST_HEAD(tuxmodules_list); + +tcapi_template_t * get_first_usermodule (void) +{ + tcapi_template_t *tcapi; + struct list_head *head, *curr, *next; + + spin_lock(&tuxmodules_lock); + head = &tuxmodules_list; + next = head->next; + + while ((curr = next) != head) { + tcapi = list_entry(curr, tcapi_template_t, modules); + next = curr->next; + if (tcapi->userspace_id) { + spin_unlock(&tuxmodules_lock); + return tcapi; + } + } + spin_unlock(&tuxmodules_lock); + return NULL; +} + +static tcapi_template_t * lookup_module (const char *vfs_name) +{ + tcapi_template_t *tcapi; + struct list_head *head, *curr, *next; + + while (*vfs_name == '/') + vfs_name++; + Dprintk("looking up TUX module {%s}.\n", vfs_name); + head = &tuxmodules_list; + next = head->next; + + while ((curr = next) != head) { + tcapi = list_entry(curr, tcapi_template_t, modules); + next = curr->next; + Dprintk("checking module {%s} == {%s}?\n", vfs_name, tcapi->vfs_name); + if (!strcmp(tcapi->vfs_name, vfs_name)) + return tcapi; + } + return NULL; +} + +/* + * Attempt to load a TUX application module. + * This is the slow path, we cache ('link') the module's + * API vector to the inode. + * The module loading path is serialized, and we handshake + * with the loaded module and fetch its API vector. + */ +tcapi_template_t * lookup_tuxmodule (const char *filename) +{ + tcapi_template_t *tcapi; + + spin_lock(&tuxmodules_lock); + tcapi = lookup_module(filename); + if (!tcapi) + Dprintk("did not find module vfs:{%s}\n", filename); + spin_unlock(&tuxmodules_lock); + return tcapi; +} + + +int register_tuxmodule (tcapi_template_t *tcapi) +{ + int ret = -EEXIST; + + spin_lock(&tuxmodules_lock); + + if (lookup_module(tcapi->vfs_name)) { + Dprintk("module with VFS binding '%s' already registered!\n", + tcapi->vfs_name); + goto out; + } + + list_add(&tcapi->modules, &tuxmodules_list); + ret = 0; + Dprintk("TUX module %s registered.\n", tcapi->vfs_name); +out: + spin_unlock(&tuxmodules_lock); + + return ret; +} + +void unregister_all_tuxmodules (void) +{ + tcapi_template_t *tcapi; + struct list_head *curr; + + spin_lock(&tuxmodules_lock); + while (((curr = tuxmodules_list.next)) != &tuxmodules_list) { + tcapi = list_entry(curr, tcapi_template_t, modules); + list_del(curr); + kfree(tcapi->vfs_name); + kfree(tcapi); + } + spin_unlock(&tuxmodules_lock); +} + +tcapi_template_t * unregister_tuxmodule (char *vfs_name) +{ + tcapi_template_t *tcapi; + int err = 0; + + spin_lock(&tuxmodules_lock); + tcapi = lookup_module(vfs_name); + if (!tcapi) { + Dprintk("huh, module %s not registered??\n", vfs_name); + err = -1; + } else { + list_del(&tcapi->modules); + Dprintk("TUX module %s unregistered.\n", vfs_name); + } + spin_unlock(&tuxmodules_lock); + + return tcapi; +} + +static int check_module_version (user_req_t *u_info) +{ + int major, minor, patch, ret; + + ret = copy_from_user(&major, &u_info->version_major, sizeof(int)); + ret += copy_from_user(&minor, &u_info->version_minor, sizeof(int)); + ret += copy_from_user(&patch, &u_info->version_patch, sizeof(int)); + if (ret) + return -EFAULT; + + if ((major != TUX_MAJOR_VERSION) || (minor > TUX_MINOR_VERSION)) { + + printk(KERN_ERR "TUX: module version %d:%d incompatible with kernel version %d:%d!\n", major, minor, TUX_MAJOR_VERSION, TUX_MINOR_VERSION); + return -EINVAL; + } + return 0; +} + +int user_register_module (user_req_t *u_info) +{ + int idx, len, ret; + tcapi_template_t *tcapi; + char modulename [MAX_URI_LEN+1]; + + ret = check_module_version(u_info); + if (ret) + return ret; + + /* + * Check module name length. + */ + ret = strnlen_user(u_info->objectname, MAX_URI_LEN+2); + if (ret < 0) + goto out; + ret = -EINVAL; + if (ret >= MAX_URI_LEN) + goto out; + + Dprintk("register user-module, %p.\n", u_info); + ret = strncpy_from_user(modulename, u_info->objectname, MAX_URI_LEN); + if (ret < 0) + goto out; + modulename[ret] = 0; + Dprintk("... user-module is: {%s}.\n", modulename); + len = strlen(modulename); + if (!len) + printk(KERN_ERR "no module name provided: please upgrade your TUX user-space utilities!\n"); + if (!len || (len > MAX_URI_LEN)) + return -EINVAL; + Dprintk("... user-module len is: %d.\n", len); + + ret = copy_from_user(&idx, &u_info->module_index, sizeof(int)); + if (ret || !idx) + goto out; + Dprintk("... user-module index is: %d.\n", idx); + + ret = -ENOMEM; + tcapi = (tcapi_template_t *) kmalloc(sizeof(*tcapi), GFP_KERNEL); + if (!tcapi) + goto out; + memset(tcapi, 0, sizeof(*tcapi)); + + tcapi->vfs_name = (char *) kmalloc(len+1, GFP_KERNEL); + if (!tcapi->vfs_name) { + kfree(tcapi); + goto out; + } + strcpy(tcapi->vfs_name, modulename); + tcapi->userspace_id = idx; + + Dprintk("... registering module {%s}.\n", tcapi->vfs_name); + ret = register_tuxmodule(tcapi); +out: + return ret; +} + +int user_unregister_module (user_req_t *u_info) +{ + int len, ret; + tcapi_template_t *tcapi; + char modulename [MAX_URI_LEN+1]; + + /* + * Check module name length. + */ + ret = strnlen_user(u_info->objectname, MAX_URI_LEN+2); + if (ret < 0) + goto out; + ret = -EINVAL; + if (ret >= MAX_URI_LEN) + goto out; + Dprintk("unregister user-module, %p.\n", u_info); + ret = strncpy_from_user(modulename, u_info->objectname, MAX_URI_LEN); + if (ret <= 0) + goto out; + modulename[ret] = 0; + Dprintk("... user-module is: {%s}.\n", modulename); + len = strlen(modulename); + if (!len || (len > MAX_URI_LEN)) + return -EINVAL; + Dprintk("... user-module len is: %d.\n", len); + + Dprintk("... unregistering module {%s}.\n", modulename); + tcapi = unregister_tuxmodule(modulename); + ret = -EINVAL; + if (tcapi) { + ret = 0; + kfree(tcapi->vfs_name); + kfree(tcapi); + } +out: + return ret; +} + Index: latest/net/tux/output.c =================================================================== --- /dev/null +++ latest/net/tux/output.c @@ -0,0 +1,352 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * output.c: Send data to clients + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +int send_sync_buf (tux_req_t *req, struct socket *sock, const char *buf, const size_t length, unsigned long flags) +{ + struct msghdr msg; + struct iovec iov; + int len, written = 0, left = length; + struct tcp_sock *tp = tcp_sk(sock->sk); + + tp->nonagle = 2; + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = flags | MSG_NOSIGNAL; +repeat_send: + msg.msg_iov->iov_len = left; + msg.msg_iov->iov_base = (char *) buf + written; + + len = sock_sendmsg(sock, &msg, left); + + Dprintk("sendmsg ret: %d, written: %d, left: %d.\n", len,written,left); + if ((len == -ERESTARTSYS) || (!(flags & MSG_DONTWAIT) && + (len == -EAGAIN))) { + flush_all_signals(); + goto repeat_send; + } + if (len > 0) { + written += len; + left -= len; + if (left) + goto repeat_send; + } + if (len >= 0) { + if (written != length) + TUX_BUG(); + if (left) + TUX_BUG(); + } + if (req && (written > 0)) + req->bytes_sent += written; + Dprintk("sendmsg FINAL ret: %d, written: %d, left: %d.\n", len,written,left); + return written ? written : len; +} + +unsigned int tux_zerocopy_sendfile = 1; + +typedef struct sock_send_desc +{ + struct socket *sock; + tux_req_t *req; +} sock_send_desc_t; + +static int sock_send_actor (read_descriptor_t * desc, struct page *page, + unsigned long offset, unsigned long orig_size) +{ + sock_send_desc_t *sock_desc = (sock_send_desc_t *)desc->arg.buf; + struct socket *sock = sock_desc->sock; + tux_req_t *req = sock_desc->req; + unsigned int flags; + ssize_t written; + char *buf = NULL; + unsigned int size; + + flags = MSG_DONTWAIT | MSG_NOSIGNAL; + if (desc->count < orig_size) + orig_size = desc->count; + if (desc->count > orig_size) + flags |= MSG_MORE; + Dprintk("sock_send_actor(), page: %p, offset: %ld, orig_size: %ld, sock: %p, desc->count: %d, desc->written: %d, MSG_MORE: %d.\n", page, offset, orig_size, sock, desc->count, desc->written, flags & MSG_MORE); + + if (req->content_gzipped >= 2) { + unsigned int gzip_left; + struct msghdr msg; + struct iovec iov; + mm_segment_t oldmm; + char *kaddr = kmap(page); + __u32 in_len, out_len; + out_len = orig_size*101/100 + 12; + buf = tux_kmalloc(out_len); + in_len = orig_size; + size = out_len; + gzip_left = 0; +// 8b1f 0808 fdc4 3bd8 0300 79 +buf[1] = 0x8b; buf[0] = 0x1f; buf[3] = 0x08; buf[2] = 0x08; +buf[5] = 0xfd; buf[4] = 0xc4; buf[7] = 0x3b; buf[6] = 0xd8; +buf[9] = 0x03; buf[8] = 0x00; buf[10] = 0x79; + size += 11; + Dprintk("pre-compress: in_len: %d, out_len: %d, gzip_left: %d, uncompressed size: %d.\n", in_len, out_len, gzip_left, size); + gzip_left = tux_gzip_compress(req, kaddr, buf+11, &in_len, &out_len); + size -= out_len; + buf[11] = 0x79; buf[12] = 0x00; + + Dprintk("post-compress: in_len: %d, out_len: %d, gzip_left: %d, compressed size: %d.\n", in_len, out_len, gzip_left, size); + kunmap(page); + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + flags &= ~MSG_DONTWAIT; + msg.msg_flags = flags; + iov.iov_base = buf; + iov.iov_len = size; + + oldmm = get_fs(); set_fs(KERNEL_DS); + written = sock_sendmsg(sock, &msg, size); + set_fs(oldmm); + + Dprintk("buf: %p, offset: %ld, size: %d, written: %d.\n", buf, offset, size, written); + if (written == size) + written = orig_size; + else + written = size; + + } else { + size = orig_size; + if (tux_zerocopy_sendfile && sock->ops->sendpage && + (sock->sk->sk_route_caps&NETIF_F_SG)) { + written = sock->ops->sendpage(sock, page, offset, size, flags); + } else { + struct msghdr msg; + struct iovec iov; + char *kaddr; + mm_segment_t oldmm; + + if (offset+size > PAGE_SIZE) + return -EFAULT; + + kaddr = kmap(page); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + msg.msg_flags = flags; + iov.iov_base = kaddr + offset; + iov.iov_len = size; + + oldmm = get_fs(); set_fs(KERNEL_DS); + written = sock_sendmsg(sock, &msg, size); + set_fs(oldmm); + + Dprintk("kaddr: %p, offset: %ld, size: %d, written: %d.\n", kaddr, offset, size, written); + kunmap(page); + } + } + if (written < 0) { + desc->error = written; + written = 0; + } + Dprintk("desc->count: %d, desc->written: %d, written: %d.\n", desc->count, desc->written, written); + desc->count -= written; + if ((int)desc->count < 0) + TUX_BUG(); + desc->written += written; + + if (buf) + kfree(buf); + + return written; +} + +/* + * Return 1 if the output space condition went away + * before adding the handler. + */ +int add_output_space_event (tux_req_t *req, struct socket *sock) +{ + struct sock *sk = sock->sk; + /* + * blocked due to socket IO? + */ + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0,&req->wait_output_space)) + TUX_BUG(); + INC_STAT(nr_output_space_pending); + + if ((sk->sk_state == TCP_ESTABLISHED) && enough_wspace(sk)) { + if (test_and_clear_bit(0, &req->wait_output_space)) { + DEC_STAT(nr_output_space_pending); + del_keepalive_timer(req); + spin_unlock_irq(&req->ti->work_lock); + return 1; + } + } + spin_unlock_irq(&req->ti->work_lock); + + return 0; +} + +#define SEND_BLOCKSIZE (164*1024) + +int generic_send_file (tux_req_t *req, struct socket *sock, int cachemiss) +{ + sock_send_desc_t sock_desc; + int len, want, nonblock = !cachemiss; + struct tcp_sock *tp = tcp_sk(sock->sk); + + tp->nonagle = 2; + + sock_desc.sock = sock; + sock_desc.req = req; + +repeat: + Dprintk("generic_send_file(%p,%d,%p) called, f_pos: %Ld, output_len: %Ld.\n", req, nonblock, sock, req->in_file->f_pos, req->output_len); + + if (req->proto->check_req_err(req, cachemiss)) + return -1; + if (connection_too_fast(req) == 2) { + len = -5; + goto out; + } + if (req->total_file_len < req->in_file->f_pos) + TUX_BUG(); + + req->desc.written = 0; + /* + * Careful, output_len can be 64-bit, while 'want' can be 32-bit. + */ + if (req->output_len > SEND_BLOCKSIZE) + want = SEND_BLOCKSIZE; + else + want = req->output_len; + req->desc.count = want; + req->desc.arg.buf = (char *) &sock_desc; + req->desc.error = 0; + Dprintk("sendfile(), desc.count: %d.\n", req->desc.count); + do_generic_file_read(req->in_file, &req->in_file->f_pos, &req->desc, sock_send_actor, nonblock); + if (req->desc.written > 0) { + req->bytes_sent += req->desc.written; + req->output_len -= req->desc.written; + } + if (!nonblock && (req->desc.error == -EWOULDBLOCKIO)) + TUX_BUG(); + Dprintk("sendfile() wrote: %d bytes.\n", req->desc.written); + if (req->output_len && !req->desc.written && !req->desc.error) { +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + req->in_file->f_pos = 0; + req->error = TUX_ERROR_CONN_CLOSE; + zap_request(req, cachemiss); + return -1; + } + + switch (req->desc.error) { + + case -EWOULDBLOCKIO: + len = -3; + break; + case -EAGAIN: +no_write_space: + Dprintk("sk->wmem_queued: %d, sk->sndbuf: %d.\n", + sock->sk->sk_wmem_queued, sock->sk->sk_sndbuf); + len = -4; + break; + default: + len = req->desc.written; +#ifdef CONFIG_TUX_DEBUG + if (req->desc.error) + TDprintk("TUX: sendfile() returned error %d (signals pending: %08lx)!\n", req->desc.error, current->pending.signal.sig[0]); +#endif + if (!req->desc.error) { + if (req->output_len < 0) + BUG(); + if (req->output_len) { + if (test_bit(SOCK_NOSPACE, &sock->flags)) + goto no_write_space; + goto repeat; + } + } +#ifdef CONFIG_TUX_DEBUG + if (req->desc.written != want) + TDprintk("TUX: sendfile() wrote %d bytes, wanted %d! (pos %Ld) (signals pending: %08lx).\n", req->desc.written, want, req->in_file->f_pos, current->pending.signal.sig[0]); + else + Dprintk("TUX: sendfile() FINISHED for req %p, wrote %d bytes.\n", req, req->desc.written); + req->bytes_expected = 0; +#endif + break; + } + +out: + Dprintk("sendfile() wrote %d bytes.\n", len); + + return len; +} + +static int file_fetch_actor (read_descriptor_t * desc, struct page *page, + unsigned long offset, unsigned long size) +{ + if (desc->count < size) + size = desc->count; + + desc->count -= size; + desc->written += size; + + return size; +} + +int tux_fetch_file (tux_req_t *req, int nonblock) +{ + int len; + + req->desc.written = 0; + req->desc.count = req->output_len; + req->desc.arg.buf = NULL; + req->desc.error = 0; + + do_generic_file_read(req->in_file, &req->in_file->f_pos, &req->desc, + file_fetch_actor, nonblock); + if (nonblock && (req->desc.error == -EWOULDBLOCKIO)) + return 1; + len = req->desc.written; + if (req->desc.error) + Dprintk("fetchfile() returned %d error!\n", req->desc.error); + Dprintk("fetchfile() fetched %d bytes.\n", len); + return 0; +} + Index: latest/net/tux/parser.h =================================================================== --- /dev/null +++ latest/net/tux/parser.h @@ -0,0 +1,102 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, Ingo Molnar <mingo@redhat.com> + * + * parser.h: generic parsing routines + */ + +#define get_c(ptr,left) \ +({ \ + char __ret; \ + \ + if (!left) \ + GOTO_INCOMPLETE; \ + left--; \ + __ret = *((ptr)++); \ + if (!__ret) \ + GOTO_REDIR; \ + __ret; \ +}) + +#define PARSE_TOKEN(ptr,str,left) \ + ({ \ + int __ret; \ + \ + if (!left) \ + GOTO_INCOMPLETE; \ + if (sizeof(str)-1 > left) { \ + if (memcmp(ptr, str, left)) \ + GOTO_REDIR; \ + GOTO_INCOMPLETE; \ + } \ + \ + if (memcmp(ptr, str, sizeof(str)-1)) \ + __ret = 0; \ + else { \ + ptr += sizeof(str)-1; \ + left -= sizeof(str)-1; \ + __ret = 1; \ + } \ + __ret; \ + }) + +#define PARSE_METHOD(req,ptr,name,left) \ + ({ \ + int __ret; \ + \ + if (PARSE_TOKEN(ptr,#name" ",left)) { \ + req->method = METHOD_##name; \ + __ret = 1; \ + } else \ + __ret = 0; \ + __ret; \ + }) + +#define COPY_LINE(ptr,target,left) \ + do { \ + char prev_c = 0, c; \ + while (((c = get_c(ptr,left))) != '\n') \ + *target++ = prev_c = c; \ + if (prev_c != '\r') \ + GOTO_REDIR; \ + } while (0) + +#define COPY_LINE_TOLOWER(ptr,target,left,limit) \ + do { \ + char prev_c = 0, c; \ + while (((c = get_c(ptr,left))) != '\n') { \ + if ((c >= 'A') && (c <= 'Z')) \ + c -= 'A'-'a'; \ + *target++ = prev_c = c; \ + if (target == (limit)) \ + GOTO_REDIR; \ + } \ + if (prev_c != '\r') \ + GOTO_REDIR; \ + } while (0) + +#define COPY_FIELD(ptr,target,left) \ + do { \ + char c; \ + while ((c = get_c(ptr,left)) != ' ') \ + *target++ = c; \ + } while (0) + +#define SKIP_LINE(ptr,left) \ + do { \ + char prev_c = 0, c; \ + while (((c = get_c(ptr,left))) != '\n') \ + prev_c = c; \ + if (prev_c != '\r') \ + GOTO_REDIR; \ + } while (0) + +#define SKIP_WHITESPACE(curr,left) \ +do { \ + while ((left) && (*(curr) == ' ')) \ + (curr)++, (left)--; \ + if (!(left)) \ + GOTO_REDIR; \ +} while (0) + Index: latest/net/tux/postpone.c =================================================================== --- /dev/null +++ latest/net/tux/postpone.c @@ -0,0 +1,77 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * postpone.c: postpone/continue userspace requests + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +void postpone_request (tux_req_t *req) +{ + if (!req->usermode) + TUX_BUG(); + INC_STAT(nr_postpone_pending); + req->postponed = 1; +} + +/* + * Continue a postponed request. The request will show up in the + * userspace queue and will be handled by the fast thread. + * A request can only be postponed in a TUX process, but can be + * continued from any process that has access to the socket file + * descriptor. + */ +int continue_request (int fd) +{ + threadinfo_t *ti; + struct socket *sock; + tux_req_t *req; + int err; + + sock = sockfd_lookup(fd, &err); + if (!sock || !sock->sk) + goto out; + req = sock->sk->sk_user_data; + + err = -EINVAL; + if (!req) + goto out_put; + ti = req->ti; + if (!req->postponed) + goto out_unlock_put; + if (!req->usermode) + TUX_BUG(); + + req->postponed = 0; + DEC_STAT(nr_postpone_pending); + + Dprintk("continuing postponed req %p.\n", req); + add_req_to_workqueue(req); + +out_unlock_put: + err = 0; +out_put: + fput(sock->file); +out: + return err; +} + Index: latest/net/tux/proc.c =================================================================== --- /dev/null +++ latest/net/tux/proc.c @@ -0,0 +1,1149 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * proc.c: /proc/sys/tux handling + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +char tux_common_docroot[200] = "/var/www/tux/"; +char tux_http_subdocroot[200] = ""; +char tux_ftp_subdocroot[200] = ""; +char tux_logfile[200] = "/var/log/tux"; +char tux_cgiroot[200] = "/var/www/tux/cgiroot/"; +char tux_404_page[200] = "404.html"; +char tux_default_vhost[200] = "default"; +char tux_extra_html_header[600]; +unsigned int tux_extra_html_header_size = 0; + +int tux_cgi_uid = -1; +int tux_cgi_gid = -1; +unsigned int tux_clientport = 8080; +unsigned int tux_logging = 0; +unsigned int tux_threads = 2; +unsigned int tux_max_connect = 10000; +unsigned int tux_max_keepalives = 10000; +unsigned int tux_max_backlog = 2048; +unsigned int tux_keepalive_timeout = 0; +unsigned int tux_max_output_bandwidth = 0; +unsigned int tux_defer_accept = 1; +unsigned int tux_mode_forbidden = 0 /*S_IXUGO*/; /* do not allow executable (CGI) files */ +unsigned int tux_mode_allowed = S_IROTH; /* allow access if read-other is set */ +unsigned int tux_virtual_server = 0; +unsigned int tux_ftp_virtual_server = 0; +unsigned int mass_hosting_hash = 0; +unsigned int strip_host_tail = 0; +unsigned int tux_max_object_size = 0; +cpumask_t tux_log_cpu_mask = CPU_MASK_ALL; +unsigned int tux_compression = 0; +unsigned int tux_noid = 0; +unsigned int tux_cgi_inherit_cpu = 0; +cpumask_t tux_cgi_cpu_mask = CPU_MASK_ALL; +unsigned int tux_zerocopy_header = 1; +unsigned int tux_max_free_requests = 1000; +unsigned int tux_ignore_query = 0; +unsigned int tux_all_userspace = 0; +unsigned int tux_redirect_logging = 1; +unsigned int tux_max_header_len = 3000; +unsigned int tux_referer_logging = 0; +unsigned int tux_generate_etags = 1; +unsigned int tux_generate_last_mod = 1; +unsigned int tux_generate_cache_control = 1; +unsigned int tux_ip_logging = 1; +unsigned int tux_ftp_wait_close = 1; +unsigned int tux_ftp_log_retr_only = 0; +unsigned int tux_hide_unreadable = 1; +unsigned int tux_http_dir_indexing = 0; +unsigned int tux_log_incomplete = 0; +unsigned int tux_cpu_offset = 0; +unsigned int tux_ftp_login_message = 0; + +static struct ctl_table_header *tux_table_header; + +static ctl_table tux_table[] = { + { NET_TUX_DOCROOT, + "documentroot", + &tux_common_docroot, + sizeof(tux_common_docroot), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_DOCROOT, + "http_subdocroot", + &tux_http_subdocroot, + sizeof(tux_http_subdocroot), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_DOCROOT, + "ftp_subdocroot", + &tux_ftp_subdocroot, + sizeof(tux_ftp_subdocroot), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_LOGFILE, + "logfile", + &tux_logfile, + sizeof(tux_logfile), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_THREADS, + "threads", + &tux_threads, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_KEEPALIVE_TIMEOUT, + "keepalive_timeout", + &tux_keepalive_timeout, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_KEEPALIVE_BW, + "max_output_bandwidth", + &tux_max_output_bandwidth, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_DEFER_ACCEPT, + "defer_accept", + &tux_defer_accept, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_BACKLOG, + "max_backlog", + &tux_max_backlog, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_CONNECT, + "max_connect", + &tux_max_connect, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_KEEPALIVES, + "max_keepalives", + &tux_max_keepalives, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MODE_FORBIDDEN, + "mode_forbidden", + &tux_mode_forbidden, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MODE_ALLOWED, + "mode_allowed", + &tux_mode_allowed, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CGI_UID, + "cgi_uid", + &tux_cgi_uid, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CGI_GID, + "cgi_gid", + &tux_cgi_gid, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CGIROOT, + "cgiroot", + &tux_cgiroot, + sizeof(tux_cgiroot), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_404_PAGE, + "404_page", + &tux_404_page, + sizeof(tux_404_page), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_404_PAGE, + "default_vhost", + &tux_default_vhost, + sizeof(tux_default_vhost), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_404_PAGE, + "extra_html_header", + &tux_extra_html_header, + sizeof(tux_extra_html_header), + 0644, + NULL, + proc_dostring, + &sysctl_string, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "extra_html_header_size", + &tux_extra_html_header_size, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "clientport", + &tux_clientport, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "generate_etags", + &tux_generate_etags, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "generate_last_mod", + &tux_generate_last_mod, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "generate_cache_control", + &tux_generate_cache_control, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "ip_logging", + &tux_ip_logging, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "ftp_wait_close", + &tux_ftp_wait_close, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "ftp_log_retr_only", + &tux_ftp_log_retr_only, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "http_dir_indexing", + &tux_http_dir_indexing, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "hide_unreadable", + &tux_hide_unreadable, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CLIENTPORT, + "log_incomplete", + &tux_log_incomplete, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_LOGGING, + "TDprintk", + &tux_TDprintk, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_LOGGING, + "Dprintk", + &tux_Dprintk, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, +#ifdef TUX_DPRINTK +#endif + { NET_TUX_LOGGING, + "logging", + &tux_logging, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_LOGENTRY_ALIGN_ORDER, + "logentry_align_order", + &tux_logentry_align_order, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_ACK_PINGPONG, + "ack_pingpong", + &tux_ack_pingpong, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_PUSH_ALL, + "push_all", + &tux_push_all, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_ZEROCOPY_PARSE, + "zerocopy_parse", + &tux_zerocopy_parse, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_VIRTUAL_SERVER, + "virtual_server", + &tux_virtual_server, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_VIRTUAL_SERVER, + "mass_hosting_hash", + &mass_hosting_hash, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_VIRTUAL_SERVER, + "strip_host_tail", + &strip_host_tail, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_VIRTUAL_SERVER, + "ftp_virtual_server", + &tux_ftp_virtual_server, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_OBJECT_SIZE, + "max_object_size", + &tux_max_object_size, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_COMPRESSION, + "compression", + &tux_compression, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_NOID, + "noid", + &tux_noid, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_CGI_INHERIT_CPU, + "cgi_inherit_cpu", + &tux_cgi_inherit_cpu, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_ZEROCOPY_HEADER, + "zerocopy_header", + &tux_zerocopy_header, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_ZEROCOPY_SENDFILE, + "zerocopy_sendfile", + &tux_zerocopy_sendfile, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_FREE_REQUESTS, + "max_free_requests", + &tux_max_free_requests, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_ALL_USERSPACE, + "all_userspace", + &tux_all_userspace, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_REDIRECT_LOGGING, + "redirect_logging", + &tux_redirect_logging, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_IGNORE_QUERY, + "ignore_query", + &tux_ignore_query, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_REFERER_LOGGING, + "referer_logging", + &tux_referer_logging, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_REFERER_LOGGING, + "cpu_offset", + &tux_cpu_offset, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_REFERER_LOGGING, + "ftp_login_message", + &tux_ftp_login_message, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + { NET_TUX_MAX_HEADER_LEN, + "max_header_len", + &tux_max_header_len, + sizeof(int), + 0644, + NULL, + proc_dointvec, + &sysctl_intvec, + NULL, + NULL, + NULL + }, + {0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL} }; + + +static ctl_table tux_dir_table[] = { + {NET_TUX, "tux", NULL, 0, 0555, tux_table, NULL, NULL, NULL, NULL, NULL}, + {0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL} +}; + +static ctl_table tux_root_table[] = { + {CTL_NET, "net", NULL, 0, 0555, tux_dir_table, NULL, NULL, NULL, NULL, NULL}, + {0, NULL, NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL} +}; + + +static struct proc_dir_entry * root_tux_dir; +static struct proc_dir_entry * log_cpu_mask_entry; +static struct proc_dir_entry * cgi_cpu_mask_entry; +static struct proc_dir_entry * stat_entry; +static struct proc_dir_entry * tux_dir [CONFIG_TUX_NUMTHREADS]; +static struct proc_dir_entry * listen_dir [CONFIG_TUX_NUMTHREADS]; + +tux_socket_t tux_listen [CONFIG_TUX_NUMTHREADS][CONFIG_TUX_NUMSOCKETS] = + { [0 ... CONFIG_TUX_NUMTHREADS-1] = { {&tux_proto_http, 0, 80, NULL}, } }; + +static int cpu_mask_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = cpumask_scnprintf(page, count, *(cpumask_t *)data); + if (count - len < 2) + return -EINVAL; + len += sprintf(page + len, "\n"); + return len; +} + +static int cpu_mask_write_proc (struct file *file, + const char __user *buffer, + unsigned long count, void *data) +{ + cpumask_t *mask = (cpumask_t *)data; + unsigned long full_count = count, err; + cpumask_t new_value; + + err = cpumask_parse(buffer, count, new_value); + if (err) + return err; + + *mask = new_value; + return full_count; +} + +#define LINE_SIZE 1024 +#define LINE_MASK (LINE_SIZE-1) + +static int print_request_stats (threadinfo_t *ti, char *page, unsigned int skip_count, unsigned int max_count) +{ + struct list_head *head, *curr; + tux_req_t *req; + unsigned int count = 0, size, line_off, len; + char stat_line [LINE_SIZE]; + + if (!max_count) + BUG(); + + head = &ti->all_requests; + curr = head->next; + + while (curr != head) { + req = list_entry(curr, tux_req_t, all); + curr = curr->next; + count++; + if (count <= skip_count) + continue; + line_off = 0; +#define SP(x...) \ + line_off += sprintf(stat_line + line_off, x) + + if (req->proto == &tux_proto_http) + SP("0 "); + else + SP("1 "); + + SP("%p ", req); + SP("%d ", req->atom_idx); + if (req->atom_idx >= 1) + SP("%p ", req->atoms[0]); + else + SP("........ "); + if (req->atom_idx >= 2) + SP("%p ", req->atoms[1]); + else + SP("........ "); + if (!list_empty(&req->work)) SP("W"); else SP("."); + if (!list_empty(&req->free)) SP("F"); else SP("."); + if (!list_empty(&req->lru)) SP("L"); else SP("."); + if (req->keep_alive) SP("K"); else SP("."); + if (req->idle_input) SP("I"); else SP("."); + if (timer_pending(&req->keepalive_timer)) + SP("T(%lu/%lu)",jiffies,req->keepalive_timer.expires); else SP("."); + if (req->wait_output_space) SP("O"); else SP("."); + if (timer_pending(&req->output_timer)) + SP("T"); else SP("."); + SP(" %d ", req->error); + SP(" %d ", req->status); + +#define SP_HOST(ip,port) \ + SP("%d.%d.%d.%d:%d ",NIPQUAD(ip),port) + + if (req->sock) { + if (req->sock->sk) + SP("%d:", req->sock->sk->sk_state); + else + SP("-2:"); + } else + SP("-1:"); + SP_HOST(req->client_addr, req->client_port); + + SP("%Ld ", req->total_file_len); + SP("%Ld ", req->in_file ? req->in_file->f_pos : -1); + if (req->proto == &tux_proto_http) { + SP("%d ", req->method); + SP("%d ", req->version); + } + if (req->proto == &tux_proto_ftp) { + SP("%d ", req->ftp_command); + if (req->data_sock) { + if (req->data_sock->sk) + SP("%d:",req->data_sock->sk->sk_state); + else + SP("-2:"); + if (req->data_sock->sk) + SP_HOST(inet_sk(req->data_sock->sk)->daddr, + inet_sk(req->data_sock->sk)->dport); + else + SP("-1:-1 "); + } else + SP("-1 "); + } + SP("%p/%p %p/%p ", req->sock, req->sock ? req->sock->sk : (void *)-1, req->data_sock, req->data_sock ? req->data_sock->sk : (void *)-1); + + SP("%d\n", req->parsed_len); + len = req->headers_len; + if (len > 500) + len = 500; + SP("\n%d\n", len); + memcpy(stat_line + line_off, req->headers, len); + line_off += len; + len = req->objectname_len; + if (len > 100) + len = 100; + SP("\n%d\n", len); + memcpy(stat_line + line_off, req->objectname, len); + line_off += len; + SP("\n\n<END>"); + if (line_off >= LINE_SIZE) + BUG(); + Dprintk("printing req %p, count %d, page %p: {%s}.\n", req, count, page, stat_line); + size = sprintf(page, "%-*s\n", LINE_SIZE-1, stat_line); + if (size != LINE_SIZE) + BUG(); + page += LINE_SIZE; + if (count-skip_count >= max_count) + break; + } + + Dprintk("count: %d.\n", count-skip_count); + return count - skip_count; +} + +static int stat_read_proc (char *page, char **start, off_t off, + int max_size, int *eof, void *data) +{ + unsigned int i, nr_total = 0, nr, nr_off, nr_skip, size = 0, nr_wanted; + + Dprintk("START, page: %p, max_size: %d, off: %ld.\n", page, max_size, off); + *eof = 1; + if (max_size & LINE_MASK) + return 0; + if (off & LINE_MASK) + return 0; + if (!max_size) + return 0; + + nr_off = off/LINE_SIZE; + + for (i = 0; i < nr_tux_threads; i++) { + threadinfo_t *ti = threadinfo + i; + spin_lock_irq(&ti->work_lock); + nr = ti->nr_requests; + Dprintk("ti: %p, nr: %d, nr_total: %d, nr_off: %d.\n", ti, nr, nr_total, nr_off); + nr_total += nr; + if (nr_off >= nr_total) { + spin_unlock_irq(&ti->work_lock); + continue; + } + nr_skip = nr_off - (nr_total - nr); + nr_wanted = (max_size-size) / LINE_SIZE; + Dprintk("nr_skip: %d, nr_wanted: %d.\n", nr_skip, nr_wanted); + nr = print_request_stats(ti, page + size, nr_skip, nr_wanted); + spin_unlock_irq(&ti->work_lock); + nr_off += nr; + size += nr * LINE_SIZE; + Dprintk("ret: %d requests, size: %d.\n", nr, size); + if (size > max_size) + BUG(); + if (size == max_size) + break; + } + Dprintk("DONE: size: %d.\n", size); + + *start = page; + + if (size) + *eof = 0; + return size; +} + +static int stat_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return -EINVAL; +} + +#define MAX_STRING "http://255.255.255.255:65535" +#define MAX_STRINGLEN (sizeof(MAX_STRING)) + +#define INACTIVE_1 "[inactive]\n" +#define INACTIVE_2 "0\n" + +static int listen_read_proc (char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + tux_socket_t *listen = data; + + if (count < MAX_STRINGLEN) + return -EINVAL; + + if (!listen->proto) + return sprintf(page, INACTIVE_1); + + return sprintf (page, "%s://%u.%u.%u.%u:%hu\n", listen->proto->name, + HIPQUAD(listen->ip), listen->port); +} + +static int listen_write_proc (struct file *file, const char *buffer, + unsigned long count, void *data) +{ + char string [MAX_STRINGLEN]; + unsigned int d1, d2, d3, d4; + unsigned short port; + tux_socket_t *listen = data; + + if (!count) + return -EINVAL; + if (count > MAX_STRINGLEN) + count = MAX_STRINGLEN; + if (copy_from_user(string, buffer, count)) + return -EFAULT; + string[count] = 0; + + if (!strcmp(string, INACTIVE_1) || !strcmp(string, INACTIVE_2)) { + listen->proto = NULL; + listen->ip = 0; + listen->port = 0; + return count; + } + +#define MK_IP(a,b,c,d) ((a << 24) | (b << 16) | (c << 8) | d) + + if (sscanf(string, "http://%u.%u.%u.%u:%hu\n", + &d1, &d2, &d3, &d4, &port) == 5) { + listen->ip = MK_IP(d1,d2,d3,d4); + listen->port = port; + listen->proto = &tux_proto_http; + return count; + } + + if (sscanf(string, "ftp://%u.%u.%u.%u:%hu\n", + &d1, &d2, &d3, &d4, &port) == 5) { + listen->ip = MK_IP(d1,d2,d3,d4); + listen->port = port; + listen->proto = &tux_proto_ftp; + return count; + } + printk(KERN_ERR "tux: invalid listen-socket parameters: %s\n", string); + return -EINVAL; +} + +#define MAX_NAMELEN 10 + +static void register_tux_proc (unsigned int nr) +{ + struct proc_dir_entry *entry; + char name [MAX_NAMELEN]; + int i; + + if (!root_tux_dir) + TUX_BUG(); + + sprintf(name, "%d", nr); + + /* create /proc/net/tux/1234/ */ + tux_dir[nr] = proc_mkdir(name, root_tux_dir); + + /* create /proc/net/tux/1234/listen/ */ + listen_dir[nr] = proc_mkdir("listen", tux_dir[nr]); + + /* create /proc/net/tux/1234/listen/ */ + for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) { + sprintf(name, "%d", i); + entry = create_proc_entry(name, 0700, listen_dir[nr]); + + entry->nlink = 1; + entry->data = (void *)(tux_listen[nr] + i); + entry->read_proc = listen_read_proc; + entry->write_proc = listen_write_proc; + tux_listen[nr][i].entry = entry; + } +} + +static void unregister_tux_proc (unsigned int nr) +{ + int i; + + for (i = 0; i < CONFIG_TUX_NUMSOCKETS; i++) { + remove_proc_entry(tux_listen[nr][i].entry->name,listen_dir[nr]); + tux_listen[nr][i].entry = NULL; + } + + remove_proc_entry(listen_dir[nr]->name, tux_dir[nr]); + + remove_proc_entry(tux_dir[nr]->name, root_tux_dir); +} + +static void cleanup_tux_proc (void) +{ + int i; + + Dprintk("cleaning up /proc/net/tux/\n"); + + for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++) + unregister_tux_proc(i); + remove_proc_entry(stat_entry->name, root_tux_dir); + remove_proc_entry(log_cpu_mask_entry->name, root_tux_dir); + remove_proc_entry(cgi_cpu_mask_entry->name, root_tux_dir); + remove_proc_entry(root_tux_dir->name, proc_net); +} + +static void init_tux_proc (void) +{ + struct proc_dir_entry *entry; + int i; + + if (root_tux_dir) + return; + + /* create /proc/net/tux */ + root_tux_dir = proc_mkdir("tux", proc_net); + + entry = create_proc_entry("log_cpu_mask", 0700, root_tux_dir); + + entry->nlink = 1; + entry->data = (void *)&tux_log_cpu_mask; + entry->read_proc = cpu_mask_read_proc; + entry->write_proc = cpu_mask_write_proc; + + log_cpu_mask_entry = entry; + + entry = create_proc_entry("cgi_cpu_mask", 0700, root_tux_dir); + + entry->nlink = 1; + entry->data = (void *)&tux_cgi_cpu_mask; + entry->read_proc = cpu_mask_read_proc; + entry->write_proc = cpu_mask_write_proc; + + cgi_cpu_mask_entry = entry; + + entry = create_proc_entry("stat", 0700, root_tux_dir); + + entry->nlink = 1; + entry->data = NULL; + entry->read_proc = stat_read_proc; + entry->write_proc = stat_write_proc; + + stat_entry = entry; + + /* + * Create entries for all existing threads. + */ + for (i = 0; i < CONFIG_TUX_NUMTHREADS; i++) + register_tux_proc(i); +} + +void start_sysctl(void) +{ + init_tux_proc(); + tux_table_header = register_sysctl_table(tux_root_table,1); +} + +void end_sysctl(void) +{ + cleanup_tux_proc(); + unregister_sysctl_table(tux_table_header); +} + + Index: latest/net/tux/proto_ftp.c =================================================================== --- /dev/null +++ latest/net/tux/proto_ftp.c @@ -0,0 +1,1555 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * ftp_proto.c: FTP application protocol support + */ + +#define __KERNEL_SYSCALLS__ +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +#define HELLO "220 Linux/TUX 3.0 FTP server welcomes you!\r\n" +#define WRITE_DONE "226 Transfer complete.\r\n" +#define BAD_FILENAME "550 No such file or directory.\r\n" +#define GOOD_DIR "250 CWD command successful.\r\n" +#define LIST_ERR "503 LIST without PORT! Closing connection.\r\n" +#define LIST_ERR_MEM "503 LIST could not allocate memory! Closing connection.\r\n" +#define WRITE_FILE "150 Opening BINARY mode data connection.\r\n" +#define WRITE_LIST "150 Opening ASCII mode data connection.\r\n" +#define RETR_ERR "503 RETR without PORT! Closing connection.\r\n" +#define PORT_OK "200 PORT command successful.\r\n" +#define LOGIN_OK "230-There are currently %d users logged in, out of %d maximum.\r\n230-Bandwidth served by TUX currently: %d KB/sec\r\n230 TUX Guest login ok.\r\n" +#define LOGIN_OK_ONE "230-There is currently 1 user logged in, out of %d maximum.\r\n230-Bandwidth served by TUX currently: %d KB/sec\r\n230 TUX Guest login ok.\r\n" +#define LOGIN_OK_PASS "230 TUX Guest login ok.\r\n" +#define LOGIN_FORBIDDEN "530 Sorry, Login Denied!\r\n" +#define TYPE_OK "200 Type set to I.\r\n" +#define BYE "221 Thank You for using TUX!\r\n" +#define NOT_IMPLEMENTED "502 Command not implemented.\r\n" +#define CLOSE_2 "221 Cannot handle request, closing connection!\r\n" +#define CLOSE "500 Unknown command.\r\n" +#define CLOSE_TIMEOUT "421 Timeout, closing connection!\r\n" +#define LINUX_SYST "215 UNIX Type: L8, Linux/TUX/3.0\r\n" +#define COMMAND_OK "200 Command OK.\r\n" +#define REST_OK "350 Restart offset OK.\r\n" +#define WRITE_ABORTED "426 Transfer aborted, data connection closed.\r\n" +#define SITE "214 No SITE commands are recognized.\r\n" + +#define INTERVAL 10 + +unsigned long last_measurement; +unsigned int ftp_bytes_sent; +unsigned int ftp_bandwidth; + +static void __update_bandwidth (tux_req_t *req, unsigned int bytes) +{ + /* + * Bandwidth measurement. Not completely accurate, + * but it's good enough and lightweight enough. + */ + if (jiffies >= last_measurement + INTERVAL*HZ) { + ftp_bandwidth = (ftp_bytes_sent + 1023)/INTERVAL/1024; + ftp_bytes_sent = 0; + last_measurement = jiffies; + } + if (bytes) + atomic_add(bytes, (atomic_t *)&ftp_bytes_sent); + Dprintk("update_bandwidth(%p,%d), bytes_sent: %d, bandwidth: %d.\n", + req, bytes, ftp_bytes_sent, ftp_bandwidth); +} + +#define update_bandwidth(req,bytes) \ + do { \ + if (unlikely(tux_ftp_login_message)) \ + __update_bandwidth(req, bytes); \ + } while (0) + +static inline void __ftp_send_async_message (tux_req_t *req, + const char *message, int status, unsigned int size) +{ + update_bandwidth(req, size); + __send_async_message(req, message, status, size, 1); +} + +#define ftp_send_async_message(req,str,status) \ + __ftp_send_async_message(req,str,status,sizeof(str)-1) + + +static void ftp_flush_req (tux_req_t *req, int cachemiss) +{ + tux_push_pending(req->sock->sk); + add_req_to_workqueue(req); +} + +static void ftp_execute_command (tux_req_t *req, int cachemiss); + +static void ftp_lookup_vhost (tux_req_t *req, int cachemiss) +{ + struct dentry *dentry; + struct nameidata base = { }; + struct vfsmount *mnt = NULL; + unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC; + char ip[3+1+3+1+3+1+3 + 2]; + + sprintf(ip, "%d.%d.%d.%d", NIPQUAD(inet_sk(req->sock->sk)->rcv_saddr)); + Dprintk("ftp_lookup_vhost(%p, %d, virtual: %d, host: %s.)\n", + req, flag, req->virtual, ip); + + base.flags = LOOKUP_FOLLOW|flag; + base.last_type = LAST_ROOT; + base.dentry = dget(req->proto->main_docroot.dentry); + base.mnt = mntget(req->proto->main_docroot.mnt); + + dentry = __tux_lookup(req, ip, &base, &mnt); + + Dprintk("looked up dentry %p.\n", dentry); + if (dentry && !IS_ERR(dentry) && !dentry->d_inode) + TUX_BUG(); + + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + add_tux_atom(req, ftp_lookup_vhost); + queue_cachemiss(req); + return; + } + goto abort; + } + + req->docroot_dentry = dentry; + req->docroot_mnt = mnt; + + add_tux_atom(req, ftp_execute_command); + add_req_to_workqueue(req); + return; +abort: + if (dentry) { + if (!IS_ERR(dentry)) + dput(dentry); + dentry = NULL; + } + if (mnt) { + if (!IS_ERR(mnt)) + mntput(mnt); + mnt = NULL; + } + req_err(req); + add_req_to_workqueue(req); +} + +static void ftp_got_request (tux_req_t *req) +{ + add_tux_atom(req, parse_request); + add_tux_atom(req, ftp_flush_req); + ftp_send_async_message(req, HELLO, 220); +} + +#define GOTO_ERR { TDprintk("FTP protocol error at: %s:%d\n", \ + __FILE__, __LINE__); goto error; } + +static void zap_data_socket (tux_req_t *req) +{ + if (!req->data_sock) + return; + Dprintk("zapping req %p's data socket %p.\n", req, req->data_sock); + + unlink_tux_data_socket(req); + sock_release(req->data_sock); + req->data_sock = NULL; +} + +static int parse_ftp_message (tux_req_t *req, const int total_len) +{ + int comm, comm1 = 0, comm2 = 0, comm3 = 0, comm4 = 0; + int newline_pos, i; + const char *mess, *curr; + + curr = mess = req->headers; + + Dprintk("FTP parser got %d bytes: --->{%s}<---\n", total_len, curr); + + newline_pos = -1; + for (i = 0; i < total_len; i++, curr++) { + if (!*curr) + GOTO_ERR; + if (!(*curr == '\r') || !(*(curr+1) == '\n')) + continue; + newline_pos = i; + break; + } + Dprintk("Newline pos: %d\n", newline_pos); + if (newline_pos == -1) { + Dprintk("incomplete mess on req %p!\n", req); + return 0; + } + if (newline_pos < 3) + GOTO_ERR; + +#define toup(c) ((((c) >= 'a') && ((c) <= 'z')) ? ((c) + 'A' - 'a') : (c)) + +#define STRING_VAL(c1,c2,c3,c4) \ + (toup(c1) + (toup(c2) << 8) + (toup(c3) << 16) + (toup(c4) << 24)) + +#define STRING_VAL_STR(str) \ + STRING_VAL(str[0], str[1], str[2], str[3]) + + Dprintk("string val (%c%c%c%c): %08x\n", + mess[0], mess[1], mess[2], mess[3], + STRING_VAL_STR(mess)); + +#define PARSE_FTP_COMM(c1,c2,c3,c4,name,num) \ + if (STRING_VAL_STR(mess) == STRING_VAL(c1,c2,c3,c4)) \ + { \ + Dprintk("parsed "#name".\n"); \ + comm##num = FTP_COMM_##name; \ + } + + PARSE_FTP_COMM('A','C','C','T', ACCT,2); + PARSE_FTP_COMM('C','D','U','P', CDUP,3); + PARSE_FTP_COMM('S','M','N','T', SMNT,4); + PARSE_FTP_COMM('Q','U','I','T', QUIT,1); + PARSE_FTP_COMM('R','E','I','N', REIN,2); + PARSE_FTP_COMM('P','A','S','V', PASV,3); + PARSE_FTP_COMM('S','T','R','U', STRU,4); + PARSE_FTP_COMM('S','T','O','R', STOR,2); + PARSE_FTP_COMM('S','T','O','U', STOU,3); + PARSE_FTP_COMM('A','P','P','E', APPE,4); + PARSE_FTP_COMM('A','L','L','O', ALLO,1); + PARSE_FTP_COMM('R','N','F','R', RNFR,2); + PARSE_FTP_COMM('R','N','T','O', RNTO,3); + PARSE_FTP_COMM('A','B','O','R', ABOR,4); + PARSE_FTP_COMM('D','E','L','E', DELE,1); + PARSE_FTP_COMM('R','M','D',' ', RMD, 2); + PARSE_FTP_COMM('M','K','D',' ', MKD, 3); + PARSE_FTP_COMM('P','W','D',' ', PWD, 4); + PARSE_FTP_COMM('S','Y','S','T', SYST,2); + PARSE_FTP_COMM('N','O','O','P', NOOP,3); + PARSE_FTP_COMM('F','E','A','T', FEAT,4); + + comm = comm1 | comm2 | comm3 | comm4; + + if (comm) { + if (newline_pos != 4) + GOTO_ERR; + req->ftp_command = comm; + goto out; + } + + switch (STRING_VAL(mess[0], mess[1], mess[2], mess[3])) { + +#define PARSE_FTP_COMM_3CHAR(c1,c2,c3,name) \ + case STRING_VAL(c1,c2,c3,'\r'): \ + { \ + Dprintk("parsed "#name".\n"); \ + req->ftp_command = FTP_COMM_##name; \ + if (newline_pos != 3) \ + GOTO_ERR; \ + } + +#define PARSE_FTP_3CHAR_COMM_IGNORE(c1,c2,c3,name) \ + case STRING_VAL(c1,c2,c3,' '): \ + { \ + Dprintk("parsed "#name".\n"); \ + req->ftp_command = FTP_COMM_##name; \ + } + +#define PARSE_FTP_COMM_IGNORE(c1,c2,c3,c4,name) \ + case STRING_VAL(c1,c2,c3,c4): \ + { \ + Dprintk("parsed "#name".\n"); \ + req->ftp_command = FTP_COMM_##name; \ + } + +#define PARSE_FTP_3CHAR_COMM_1_FIELD(c1,c2,c3,name,field,field_len,max) \ + case STRING_VAL(c1,c2,c3,' '): \ + { \ + Dprintk("parsed "#name".\n"); \ + req->ftp_command = FTP_COMM_##name; \ + if (newline_pos == 4) \ + GOTO_ERR; \ + if (newline_pos >= 5) { \ + curr = mess + 3; \ + if (*curr++ != ' ') \ + GOTO_ERR; \ + *(field_len) = newline_pos-4; \ + if (*(field_len) >= max) \ + GOTO_ERR; \ + memcpy(field, curr, *(field_len)); \ + (field)[*(field_len)] = 0; \ + } \ + } + +#define PARSE_FTP_COMM_1_FIELD(c1,c2,c3,c4,name,field,field_len,max) \ + case STRING_VAL(c1,c2,c3,c4): \ + { \ + Dprintk("parsed "#name".\n"); \ + req->ftp_command = FTP_COMM_##name; \ + if (newline_pos < 4) \ + GOTO_ERR; \ + if (newline_pos == 4) \ + *(field_len) = 0; \ + else { \ + curr = mess + 4; \ + if (*curr++ != ' ') \ + GOTO_ERR; \ + *(field_len) = newline_pos-5; \ + if (*(field_len) >= max) \ + GOTO_ERR; \ + memcpy(field, curr, *(field_len)); \ + (field)[*(field_len)] = 0; \ + } \ + } + + PARSE_FTP_COMM_1_FIELD('U','S','E','R', USER, + req->username, &req->username_len, + MAX_USERNAME_LEN-1); + if (!req->username_len) + GOTO_ERR; + break; + + { + #define MAX_PASS_LEN 100 + char pass[MAX_PASS_LEN]; + unsigned int pass_len; + PARSE_FTP_COMM_1_FIELD('P','A','S','S', PASS, + pass, &pass_len, + MAX_PASS_LEN-1); + if (!pass_len) + GOTO_ERR; + break; + } + + PARSE_FTP_3CHAR_COMM_1_FIELD('C','W','D', CWD, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (!req->objectname_len) + GOTO_ERR; + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + break; + + PARSE_FTP_COMM_3CHAR('P','W','D', PWD); break; + + { + char type[3]; + unsigned int type_len; + + PARSE_FTP_COMM_1_FIELD('T','Y','P','E', TYPE, + type, &type_len, 2); + if (!type_len) + GOTO_ERR; + if ((type[0] != 'I') && (type[0] != 'A')) + GOTO_ERR; + } + break; + + PARSE_FTP_COMM_1_FIELD('R','E','T','R', RETR, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (!req->objectname_len) { + zap_data_socket(req); + req->ftp_command = FTP_COMM_NONE; + } + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + break; + + PARSE_FTP_COMM_1_FIELD('S','I','Z','E', SIZE, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (!req->objectname_len) + req->ftp_command = FTP_COMM_NONE; + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + break; + + PARSE_FTP_COMM_1_FIELD('M','D','T','M', MDTM, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (!req->objectname_len) + req->ftp_command = FTP_COMM_NONE; + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + break; + + PARSE_FTP_COMM_IGNORE('M','O','D','E', MODE); + break; + + PARSE_FTP_COMM_IGNORE('S','T','A','T', STAT); + break; + + PARSE_FTP_COMM_IGNORE('S','I','T','E', SITE); + break; + + PARSE_FTP_COMM_1_FIELD('L','I','S','T', LIST, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (req->objectname[0] == '-') { + req->objectname_len = 0; + req->objectname[0] = 0; + } + if (req->objectname_len) { + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + } + break; + + PARSE_FTP_COMM_1_FIELD('N','L','S','T', NLST, + req->objectname, &req->objectname_len, + MAX_OBJECTNAME_LEN-1); + if (req->objectname[0] == '-') { + req->objectname_len = 0; + req->objectname[0] = 0; + } + if (req->objectname_len) { + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + } + break; + + PARSE_FTP_COMM_IGNORE('H','E','L','P', HELP); + break; + + PARSE_FTP_COMM_IGNORE('C','L','N','T', CLNT); + break; + +#define IS_NUM(n) (((n) >= '0') && ((n) <= '9')) + +#define GET_DIGIT(curr,n) \ + n += (*curr) - '0'; \ + curr++; \ + if (IS_NUM(*curr)) { \ + n *= 10; + +#define PARSE_PORTNUM(curr,n) \ +do { \ + Dprintk("PORT NUM parser:--->{%s}<---\n", curr);\ + if (!IS_NUM(*curr)) \ + GOTO_ERR; \ + n = 0; \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + }}} \ + if (n > 255) \ + GOTO_ERR; \ + Dprintk("PORT NUM parser:--->{%s}<---\n", curr);\ + Dprintk("PORT NUM parser parsed %d.\n", n); \ +} while (0) + +#define PARSE_NUM(curr,n) \ +do { \ + Dprintk("NUM parser:--->{%s}<---\n", curr); \ + if (!IS_NUM(*curr)) \ + GOTO_ERR; \ + n = 0; \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + GET_DIGIT(curr,n); \ + }}}}}}}}}} \ + Dprintk("NUM parser:--->{%s}<---\n", curr); \ + Dprintk("NUM parser parsed %d.\n", n); \ +} while (0) + + case STRING_VAL('P','O','R','T'): + { + unsigned int h1, h2, h3, h4, p1, p2; + if (req->data_sock) + zap_data_socket(req); + /* + * Minimum size: "PORT 0,0,0,0,0,0", 16 bytes. + */ + if (newline_pos < 16) + GOTO_ERR; + Dprintk("parsed PORT.\n"); + if (req->data_sock) + GOTO_ERR; + curr = mess + 4; + if (*curr++ != ' ') + GOTO_ERR; + PARSE_PORTNUM(curr,h1); + if (*curr++ != ',') + GOTO_ERR; + PARSE_PORTNUM(curr,h2); + if (*curr++ != ',') + GOTO_ERR; + PARSE_PORTNUM(curr,h3); + if (*curr++ != ',') + GOTO_ERR; + PARSE_PORTNUM(curr,h4); + if (*curr++ != ',') + GOTO_ERR; + PARSE_PORTNUM(curr,p1); + if (*curr++ != ',') + GOTO_ERR; + PARSE_PORTNUM(curr,p2); + if (curr-mess != newline_pos) + GOTO_ERR; + req->ftp_command = FTP_COMM_PORT; + req->ftp_user_addr = (h1<<24) + (h2<<16) + (h3<<8) + h4; + req->ftp_user_port = (p1<<8) + p2; + Dprintk("FTP PORT got: %d.%d.%d.%d:%d.\n", + h1, h2, h3, h4, req->ftp_user_port); + Dprintk("FTP user-addr: %08x (htonl: %08x), socket: %08x.\n", + req->ftp_user_addr, htonl(req->ftp_user_addr), + inet_sk(req->sock->sk)->daddr); + /* + * Do not allow redirection of connections, and do + * not allow reserved ports to be accessed. + */ + if (inet_sk(req->sock->sk)->daddr != htonl(req->ftp_user_addr)) + GOTO_ERR; + if (req->ftp_user_port < 1024) + GOTO_ERR; + break; + } + case STRING_VAL('R','E','S','T'): + { + unsigned int offset; + + /* + * Minimum size: "REST 0", 6 bytes. + */ + if (newline_pos < 6) + GOTO_ERR; + Dprintk("parsed REST.\n"); + curr = mess + 4; + if (*curr++ != ' ') + GOTO_ERR; + PARSE_NUM(curr,offset); + if (curr-mess != newline_pos) + GOTO_ERR; + req->ftp_command = FTP_COMM_REST; + req->ftp_offset_start = offset; + Dprintk("FTP REST got: %d bytes offset.\n", offset); + + break; + } + default: + req->ftp_command = FTP_COMM_NONE; + break; + } + +out: + req->parsed_len = newline_pos + 2; + + req->virtual = tux_ftp_virtual_server; + if (req->virtual) + add_tux_atom(req, ftp_lookup_vhost); + else { + req->docroot_dentry = dget(req->proto->main_docroot.dentry); + req->docroot_mnt = mntget(req->proto->main_docroot.mnt); + add_tux_atom(req, ftp_execute_command); + } + + return req->parsed_len; +error: + clear_keepalive(req); + TDprintk("rejecting FTP session!\n"); + TDprintk("mess :--->{%s}<---\n", mess); + TDprintk("mess left:--->{%s}<---\n", curr); + req_err(req); + return -1; +} + +static void ftp_wait_close (tux_req_t *req, int cachemiss); +static void ftp_wait_syn (tux_req_t *req, int cachemiss); + +static int ftp_check_req_err (tux_req_t *req, int cachemiss) +{ + int state = req->sock->sk->sk_state; + int err = req->sock->sk->sk_err | req->error; + int urg = tcp_sk(req->sock->sk)->urg_data; + + if (req->data_sock) { + urg |= tcp_sk(req->data_sock->sk)->urg_data; + state |= req->data_sock->sk->sk_state; + err |= req->data_sock->sk->sk_err; + } + + if ((state <= TCP_SYN_RECV) && !err) { + if (!urg) + return 0; + req->in_file->f_pos = 0; + add_tux_atom(req, flush_request); + zap_data_socket(req); + ftp_send_async_message(req, WRITE_ABORTED, 426); + return 1; + } +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; + if (tux_TDprintk) + dump_stack(); +#endif + req->in_file->f_pos = 0; + TDprintk("zapping, data sock state: %d (err: %d, urg: %d)\n", + state, err, urg); + /* + * We are in the middle of a file transfer, + * zap it immediately: + */ + req->error = TUX_ERROR_CONN_CLOSE; + zap_request(req, cachemiss); + return 1; +} + +void ftp_send_file (tux_req_t *req, int cachemiss) +{ + int ret; + + SET_TIMESTAMP(req->output_timestamp); +repeat: + ret = generic_send_file(req, req->data_sock, cachemiss); + update_bandwidth(req, req->in_file->f_pos - req->prev_pos); + req->prev_pos = req->in_file->f_pos; + + switch (ret) { + case -5: + add_tux_atom(req, ftp_send_file); + output_timeout(req); + break; + case -4: + add_tux_atom(req, ftp_send_file); + if (add_output_space_event(req, req->data_sock)) { + del_tux_atom(req); + goto repeat; + } + break; + case -3: + add_tux_atom(req, ftp_send_file); + queue_cachemiss(req); + break; + case -1: + break; + default: + req->in_file->f_pos = 0; + + if (tux_ftp_wait_close) { + req->data_sock->ops->shutdown(req->data_sock, SEND_SHUTDOWN); + add_tux_atom(req, ftp_wait_close); + add_req_to_workqueue(req); + return; + } + Dprintk("FTP send file req %p finished!\n", req); + zap_data_socket(req); + add_tux_atom(req, ftp_flush_req); + if (req->error) + ftp_send_async_message(req, BAD_FILENAME, 200); + else + ftp_send_async_message(req, WRITE_DONE, 200); + break; + } +} + +#define sk_syn(sk) \ + (!(sk)->sk_err && ((1 << (sk)->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) +#define req_syn(req) \ + (!(req)->error && sk_syn((req)->data_sock->sk)) + +static void ftp_wait_syn (tux_req_t *req, int cachemiss) +{ + Dprintk("ftp_wait_syn in: data socket state %d.\n", req->data_sock->state); + if (req_syn(req)) { + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + spin_unlock_irq(&req->ti->work_lock); + if (req_syn(req)) { + add_tux_atom(req, ftp_wait_syn); + return; + } + unidle_req(req); + } + Dprintk("ftp_wait_syn out: data socket state %d.\n", req->data_sock->state); + add_req_to_workqueue(req); +} + +static void ftp_wait_close (tux_req_t *req, int cachemiss) +{ + struct sock *sk = req->data_sock->sk; + + Dprintk("ftp_wait_close: data socket state %d.\n", sk->sk_state); + + if (!req->error && (sk->sk_state <= TCP_FIN_WAIT1) && !sk->sk_err) { + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + spin_unlock_irq(&req->ti->work_lock); + if (!req->error && (sk->sk_state <= TCP_FIN_WAIT1) && !sk->sk_err) { + add_tux_atom(req, ftp_wait_close); + return; + } + unidle_req(req); + } + zap_data_socket(req); + add_tux_atom(req, ftp_flush_req); + if (req->error) + ftp_send_async_message(req, BAD_FILENAME, 200); + else + ftp_send_async_message(req, WRITE_DONE, 200); +} + +void ftp_get_size (tux_req_t *req, int cachemiss) +{ + char file_size[200]; + int missed, len; + + if (!req->dentry) { + missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC); + if (!missed && !req->dentry) { + ftp_send_async_message(req, BAD_FILENAME, 200); + return; + } + if (missed) { + if (cachemiss) + TUX_BUG(); + add_tux_atom(req, ftp_get_size); + queue_cachemiss(req); + return; + } + } + req->in_file->f_pos = 0; + len = sprintf(file_size, "213 %Li\r\n", req->dentry->d_inode->i_size); + __ftp_send_async_message(req, file_size, 200, len); +} + +void ftp_get_mdtm (tux_req_t *req, int cachemiss) +{ + unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC; + struct dentry *dentry; + struct vfsmount *mnt = NULL; + char file_mdtm[200]; + unsigned int len; + int err; + + dentry = tux_lookup(req, req->objectname, flag, &mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + if (cachemiss) + TUX_BUG(); + add_tux_atom(req, ftp_get_mdtm); + queue_cachemiss(req); + return; + } + goto out_err; + } + err = permission(dentry->d_inode, MAY_READ, NULL); + if (err) + goto out_err_put; + + req->in_file->f_pos = 0; + len = mdtm_time (file_mdtm, dentry->d_inode->i_mtime.tv_sec); + dput(dentry); + mntput(mnt); + __ftp_send_async_message(req, file_mdtm, 200, len); + return; + +out_err_put: + dput(dentry); + mntput(mnt); +out_err: + ftp_send_async_message(req, BAD_FILENAME, 550); +} + +static void ftp_get_file (tux_req_t *req, int cachemiss) +{ + int missed; + + if (!req->dentry) { + missed = lookup_object(req, cachemiss ? 0 : LOOKUP_ATOMIC); + if (!missed && !req->dentry) { + ftp_send_async_message(req, BAD_FILENAME, 200); + return; + } + if (missed) { + if (cachemiss) + TUX_BUG(); + add_tux_atom(req, ftp_get_file); + queue_cachemiss(req); + return; + } + } + Dprintk("ftp_send_file %p, ftp_offset: %Ld, total_len: %Ld.\n", req, req->ftp_offset_start, req->total_file_len); + req->in_file->f_pos = 0; + if (req->ftp_offset_start) { + if (req->ftp_offset_start <= req->total_file_len) { + req->offset_start = req->ftp_offset_start; + req->in_file->f_pos = req->offset_start; + } + req->ftp_offset_start = 0; + } + req->output_len = req->total_file_len - req->offset_start; + req->prev_pos = req->in_file->f_pos; + Dprintk("ftp_send_file %p, f_pos: %Ld (out_len: %Ld).\n", req, req->in_file->f_pos, req->output_len); + add_tux_atom(req, ftp_send_file); + add_tux_atom(req, ftp_wait_syn); + add_tux_atom(req, ftp_flush_req); + ftp_send_async_message(req, WRITE_FILE, 200); +} + +static void __exchange_sockets (tux_req_t *req) +{ + struct socket *tmp; + + tmp = req->data_sock; + req->data_sock = req->sock; + req->sock = tmp; + + req->in_file->f_pos = 0; +} + +static void ftp_do_ls_start (tux_req_t *req, int cachemiss) +{ + Dprintk("ftp_do_ls_start(%p, %d).\n", req, cachemiss); + if (!req->cwd_dentry) + TUX_BUG(); + __exchange_sockets(req); + queue_cachemiss(req); +} + +static void ftp_do_ls_end (tux_req_t *req, int cachemiss) +{ + Dprintk("ftp_do_ls_end(%p, %d).\n", req, cachemiss); + __exchange_sockets(req); + if (tux_ftp_wait_close) { + req->data_sock->ops->shutdown(req->data_sock, SEND_SHUTDOWN); + add_tux_atom(req, ftp_wait_close); + add_req_to_workqueue(req); + return; + } + zap_data_socket(req); + add_tux_atom(req, ftp_flush_req); + if (req->error) + ftp_send_async_message(req, BAD_FILENAME, 200); + else + ftp_send_async_message(req, WRITE_DONE, 200); +} + +static void ftp_chdir (tux_req_t *req, int cachemiss) +{ + unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC; + struct dentry *dentry; + struct vfsmount *mnt = NULL; + int err; + + Dprintk("ftp_chdir(%p, %d, {%s})\n", req, cachemiss, req->objectname); + dentry = tux_lookup(req, req->objectname, flag, &mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + if (cachemiss) + TUX_BUG(); + add_tux_atom(req, ftp_chdir); + queue_cachemiss(req); + return; + } + goto out_err; + } + err = permission(dentry->d_inode, MAY_EXEC, NULL); + if (err) + goto out_err_put; + req->cwd_dentry = dentry; + req->cwd_mnt = mnt; + ftp_send_async_message(req, GOOD_DIR, 200); + return; + +out_err_put: + dput(dentry); + mntput(mnt); +out_err: + ftp_send_async_message(req, BAD_FILENAME, 550); +} + +void ftp_accept_pasv (tux_req_t *req, int cachemiss) +{ + struct socket *sock, *new_sock = NULL; + struct inet_connection_sock *icsk1, *icsk2; + struct tcp_sock *tp1, *tp2; + int err; + + tp1 = tcp_sk(req->data_sock->sk); + icsk1 = inet_csk(req->data_sock->sk); + + Dprintk("PASV accept on req %p, accept_queue: %p.\n", + req, &icsk1->icsk_accept_queue); + if (req->error || (req->data_sock->sk->sk_state != TCP_LISTEN)) + goto error; +new_socket: + if (reqsk_queue_empty(&icsk1->icsk_accept_queue)) { + spin_lock_irq(&req->ti->work_lock); + add_keepalive_timer(req); + if (test_and_set_bit(0, &req->idle_input)) + TUX_BUG(); + spin_unlock_irq(&req->ti->work_lock); + if (reqsk_queue_empty(&icsk1->icsk_accept_queue)) { + add_tux_atom(req, ftp_accept_pasv); + return; + } + unidle_req(req); + } + new_sock = sock_alloc(); + if (!new_sock) + goto error; + sock = req->data_sock; + new_sock->type = sock->type; + new_sock->ops = sock->ops; + + err = sock->ops->accept(sock, new_sock, O_NONBLOCK); + Dprintk("PASV accept() returned %d (state %d).\n", err, new_sock->sk->sk_state); + if (err < 0) + goto error; + if (new_sock->sk->sk_state != TCP_ESTABLISHED) + goto error; + /* + * Do not allow other clients to steal the FTP connection! + */ + if (inet_sk(new_sock->sk)->daddr != inet_sk(req->sock->sk)->daddr) { + Dprintk("PASV: ugh, unauthorized connect?\n"); + sock_release(new_sock); + new_sock = NULL; + goto new_socket; + } + /* + * Zap the listen socket: + */ + zap_data_socket(req); + + tp2 = tcp_sk(new_sock->sk); + icsk2 = inet_csk(new_sock->sk); + tp2->nonagle = 2; + icsk2->icsk_ack.pingpong = tux_ack_pingpong; + new_sock->sk->sk_reuse = 1; + sock_set_flag(new_sock->sk, SOCK_URGINLINE); + sock_reset_flag(new_sock->sk, SOCK_LINGER); + + link_tux_data_socket(req, new_sock); + add_req_to_workqueue(req); + return; + +error: + if (new_sock) + sock_release(new_sock); + req_err(req); + zap_data_socket(req); + ftp_send_async_message(req, CLOSE, 500); +} + +static char * ftp_print_dir_line (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode) +{ + char *string0 = tmp; + unsigned int size; + + if (req->ftp_command == FTP_COMM_NLST) { + memcpy(tmp, d_name, d_len); + tmp += d_len; + *tmp++ = '\r'; + *tmp++ = '\n'; + *tmp = 0; + return tmp; + } + switch (d_type) { + default: + case DT_UNKNOWN: + case DT_WHT: + if (tux_hide_unreadable) + goto out_dput; + *tmp++ = '?'; + break; + + case DT_FIFO: + if (tux_hide_unreadable) + goto out_dput; + *tmp++ = 'p'; + break; + + case DT_CHR: + if (tux_hide_unreadable) + goto out_dput; + *tmp++ = 'c'; + break; + + case DT_DIR: + *tmp++ = 'd'; + break; + + case DT_BLK: + if (tux_hide_unreadable) + goto out_dput; + *tmp++ = 'b'; + break; + + case DT_REG: + *tmp++ = '-'; + break; + + case DT_LNK: + *tmp++ = 'l'; + break; + + case DT_SOCK: + if (tux_hide_unreadable) + goto out_dput; + *tmp++ = 's'; + break; + } + + if (inode->i_mode & S_IRUSR) *tmp++ = 'r'; else *tmp++ = '-'; + if (inode->i_mode & S_IWUSR) *tmp++ = 'w'; else *tmp++ = '-'; + if (inode->i_mode & S_IXUSR) *tmp++ = 'x'; else *tmp++ = '-'; + if (inode->i_mode & S_IRGRP) *tmp++ = 'r'; else *tmp++ = '-'; + if (inode->i_mode & S_IWGRP) *tmp++ = 'w'; else *tmp++ = '-'; + if (inode->i_mode & S_IXGRP) *tmp++ = 'x'; else *tmp++ = '-'; + if (inode->i_mode & S_IROTH) *tmp++ = 'r'; else *tmp++ = '-'; + if (inode->i_mode & S_IWOTH) *tmp++ = 'w'; else *tmp++ = '-'; + if (inode->i_mode & S_IXOTH) *tmp++ = 'x'; else *tmp++ = '-'; + + *tmp++ = ' '; + + size = sprintf(tmp, "%4i %d", inode->i_nlink, inode->i_uid); + tmp += size; + + size = 14 - size; + if (size <= 0) + size = 1; + memset(tmp, ' ', size); + tmp += size; + + size = sprintf(tmp, "%d", inode->i_gid); + tmp += size; + + size = 9 - size; + if (size <= 0) + size = 1; + memset(tmp, ' ', size); + tmp += size; + + tmp += sprintf(tmp, "%8Li", inode->i_size); + *tmp++ = ' '; + + tmp += time_unix2ls(inode->i_mtime.tv_sec, tmp); + *tmp++ = ' '; + + memcpy(tmp, d_name, d_len); + tmp += d_len; + + if (d_type == DT_LNK) { + int len = 0, max_len; + #define ARROW " -> " + + memcpy(tmp, ARROW, sizeof(ARROW)-1); + tmp += sizeof(ARROW)-1; + max_len = MAX_OBJECTNAME_LEN-(tmp-string0); + if (inode->i_op && inode->i_op->readlink) { + mm_segment_t oldmm; + + oldmm = get_fs(); set_fs(KERNEL_DS); + set_fs(KERNEL_DS); + len = inode->i_op->readlink(dentry, tmp, max_len); + set_fs(oldmm); + } + if (len > 0) + tmp += len; + else + Dprintk("hm, readlink() returned %d.\n", len); + } + *tmp++ = '\r'; + *tmp++ = '\n'; + *tmp = 0; + + return tmp; +out_dput: + return NULL; +} + +static void ftp_do_ls_onefile (tux_req_t *req, int cachemiss) +{ + char string0[MAX_OBJECTNAME_LEN+200], *tmp; + + tmp = ftp_print_dir_line(req, string0, req->objectname, req->objectname_len, +DT_REG, req->dentry, req->dentry->d_inode); + if (!tmp) { + req_err(req); + add_req_to_workqueue(req); + return; + } + if (tmp - string0 >= MAX_OBJECTNAME_LEN+200) + BUG(); + __ftp_send_async_message(req, string0, 200, tmp - string0); +} + +static void ftp_lookup_listfile (tux_req_t *req, int cachemiss) +{ + unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC; + struct dentry *dentry; + struct vfsmount *mnt = NULL; + int err; + + Dprintk("ftp_lookup_listfile(%p, %d, {%s})\n", req, cachemiss, req->objectname); + dentry = tux_lookup(req, req->objectname, flag, &mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + if (cachemiss) + TUX_BUG(); + add_tux_atom(req, ftp_lookup_listfile); + queue_cachemiss(req); + return; + } + goto out_err; + } + + if (S_ISDIR(dentry->d_inode->i_mode)) { + err = permission(dentry->d_inode, MAY_EXEC, NULL); + if (err) { + Dprintk("Directory permission error: %d.\n", err); + goto out_err_put; + } + install_req_dentry(req, dentry, mnt); + + add_tux_atom(req, ftp_do_ls_end); + if (!req->cwd_dentry) + TUX_BUG(); + add_tux_atom(req, list_directory); + } else { + install_req_dentry(req, dentry, mnt); + + add_tux_atom(req, ftp_do_ls_end); + add_tux_atom(req, ftp_do_ls_onefile); + } + + add_tux_atom(req, ftp_do_ls_start); + add_tux_atom(req, ftp_wait_syn); + add_tux_atom(req, ftp_flush_req); + ftp_send_async_message(req, WRITE_LIST, 200); + return; + +out_err_put: + dput(dentry); + mntput(mnt); +out_err: + ftp_send_async_message(req, BAD_FILENAME, 550); +} + +static void ftp_execute_command (tux_req_t *req, int cachemiss) +{ + if (!req->parsed_len) + TUX_BUG(); + trunc_headers(req); + req->keep_alive = 1; + + switch (req->ftp_command) { + +#define ABORTED \ + "226 Abort successful.\r\n" + + case FTP_COMM_ABOR: + { + zap_data_socket(req); + ftp_send_async_message(req, ABORTED, 226); + break; + } + + case FTP_COMM_PWD: + { + unsigned int str_len; + char *buf, *path; + + buf = (char *)__get_free_page(GFP_KERNEL); + if (!buf) { + req_err(req); + ftp_send_async_message(req, LIST_ERR_MEM, 200); + GOTO_ERR; + } + + if (!req->cwd_dentry) { + req->cwd_dentry = dget(req->docroot_dentry); + req->cwd_mnt = mntget(req->docroot_mnt); + } + +// "257 "/" is current directory.\r\n" + +#define PART_1 "257 \"" +#define PART_1_LEN (sizeof(PART_1)-1) + +#define PART_3 "\" is current directory.\r\n" +#define PART_3_LEN sizeof(PART_3) + + path = tux_print_path(req, req->cwd_dentry, req->cwd_mnt, + buf+PART_1_LEN, PAGE_SIZE - PART_3_LEN - PART_1_LEN); + + if (path < buf + PART_1_LEN) + BUG(); + + memcpy(path - PART_1_LEN, PART_1, PART_1_LEN); + memcpy(buf + PAGE_SIZE-PART_3_LEN-1, PART_3, PART_3_LEN); + str_len = buf + PAGE_SIZE-1 - (path - PART_1_LEN) - 1; + + __ftp_send_async_message(req, path - PART_1_LEN, 226, str_len); + free_page((unsigned long)buf); + break; + } + + case FTP_COMM_CDUP: + { + memcpy(req->objectname, "..", 3); + req->objectname_len = 2; + req->uri_str = req->objectname; + req->uri_len = req->objectname_len; + + // fall through to CWD: + } + case FTP_COMM_CWD: + { + ftp_chdir(req, cachemiss); + break; + } + + case FTP_COMM_NLST: + case FTP_COMM_LIST: + { + if (!req->data_sock) { + req_err(req); + ftp_send_async_message(req, LIST_ERR, 200); + GOTO_ERR; + } + if (req->dentry) + TUX_BUG(); + if (!req->cwd_dentry) { + req->cwd_dentry = dget(req->docroot_dentry); + req->cwd_mnt = mntget(req->docroot_mnt); + } + if (req->objectname_len) + ftp_lookup_listfile(req, cachemiss); + else { + dget(req->cwd_dentry); + mntget(req->cwd_mnt); + install_req_dentry(req, req->cwd_dentry, req->cwd_mnt); + if (!req->dentry) + TUX_BUG(); + add_tux_atom(req, ftp_do_ls_end); + if (!req->cwd_dentry) + TUX_BUG(); + add_tux_atom(req, list_directory); + add_tux_atom(req, ftp_do_ls_start); + add_tux_atom(req, ftp_wait_syn); + add_tux_atom(req, ftp_flush_req); + ftp_send_async_message(req, WRITE_LIST, 200); + } + break; + } + + case FTP_COMM_RETR: + { + if (!req->data_sock) { + req_err(req); + ftp_send_async_message(req, RETR_ERR, 200); + GOTO_ERR; + } + ftp_get_file(req, cachemiss); + break; + } + + case FTP_COMM_SIZE: + { + ftp_get_size(req, cachemiss); + break; + } + + case FTP_COMM_MDTM: + { + ftp_get_mdtm(req, cachemiss); + break; + } + + case FTP_COMM_PASV: + { + char buf [36 + 4*3 + 5 + 10]; + struct socket *data_sock; + struct sockaddr_in addr; + unsigned int str_len; + struct tcp_sock *tp; + struct inet_connection_sock *icsk; + u32 local_addr; + int err; + + if (req->data_sock) + zap_data_socket(req); + /* + * Create FTP data connection to client: + */ + err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_IP, &data_sock); + if (err < 0) { + Dprintk("sock create err: %d\n", err); + req_err(req); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + + local_addr = inet_sk(req->sock->sk)->rcv_saddr; + addr.sin_family = AF_INET; + addr.sin_port = 0; + addr.sin_addr.s_addr = local_addr; + Dprintk("client address: (%d,%d,%d,%d).\n", + NIPQUAD(inet_sk(req->sock->sk)->daddr)); + + data_sock->sk->sk_reuse = 1; + sock_set_flag(data_sock->sk, SOCK_URGINLINE); + sock_reset_flag(data_sock->sk, SOCK_LINGER); + + err = data_sock->ops->bind(data_sock, + (struct sockaddr*)&addr, sizeof(addr)); + tp = tcp_sk(data_sock->sk); + icsk = inet_csk(data_sock->sk); + + tp->nonagle = 2; + Dprintk("PASV bind() ret: %d.\n", err); + if (err < 0) { + req_err(req); + sock_release(data_sock); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + + icsk->icsk_ack.pingpong = tux_ack_pingpong; + + if (!tux_keepalive_timeout) + tp->linger2 = 0; + else + tp->linger2 = tux_keepalive_timeout * HZ; + + err = data_sock->ops->listen(data_sock, 1); + Dprintk("PASV listen() ret: %d\n", err); + if (err) { + req_err(req); + sock_release(data_sock); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + link_tux_data_socket(req, data_sock); + + Dprintk("FTP PASV listen sock state: %d, sk state: %d\n", + data_sock->state, data_sock->sk->sk_state); + + str_len = sprintf(buf, + "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n", + NIPQUAD(local_addr), + ntohs(inet_sk(data_sock->sk)->sport) / 256, + ntohs(inet_sk(data_sock->sk)->sport) & 255 ); + Dprintk("PASV mess: {%s}\n", buf); + + add_tux_atom(req, ftp_accept_pasv); + add_tux_atom(req, ftp_flush_req); + __ftp_send_async_message(req, buf, 227, str_len); + break; + } + + case FTP_COMM_PORT: + { + struct socket *data_sock; + struct sockaddr_in addr; + kernel_cap_t saved_cap; + u32 local_addr; + int err; + + /* + * Create FTP data connection to client: + */ + err = sock_create_kern(AF_INET, SOCK_STREAM, IPPROTO_IP, &data_sock); + if (err < 0) { + Dprintk("sock create err: %d\n", err); + req_err(req); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + + local_addr = inet_sk(req->sock->sk)->rcv_saddr; + addr.sin_family = AF_INET; + addr.sin_port = htons(20); + addr.sin_addr.s_addr = local_addr; + + Dprintk("data socket address: (%d,%d,%d,%d).\n", + NIPQUAD(local_addr)); + + data_sock->sk->sk_reuse = 1; + sock_set_flag(data_sock->sk, SOCK_URGINLINE); + sock_reset_flag(data_sock->sk, SOCK_LINGER); + + saved_cap = current->cap_effective; + cap_raise (current->cap_effective, CAP_NET_BIND_SERVICE); + err = data_sock->ops->bind(data_sock, + (struct sockaddr*)&addr, sizeof(addr)); + current->cap_effective = saved_cap; + + Dprintk("ACTIVE bind() ret: %d.\n", err); + if (err) { + sock_release(data_sock); + req_err(req); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + tcp_sk(data_sock->sk)->nonagle = 2; + + link_tux_data_socket(req, data_sock); + + addr.sin_family = AF_INET; + addr.sin_port = htons(req->ftp_user_port); + addr.sin_addr.s_addr = htonl(req->ftp_user_addr); + + err = data_sock->ops->connect(data_sock, (struct sockaddr *) &addr, sizeof(addr), O_RDWR|O_NONBLOCK); + if (err && (err != -EINPROGRESS)) { + Dprintk("connect error: %d\n", err); + zap_data_socket(req); + req_err(req); + ftp_send_async_message(req, CLOSE, 500); + GOTO_ERR; + } + Dprintk("FTP data sock state: %d, sk state: %d\n", data_sock->state, data_sock->sk->sk_state); + ftp_send_async_message(req, PORT_OK, 200); + break; + } + + case FTP_COMM_USER: + { + if (!strcmp(req->username, "ftp") + || !strcmp(req->username, "FTP") + || !strcmp(req->username, "anonymous") + || !strcmp(req->username, "ANONYMOUS")) { + unsigned int str_len; + char login_ok [200]; + + if (!tux_ftp_login_message) { + ftp_send_async_message(req, LOGIN_OK_PASS, 230); + break; + } + update_bandwidth(req, 0); /* get current bandwidth */ + if (nr_requests_used() == 1) + str_len = sprintf(login_ok, LOGIN_OK_ONE, + tux_max_connect, ftp_bandwidth); + else + str_len = sprintf(login_ok, LOGIN_OK, + nr_requests_used(), tux_max_connect, ftp_bandwidth); + __ftp_send_async_message(req, login_ok, 200, str_len); + } else { + clear_keepalive(req); + ftp_send_async_message(req, LOGIN_FORBIDDEN, 530); + } + break; + } + case FTP_COMM_PASS: + { + ftp_send_async_message(req, LOGIN_OK_PASS, 230); + break; + } + case FTP_COMM_SITE: + { + ftp_send_async_message(req, SITE, 214); + break; + } + case FTP_COMM_SYST: + { + ftp_send_async_message(req, LINUX_SYST, 200); + break; + } + case FTP_COMM_TYPE: + { + ftp_send_async_message(req, TYPE_OK, 200); + break; + } +#define EXTRA_FEATURES "211-Extensions supported:\r\n SIZE\r\n MDTM\r\n211 End\r\n" + + case FTP_COMM_FEAT: + { + ftp_send_async_message(req, EXTRA_FEATURES, 211); + break; + } + case FTP_COMM_HELP: + case FTP_COMM_CLNT: + case FTP_COMM_NOOP: + { + ftp_send_async_message(req, COMMAND_OK, 200); + break; + } + case FTP_COMM_REST: + { + ftp_send_async_message(req, REST_OK, 200); + break; + } + case FTP_COMM_QUIT: + { + clear_keepalive(req); + ftp_send_async_message(req, BYE, 200); + break; + } + + default: + { + req->keep_alive = 1; + ftp_send_async_message(req, CLOSE, 500); + break; + } + } + return; +error: + Dprintk("rejecting FTP session!\n"); + return; +} + + +static void ftp_timeout (tux_req_t *req, int cachemiss) +{ + Dprintk("called ftp_timeout(%p)\n", req); + if (req->error != TUX_ERROR_CONN_TIMEOUT) + TUX_BUG(); + ftp_send_async_message(req, CLOSE_TIMEOUT, 421); +} + +static void ftp_close (tux_req_t *req, int cachemiss) +{ + Dprintk("called ftp_close(%p)\n", req); + ftp_send_async_message(req, CLOSE, 500); +} + +static void ftp_pre_log (tux_req_t *req) +{ + if (tux_ftp_log_retr_only && (req->ftp_command != FTP_COMM_RETR)) + req->status = 0; + else + req->status = req->ftp_command; +} + +tux_proto_t tux_proto_ftp = { + .defer_accept = 0, + .can_redirect = 0, + .got_request = ftp_got_request, + .parse_message = parse_ftp_message, + .illegal_request = ftp_close, + .request_timeout = ftp_timeout, + .pre_log = ftp_pre_log, + .check_req_err = ftp_check_req_err, + .print_dir_line = ftp_print_dir_line, + .name = "ftp", +}; + Index: latest/net/tux/proto_http.c =================================================================== --- /dev/null +++ latest/net/tux/proto_http.c @@ -0,0 +1,2197 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * proto_http.c: HTTP application protocol support + * + * Right now we detect simple GET headers, anything more + * subtle gets redirected to secondary server port. + */ + +#include <net/tux.h> +#include "parser.h" + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +/* + * Parse the HTTP message and put results into the request structure. + * CISAPI extensions do not see the actual message buffer. + * + * Any perceived irregularity is honored with a redirect to the + * secondary server - which in most cases should be Apache. So + * if TUX gets confused by some strange request we fall back + * to Apache to be RFC-correct. + * + * The parser is 'optimistic', ie. it's optimized for the case where + * the whole message is available and correct. The parser is also + * supposed to be 'robust', ie. it can be called multiple times with + * an incomplete message, as new packets arrive. + */ + +static inline int TOHEX (char c) +{ + switch (c) { + case '0' ... '9': c -= '0'; break; + case 'a' ... 'f': c -= 'a'-10; break; + case 'A' ... 'F': c -= 'A'-10; break; + default: + c = -1; + } + return c; +} + +/* + * This function determines whether the client supports + * gzip-type content-encoding. + */ +static int may_gzip (const char *str, int len) +{ + const char *tmp, *curr; + int i; + + if (len <= 4) + return 0; + tmp = str; + for (i = 0; i <= len-6; i++) { + Dprintk("gzip-checking: {%s}\n", tmp); + if (memcmp(tmp, " gzip", 5)) { + tmp++; + continue; + } + curr = tmp + 5; + + if (*curr == ',' || *curr == '\r') + return 1; + if (memcmp(curr, ";q=", 3)) + return 0; + curr += 3; + /* + * Every qvalue except explicitly zero is accepted. + * Zero values are "q=0.0", "q=0.00", "q=0.000". + * Parsing is optimized. + */ + if (*curr == '0') { + curr += 2; + if (*curr == '0') { + curr++; + if (*curr == ' ' || *curr == '\r') + return 0; + if (*curr == '0') { + curr++; + if (*curr == ' ' || *curr == '\r') + return 0; + if (*curr == '0') { + curr++; + if (*curr == ' ' || + *curr == '\r') + return 0; + } + } + } + } + return 1; + } + return 0; +} + +/* + * This function strips off 'strip_host_tail' number of hostname + * components from the tail of the hostname. + * + * Eg. with a value of '1', the "somesite.hosting.com" hostname gets + * transformed into the "somesite" string. + */ +static void strip_hostname(tux_req_t *req) +{ + int strip = strip_host_tail; + int left = req->host_len; + int component = 0; + + if (!strip || !left) + return; + + while (--left) { + if (req->host[left] != '.') + continue; + if (++component == strip) + break; + } + if (!left) + return; + req->host[left] = 0; + req->host_len = left; +} + +static void http_lookup_vhost (tux_req_t *req, int cachemiss); +static void http_process_message (tux_req_t *req, int cachemiss); + +int parse_http_message (tux_req_t *req, const int total_len) +{ + int hexhex = 0, hex_val_0 = 0, hex_val_1 = 0; + const char *curr, *uri, *message; + unsigned int objectname_len, left; + unsigned int have_r = 0; + char c; + + left = total_len; + message = req->headers; + Dprintk("parsing request:\n---\n%s\n---\n", message); +/* + * RFC 2616, 5.1: + * + * Request-Line = Method SP Request-URI SP HTTP-Version CRLF + */ + + if (!total_len) + TUX_BUG(); + + curr = message; + +#define GOTO_INCOMPLETE do { Dprintk("incomplete at %s:%d.\n", __FILE__, __LINE__); goto incomplete_message; } while (0) +#define GOTO_REDIR do { TDprintk("redirect secondary at %s:%d.\n", __FILE__, __LINE__); goto error; } while (0) + +#define PRINT_MESSAGE_LEFT \ + Dprintk("message left (%d) at %s:%d:\n--->{%s}<---\n", left, __FILE__, __LINE__, curr) + + switch (*curr) { + case 'G': + if (PARSE_METHOD(req,curr,GET,left)) + break; + GOTO_REDIR; + + case 'H': + if (PARSE_METHOD(req,curr,HEAD,left)) + break; + GOTO_REDIR; + + case 'P': + if (PARSE_METHOD(req,curr,POST,left)) + break; + if (PARSE_METHOD(req,curr,PUT,left)) + break; + GOTO_REDIR; + + default: + GOTO_REDIR; + } + + req->method_str = message; + req->method_len = curr-message-1; + + Dprintk("got method %d\n", req->method); + + PRINT_MESSAGE_LEFT; + + /* + * Ok, we got one of the methods we can handle, parse + * the URI: + */ + + { + // Do not allow leading "../" and intermediate "/../" + int dotdot = 1; + char *tmp = req->objectname; + int slashcheck = 1; + + req->uri_str = uri = curr; + + for (;;) { + c = get_c(curr,left); + if (slashcheck) { + if (c == '/') + continue; + slashcheck = 0; + } + + PRINT_MESSAGE_LEFT; + if (c == ' ' || ((c == '?') && (tux_ignore_query != 1)) || c == '\r' || c == '\n') + break; + if (c == '#') + GOTO_REDIR; + + Dprintk("hexhex: %d.\n", hexhex); + /* + * First handle HEX HEX encoding + */ + switch (hexhex) { + case 0: + if (c == '%') { + hexhex = 1; + goto continue_parsing; + } + break; + case 1: + hex_val_0 = TOHEX(c); + if (hex_val_0 < 0) + GOTO_REDIR; + hexhex = 2; + goto continue_parsing; + case 2: + hex_val_1 = TOHEX(c); + if (hex_val_1 < 0) + GOTO_REDIR; + c = (hex_val_0 << 4) | hex_val_1; + if (!c) + GOTO_REDIR; + hexhex = 0; + break; + default: + TUX_BUG(); + } + if (hexhex) + TUX_BUG(); + + switch (dotdot) { + case 0: + break; + case 1: + if (c == '.') + dotdot = 2; + else + dotdot = 0; + break; + case 2: + if (c == '.') + dotdot = 3; + else + dotdot = 0; + break; + case 3: + if (c == '/') + GOTO_REDIR; + else + dotdot = 0; + break; + default: + TUX_BUG(); + } + if (!dotdot && (c == '/')) + dotdot = 1; + + *(tmp++) = c; +continue_parsing: + if (curr - uri >= MAX_OBJECTNAME_LEN) + GOTO_REDIR; + } + PRINT_MESSAGE_LEFT; + *tmp = 0; + + // handle trailing "/.." + if (dotdot == 3) + GOTO_REDIR; + + objectname_len = tmp - req->objectname; + req->objectname_len = objectname_len; + } + Dprintk("got filename %s (%d)\n", req->objectname, req->objectname_len); + + PRINT_MESSAGE_LEFT; + + /* + * Parse optional query string. Copy until end-of-string or space. + */ + if (c == '?') { + int query_len; + const char *query; + + req->query_str = query = curr; + + for (;;) { + c = get_c(curr,left); + if (c == ' ') + break; + if (c == '#') + GOTO_REDIR; + } + if (unlikely(tux_ignore_query == 2)) + req->query_str = NULL; + else { + query_len = curr-query-1; + req->query_len = query_len; + } + } + if (req->query_len) + Dprintk("got query string %s (%d)\n", req->query_str, req->query_len); + req->uri_len = curr-uri-1; + if (!req->uri_len) + GOTO_REDIR; + Dprintk("got URI %s (%d)\n", req->uri_str, req->uri_len); + + PRINT_MESSAGE_LEFT; + /* + * Parse the HTTP version field: + */ + req->version_str = curr; + if (!PARSE_TOKEN(curr,"HTTP/1.",left)) + GOTO_REDIR; + + switch (get_c(curr,left)) { + case '0': + req->version = HTTP_1_0; + break; + case '1': + req->version = HTTP_1_1; + break; + default: + GOTO_REDIR; + } + /* + * We default to keepalive in the HTTP/1.1 case and default + * to non-keepalive in the HTTP/1.0 case. If max_keepalives + * is 0 then we do no keepalives. + */ + clear_keepalive(req); + if (tux_max_keepalives && (req->version == HTTP_1_1)) + req->keep_alive = 1; + req->version_len = curr - req->version_str; + + if (get_c(curr,left) != '\r') + GOTO_REDIR; + if (get_c(curr,left) != '\n') + GOTO_REDIR; + + Dprintk("got version %d [%d]\n", req->version, req->version_len); + PRINT_MESSAGE_LEFT; + + /* + * Now parse (optional) request header fields: + */ + for (;;) { + char c; + + c = get_c(curr,left); + switch (c) { + case '\r': + if (have_r) + GOTO_REDIR; + have_r = 1; + continue; + case '\n': + if (!have_r) + GOTO_REDIR; + goto out; + default: + if (have_r) + GOTO_REDIR; + } + +#define PARSE_STR_FIELD(char,field,str,len) \ + if (PARSE_TOKEN(curr,field,left)) { \ + req->str = curr; \ + SKIP_LINE(curr,left); \ + req->len = curr - req->str - 2; \ + Dprintk(char field "field: %s.\n", req->str); \ + break; \ + } + +#define ALLOW_UNKNOWN_FIELDS 1 +#ifdef ALLOW_UNKNOWN_FIELDS +# define UNKNOWN_FIELD { SKIP_LINE(curr,left); break; } +#else +# define UNKNOWN_FIELD GOTO_REDIR +#endif + + switch (c) { + case 'A': + PARSE_STR_FIELD("A","ccept: ", + accept_str,accept_len); + if (PARSE_TOKEN(curr,"ccept-Encoding: ",left)) { + const char *str = curr-1; + + req->accept_encoding_str = curr; + SKIP_LINE(curr,left); + req->accept_encoding_len = curr - req->accept_encoding_str - 2; + Dprintk("Accept-Encoding field: {%s}.\n", str); + + if (tux_compression && may_gzip(str,curr-str)) { + Dprintk("client accepts gzip!.\n"); + req->may_send_gzip = 1; + } + break; + } + PARSE_STR_FIELD("A","ccept-Charset: ", + accept_charset_str,accept_charset_len); + PARSE_STR_FIELD("A","ccept-Language: ", + accept_language_str,accept_language_len); + UNKNOWN_FIELD; + + case 'C': + if (PARSE_TOKEN(curr,"onnection: ",left)) { +next_token: + switch (get_c(curr,left)) { + case 'K': + if (!PARSE_TOKEN(curr,"eep-Alive",left)) + GOTO_REDIR; + if (tux_max_keepalives) + req->keep_alive = 1; + break; + + case 'C': + case 'c': + if (!PARSE_TOKEN(curr,"lose",left)) + GOTO_REDIR; + clear_keepalive(req); + break; + + case 'k': + if (!PARSE_TOKEN(curr,"eep-alive",left)) + GOTO_REDIR; + if (tux_max_keepalives) + req->keep_alive = 1; + break; + case 'T': + if (PARSE_TOKEN(curr,"E",left)) + break; + if (PARSE_TOKEN(curr,"railers",left)) + break; + if (PARSE_TOKEN(curr,"ransfer-Encoding",left)) + break; + GOTO_REDIR; + case 'P': + if (PARSE_TOKEN(curr,"roxy-Authenticate",left)) + break; + if (PARSE_TOKEN(curr,"roxy-Authorization",left)) + break; + GOTO_REDIR; + case 'U': + if (!PARSE_TOKEN(curr,"pgrade",left)) + GOTO_REDIR; + break; + case ' ': + PRINT_MESSAGE_LEFT; + goto next_token; + case ',': + PRINT_MESSAGE_LEFT; + goto next_token; + default: + GOTO_REDIR; + } + PRINT_MESSAGE_LEFT; + if (*curr != '\r') + goto next_token; + // allow other tokens. + SKIP_LINE(curr,left); + break; + } + + PARSE_STR_FIELD("C","ookie: ", + cookies_str,cookies_len); + PARSE_STR_FIELD("C","ontent-Type: ", + content_type_str,content_type_len); + + if (PARSE_TOKEN(curr,"ontent-Length: ",left) || + PARSE_TOKEN(curr,"ontent-length: ",left)) { + const char *tmp; + req->contentlen_str = curr; + SKIP_LINE(curr,left); + req->contentlen_len = curr - req->contentlen_str - 2; + if (req->contentlen_len) { + tmp = req->contentlen_str; + req->content_len = simple_strtoul(tmp, NULL, 10); + } + Dprintk("Content-Length field: %s [%d].\n", req->contentlen_str, req->contentlen_len); + Dprintk("Content-Length value: %d.\n", req->content_len); + break; + } + PARSE_STR_FIELD("C","ache-Control: ", + cache_control_str,cache_control_len); + UNKNOWN_FIELD; + + case 'H': + if (PARSE_TOKEN(curr,"ost: ",left)) { + const char *tmp = curr; + char *tmp2 = req->host; + + /* + * canonize the hostname: + * + * 1) strip off preceding 'www.' variants, + * 2) transform it to lowercase. + * 3) strip trailing dots + * 4) potentially strip off tail + */ + +#define is_w(n) ((curr[n] == 'w') || (curr[n] == 'W')) + + if ((left > 4) && is_w(0) && is_w(1) && + is_w(2) && curr[3] == '.') { + curr += 4; + left -= 4; + tmp = curr; + } + + COPY_LINE_TOLOWER(curr, tmp2, left, req->host+MAX_HOST_LEN-2); + req->host_len = curr - tmp - 2; + while (req->host[req->host_len] == '.') { + if (!req->host_len) + break; + req->host_len--; + } + req->host[req->host_len] = 0; + if (strip_host_tail) + strip_hostname(req); + Dprintk("Host field: %s [%d].\n", req->host, req->host_len); + break; + } + UNKNOWN_FIELD; + + case 'I': + PARSE_STR_FIELD("I","f-None-Match: ", + if_none_match_str,if_none_match_len); + PARSE_STR_FIELD("I","f-Modified-Since: ", + if_modified_since_str,if_modified_since_len); + PARSE_STR_FIELD("I","f-Range: ", + if_range_str,if_range_len); + UNKNOWN_FIELD; + + case 'N': + PARSE_STR_FIELD("N","egotiate: ", + negotiate_str,negotiate_len); + UNKNOWN_FIELD; + + case 'P': + PARSE_STR_FIELD("P","ragma: ", + pragma_str,pragma_len); + UNKNOWN_FIELD; + + case 'R': + + PARSE_STR_FIELD("R","eferer: ", + referer_str,referer_len); + if (!PARSE_TOKEN(curr,"ange: bytes=",left)) + UNKNOWN_FIELD; + { + const char *tmp = curr; + char *tmp2 = (char *)curr; + unsigned int offset_start = 0, offset_end = 0; + + if (*tmp2 != '-') + offset_start = simple_strtoul(tmp2, &tmp2, 10); + if (*tmp2 == '-') { + tmp2++; + if (*tmp2 != '\r') + offset_end = simple_strtoul(tmp2, &tmp2, 10) +1; + } + curr = tmp2; + left -= tmp2-tmp; + + req->offset_start = offset_start; + req->offset_end = offset_end; + + SKIP_LINE(curr,left); + Dprintk("Range field: %s [%d] (%d-%d).\n", tmp, curr-tmp, offset_start, offset_end); + break; + } + + case 'U': + PARSE_STR_FIELD("U","ser-Agent: ", + user_agent_str,user_agent_len); + UNKNOWN_FIELD; + + default: + UNKNOWN_FIELD; + } + PRINT_MESSAGE_LEFT; + } +out: + /* + * POST data. + */ + if ((req->method == METHOD_POST) && req->content_len) { + PRINT_MESSAGE_LEFT; + if (curr + req->content_len > message + total_len) + GOTO_INCOMPLETE; + req->post_data_str = curr; + req->post_data_len = req->content_len; + curr += req->content_len; + left -= req->content_len; + Dprintk("POST-ed data: {%s}\n", req->post_data_str); + } + + switch (req->method) { + default: + GOTO_REDIR; + case METHOD_GET: + case METHOD_HEAD: + case METHOD_POST: + case METHOD_PUT: + ; + } + +#define TUX_SCHEME "http://" +#define TUX_SCHEME_LEN (sizeof(TUX_SCHEME)-1) + + if (!memcmp(req->objectname, TUX_SCHEME, TUX_SCHEME_LEN)) { + + /* http://user:password@host:port/object */ + + const char *head, *tail, *end, *host, *port; + int host_len, objectname_len; + + head = req->objectname + TUX_SCHEME_LEN; + end = req->objectname + req->objectname_len; + + tail = memchr(head, '/', end - head); + if (!tail) + GOTO_REDIR; + host = memchr(head, '@', tail - head); + if (!host) + host = head; + else + host++; + if (!*host) + GOTO_REDIR; + port = memchr(host, ':', tail - host); + if (port) + host_len = port - host; + else + host_len = tail - host; + if (host_len >= MAX_HOST_LEN) + GOTO_REDIR; + memcpy(req->host, host, host_len); + req->host_len = host_len; + req->host[host_len] = 0; + + if (*tail != '/') + TUX_BUG(); + + req->uri_str = tail; + req->uri_len = end - tail; + + tail++; + while (*tail == '/') + tail++; + + objectname_len = end - tail; + memcpy(req->objectname, tail, objectname_len); + req->objectname_len = objectname_len; + req->objectname[objectname_len] = 0; + } else + if (req->uri_str[0] != '/') + GOTO_REDIR; + + if ((req->version == HTTP_1_1) && !req->host_len) + GOTO_REDIR; + if (req->objectname[0] == '/') + GOTO_REDIR; + /* + * Lets make sure nobody plays games with the host + * header in a virtual hosting environment: + */ + if (req->virtual && req->host_len) { + if (memchr(req->host, '/', req->host_len)) + GOTO_REDIR; + if (req->host[0] == '.') { + if (req->host_len == 1) + GOTO_REDIR; + if ((req->host_len == 2) && (req->host[0] == '.')) + GOTO_REDIR; + } + } + /* + * From this point on the request is for the main TUX engine: + */ + Dprintk("ok, request accepted.\n"); + + if (req->keep_alive) { + req->nr_keepalives++; + if (req->nr_keepalives == -1) + req->nr_keepalives--; + INC_STAT(nr_keepalive_reqs); + } else + INC_STAT(nr_nonkeepalive_reqs); + INC_STAT(keepalive_hist[req->nr_keepalives]); + + PRINT_MESSAGE_LEFT; + req->parsed_len = curr-message; + if (req->dentry) + TUX_BUG(); + req->virtual = tux_virtual_server; + if (req->virtual) + add_tux_atom(req, http_lookup_vhost); + else { + req->docroot_dentry = dget(req->proto->main_docroot.dentry); + req->docroot_mnt = mntget(req->proto->main_docroot.mnt); + add_tux_atom(req, http_process_message); + } + + return req->parsed_len; + +incomplete_message: + Dprintk("incomplete message!\n"); + PRINT_MESSAGE_LEFT; + + return 0; + +error: + if (total_len > 0) + req->parsed_len = total_len; + else + req->parsed_len = 0; + PRINT_MESSAGE_LEFT; + if (tux_TDprintk) { + TDprintk("redirecting message to secondary server.\n"); + print_req(req); + } + return -1; +} + +static int lookup_url (tux_req_t *req, const unsigned int flag) +{ + /* + * -1 : no previous checks made + * 0 : previous check failed, do not check farther, + * 1 : previous check successed, check farther + */ + int not_modified = -1; + int perm = 0; + struct dentry *dentry = NULL; + struct vfsmount *mnt = NULL; + struct inode *inode; + const char *filename; + + /* + * Do not do any etag or last_modified header checking + * if both unset. + */ + if (!tux_generate_etags && !tux_generate_last_mod) + not_modified = 0; + +repeat_lookup: + if (req->dentry) + TUX_BUG(); + + filename = req->objectname; + Dprintk("will look up {%s} (%d)\n", filename, req->objectname_len); + Dprintk("current->fsuid: %d, current->fsgid: %d, ngroups: %d\n", + current->fsuid, current->fsgid, current->group_info->ngroups); + + dentry = tux_lookup(req, filename, flag, &mnt); + +#define INDEX "/index.html" + + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) + goto cachemiss; + + if (tux_http_dir_indexing && (req->lookup_dir == 1)) { + // undo the index.html appending: + req->objectname_len -= sizeof(INDEX)-1; + req->objectname[req->objectname_len] = 0; + req->lookup_dir = 2; + goto repeat_lookup; + } + if (!req->lookup_404) { + int len = strlen(tux_404_page); + memcpy(req->objectname, tux_404_page, len); + req->objectname[len] = 0; + req->objectname_len = len; + req->lookup_404 = 1; + req->status = 404; + goto repeat_lookup; + } + TDprintk("abort - lookup error.\n"); + goto abort; + } + + Dprintk("SUCCESS, looked up {%s} == dentry %p (inode %p, count %d.)\n", filename, dentry, dentry->d_inode, atomic_read(&dentry->d_count)); + inode = dentry->d_inode; + + /* + * At this point we have a real, non-negative dentry. + */ + perm = tux_permission(inode); + + if ((perm < 0) || (!S_ISDIR(dentry->d_inode->i_mode) + && !S_ISREG(dentry->d_inode->i_mode))) { + Dprintk("FAILED trusted dentry %p permission %d.\n", dentry, perm); + req->status = 403; + goto abort; + } + if ((req->lookup_dir != 2) && S_ISDIR(dentry->d_inode->i_mode)) { + if (req->lookup_dir || (req->objectname_len + + sizeof(INDEX) >= MAX_OBJECTNAME_LEN)) { + req->status = 403; + goto abort; + } + if (req->objectname_len && (req->objectname[req->objectname_len-1] != '/')) { + dput(dentry); + mntput(mnt); + req->lookup_dir = 0; + return 2; + } + memcpy(req->objectname + req->objectname_len, + INDEX, sizeof(INDEX)); + req->objectname_len += sizeof(INDEX)-1; + req->lookup_dir = 1; + dput(dentry); + mntput(mnt); + mnt = NULL; + dentry = NULL; + goto repeat_lookup; + } + if (tux_max_object_size && (inode->i_size > tux_max_object_size)) { + TDprintk("too big object, %Ld bytes.\n", inode->i_size); + req->status = 403; + goto abort; + } + req->total_file_len = inode->i_size; + req->mtime = inode->i_mtime.tv_sec; + + { + loff_t num = req->total_file_len; + int nr_digits = 0; + unsigned long modulo; + char * etag_p = req->etag; + char digits [30]; + + do { + modulo = do_div(num, 10); + digits[nr_digits++] = '0' + modulo; + } while (num); + + req->lendigits = nr_digits; + req->etaglen = nr_digits; + + while (nr_digits) + *etag_p++ = digits[--nr_digits]; + + *etag_p++ = '-'; + num = req->mtime; + nr_digits = 0; + + do { + digits[nr_digits++] = 'a' + num % 16; + num /= 16; + } while (num); + req->etaglen += nr_digits+1; + while (nr_digits) + *etag_p++ = digits[--nr_digits]; + *etag_p = 0; + } + + if ((req->if_none_match_len >= req->etaglen) && (abs(not_modified) == 1)) { + + char * etag_p = req->etag; + const char * match_p = req->if_none_match_str; + int pos = req->etaglen - 1; + int matchpos = req->etaglen - 1; + + do { + while (etag_p[matchpos--] == match_p[pos--]) + if (matchpos < 0) + break; + if (matchpos < 0) + pos = req->if_none_match_len; + else { + if (match_p[pos+1] == ',') + pos += req->etaglen + 2; + else + pos += req->etaglen-matchpos; + matchpos = req->etaglen - 1; + } + } while (pos < req->if_none_match_len); + + if (matchpos < 0) { + not_modified = 1; + TDprintk("Etag matched.\n"); + } else + not_modified = 0; + } + + if ((req->if_modified_since_len >= 24) && (abs(not_modified) == 1)) { + if (parse_time(req->if_modified_since_str, req->if_modified_since_len) >= req->mtime ) { + not_modified = 1; + Dprintk("Last-Modified matched.\n"); + } else + not_modified = 0; + } + + if (not_modified == 1) { + req->status = 304; + goto abort; + } + + Dprintk("looked up cached dentry %p, (count %d.)\n", dentry, dentry ? atomic_read(&dentry->d_count) : -1 ); + + url_hist_hit(req->total_file_len); +out: + install_req_dentry(req, dentry, mnt); + req->lookup_dir = 0; + return 0; + +cachemiss: + return 1; + +abort: + if (dentry) { + if (!IS_ERR(dentry)) + dput(dentry); + dentry = NULL; + } + if (mnt) { + if (!IS_ERR(mnt)) + mntput(mnt); + mnt = NULL; + } +#ifdef CONFIG_TUX_DEBUG + if (!not_modified) { + TDprintk("req %p has lookup errors!\n", req); + if (tux_TDprintk) + print_req(req); + } +#endif + req_err(req); + goto out; +} + +int handle_gzip_req (tux_req_t *req, unsigned int flags) +{ + char *curr = req->objectname + req->objectname_len; + struct dentry *dentry; + struct vfsmount *mnt = NULL; + struct inode *inode, *orig_inode; + loff_t size, orig_size; + + *curr++ = '.'; + *curr++ = 'g'; + *curr++ = 'z'; + *curr++ = 0; + req->objectname_len += 3; + + dentry = tux_lookup(req, req->objectname, flags, &mnt); + + req->objectname_len -= 3; + req->objectname[req->objectname_len] = 0; + + if (!dentry) + return 0; + if (IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + release_req_dentry(req); + return 1; + } + return 0; + } + + inode = dentry->d_inode; + size = inode->i_size; + orig_inode = req->dentry->d_inode; + orig_size = orig_inode->i_size; + + if (!tux_permission(inode) + && (size < orig_size) + && (inode->i_mtime.tv_sec >= orig_inode->i_mtime.tv_sec)) { + + release_req_dentry(req); + install_req_dentry(req, dentry, mnt); + req->total_file_len = req->output_len = size; + Dprintk("content WILL be gzipped!\n"); + req->content_gzipped = 1; + } else { + dput(dentry); + mntput(mnt); + } + + return 0; +} + +static DEFINE_SPINLOCK(mimetypes_lock); + +static LIST_HEAD(mimetypes_head); + +static mimetype_t default_mimetype = { .type = "text/plain", .type_len = 10, .expire_str = "", .expire_str_len = 0 }; + +#define MAX_MIMETYPE_LEN 128 +#define MAX_CACHE_CONTROL_AGE_LEN 30 + +void add_mimetype (char *new_ext, char *new_type, char *new_expire) +{ + int type_len = strlen(new_type); + int ext_len = strlen(new_ext); + int expire_len = strlen(new_expire); + mimetype_t *mime; + char *ext, *type, *expire; + + if (type_len > MAX_MIMETYPE_LEN) + type_len = MAX_MIMETYPE_LEN; + if (ext_len > MAX_URI_LEN) + ext_len = MAX_URI_LEN; + if (expire_len > MAX_CACHE_CONTROL_AGE_LEN) + expire_len = MAX_CACHE_CONTROL_AGE_LEN; + + mime = tux_kmalloc(sizeof(*mime)); + memset(mime, 0, sizeof(*mime)); + ext = tux_kmalloc(ext_len + 1); + type = tux_kmalloc(type_len + 1); + expire = tux_kmalloc(expire_len + 1); + + strncpy(ext, new_ext, ext_len); + strncpy(type, new_type, type_len); + strncpy(expire, new_expire, expire_len); + + // in case one of the above parameters was too long : + + ext[ext_len] = '\0'; + type[type_len] = '\0'; + expire[expire_len] = '\0'; + + mime->ext = ext; + mime->ext_len = ext_len; + + mime->type = type; + mime->type_len = type_len; + + mime->expire_str = expire; + mime->expire_str_len = expire_len; + + mime->special = NORMAL_MIME_TYPE; + if (!strcmp(type, "TUX/redirect")) + mime->special = MIME_TYPE_REDIRECT; + if (!strcmp(type, "TUX/CGI")) + mime->special = MIME_TYPE_CGI; + if (!strcmp(type, "TUX/module")) + mime->special = MIME_TYPE_MODULE; + + spin_lock(&mimetypes_lock); + list_add(&mime->list, &mimetypes_head); + spin_unlock(&mimetypes_lock); +} + +static inline int ext_matches (char *file, int len, char *ext, int extlen) +{ + int i; + char *tmp = file + len-1; + char *tmp2 = ext + extlen-1; + + if (len < extlen) + return 0; + + for (i = 0; i < extlen; i++) { + if (*tmp != *tmp2) + return 0; + tmp--; + tmp2--; + } + return 1; +} + +/* + * Overhead is not a problem, we cache the MIME type + * in the dentry. + */ +static mimetype_t * lookup_mimetype (tux_req_t *req) +{ + char *objectname = req->objectname; + int len = req->objectname_len; + mimetype_t *mime = NULL; + struct list_head *head, *tmp, *tmp1, *tmp2, *tmp3; + + if (!memchr(objectname, '.', len)) + goto out; + + spin_lock(&mimetypes_lock); + head = &mimetypes_head; + tmp = head->next; + + while (tmp != head) { + mime = list_entry(tmp, mimetype_t, list); + if (ext_matches(objectname, len, mime->ext, mime->ext_len)) { + /* + * Percolate often-used mimetypes up: + */ + if (tmp->prev != &mimetypes_head) { + tmp1 = tmp; + tmp2 = tmp->prev; + tmp3 = tmp->prev->prev; + list_del(tmp1); + list_del(tmp2); + list_add(tmp, tmp3); + list_add(tmp2, tmp); + } + break; + } else + mime = NULL; + tmp = tmp->next; + } + spin_unlock(&mimetypes_lock); + +out: + if (!mime) + mime = &default_mimetype; + return mime; +} + +void free_mimetypes (void) +{ + struct list_head *head, *tmp, *next; + mimetype_t *mime; + + spin_lock(&mimetypes_lock); + head = &mimetypes_head; + tmp = head->next; + + while (tmp != head) { + next = tmp->next; + mime = list_entry(tmp, mimetype_t, list); + list_del(tmp); + + kfree(mime->ext); + mime->ext = NULL; + kfree(mime->type); + mime->type = NULL; + kfree(mime); + + tmp = next; + } + spin_unlock(&mimetypes_lock); +} + +/* + * Various constant HTTP responses: + */ + +static const char forbidden[] = + "HTTP/1.1 403 Forbidden\r\n" + "Connection: Keep-Alive\r\n" \ + "Content-Length: 24\r\n\r\n" + "<HTML> Forbidden </HTML>"; + +static const char not_found[] = + "HTTP/1.1 404 Not Found\r\n" + "Connection: Keep-Alive\r\n" \ + "Content-Length: 29\r\n\r\n" + "<HTML> Page Not Found </HTML>"; + +#define NOTMODIFIED_1 \ + "HTTP/1.1 304 Not Modified\r\n" \ + "Connection: Keep-Alive\r\n" \ + "Date: " + +#define NOTMODIFIED_1_LEN (sizeof(NOTMODIFIED_1) - 1) + +#define NOTMODIFIED_2 \ + "\r\nETag: \"" + +#define NOTMODIFIED_2_LEN (sizeof(NOTMODIFIED_2) - 1) + +#define NOTMODIFIED_3 \ + "\"\r\n\r\n" + +#define NOTMODIFIED_3_LEN (sizeof(NOTMODIFIED_3) - 1) + +#define REDIRECT_1 \ + "HTTP/1.1 301 Moved Permanently\r\n" \ + "Location: http://" + +#define REDIRECT_1_LEN (sizeof(REDIRECT_1) - 1) + +#define REDIRECT_2 \ + "/\r\nContent-Length: 36\r\n" \ + "Connection: Keep-Alive\r\n" \ + "Content-Type: text/html\r\n\r\n" \ + "<HTML> 301 Moved Permanently </HTML>" + +#define REDIRECT_2_LEN (sizeof(REDIRECT_2) - 1) + +void send_async_err_forbidden (tux_req_t *req) +{ + send_async_message(req, forbidden, 403, 1); +} + +void send_async_err_not_found (tux_req_t *req) +{ + send_async_message(req, not_found, 404, 1); +} + +static void send_ret_notmodified (tux_req_t *req) +{ + char *buf; + int size; + + size = NOTMODIFIED_1_LEN + DATE_LEN - 1 + NOTMODIFIED_2_LEN + req->etaglen + NOTMODIFIED_3_LEN; + buf = get_abuf(req, size); + memcpy(buf, NOTMODIFIED_1, NOTMODIFIED_1_LEN); + buf += NOTMODIFIED_1_LEN; + memcpy(buf, tux_date, DATE_LEN-1); + buf += DATE_LEN-1; + memcpy(buf, NOTMODIFIED_2, NOTMODIFIED_2_LEN); + buf += NOTMODIFIED_2_LEN; + memcpy(buf, &req->etag, req->etaglen); + buf += req->etaglen; + memcpy(buf, NOTMODIFIED_3, NOTMODIFIED_3_LEN); + buf += NOTMODIFIED_3_LEN; + + req->status = 304; + send_abuf(req, size, MSG_DONTWAIT); + add_req_to_workqueue(req); +} + +static void send_ret_redirect (tux_req_t *req, int cachemiss) +{ + char *buf; + unsigned int size; + unsigned int uts_len = 0; + + size = REDIRECT_1_LEN; + if (req->host_len) + size += req->host_len; + else { + down_read(&uts_sem); + uts_len = strlen(system_utsname.nodename); + size += uts_len; + } + if (req->objectname[0] != '/') + size++; + size += req->objectname_len; + size += REDIRECT_2_LEN; + + if (size > PAGE_SIZE) { + req->error = TUX_ERROR_CONN_CLOSE; + zap_request(req, cachemiss); + return; + } + + buf = get_abuf(req, size); + + memcpy(buf, REDIRECT_1, REDIRECT_1_LEN); + buf += REDIRECT_1_LEN; + + Dprintk("req %p, host: %s, host_len: %d.\n", req, req->host, req->host_len); + if (req->host_len) { + memcpy(buf, req->host, req->host_len); + buf += req->host_len; + } else { + memcpy(buf, system_utsname.nodename, uts_len); + up_read(&uts_sem); + buf += uts_len; + } + if (req->objectname[0] != '/') { + buf[0] = '/'; + buf++; + } + + memcpy(buf, req->objectname, req->objectname_len); + buf += req->objectname_len; + + memcpy(buf, REDIRECT_2, REDIRECT_2_LEN); + buf += REDIRECT_2_LEN; + + req->status = 301; + send_abuf(req, size, MSG_DONTWAIT); + add_req_to_workqueue(req); +} + +static void http_got_request (tux_req_t *req) +{ + req->host[0] = 0; + req->host_len = 0; + add_tux_atom(req, parse_request); + add_req_to_workqueue(req); +} + + +tux_attribute_t * lookup_tux_attribute (tux_req_t *req) +{ + tux_attribute_t *attr; + struct inode *inode; + mimetype_t *mime; + + attr = tux_kmalloc(sizeof(*attr)); + memset(attr, 0, sizeof(*attr)); + + mime = lookup_mimetype(req); + + inode = req->dentry->d_inode; + if (!inode->i_uid && !inode->i_gid) { + if (mime->special == MIME_TYPE_MODULE) { + attr->tcapi = lookup_tuxmodule(req->objectname); + if (!attr->tcapi) { + req_err(req); + mime = &default_mimetype; + } + } + } else { + if (mime->special && (mime->special != MIME_TYPE_REDIRECT)) + mime = &default_mimetype; + } + attr->mime = mime; + + return attr; +} + +static void handle_range(tux_req_t *req) +{ + if (req->if_range_len) { + time_t range_time; + + range_time = parse_time(req->if_range_str, req->if_range_len); + + /* + * If the file is newer then we send the whole file. + */ + if (range_time < req->mtime ) + goto out_no_range; + } + /* if no offset_end was specified then default to 'end of file': */ + if (!req->offset_end) + req->offset_end = req->total_file_len; + /* + * Sanity checks: + * + * - is the range between 0...file_len-1 ? + * - is offset_end after offset_start? + * + * (note that offset_end is higher by 1) + */ + if ((req->offset_end > req->total_file_len) || + (req->offset_start >= req->total_file_len) || + (req->offset_end <= req->offset_start)) + goto out_no_range; + /* + * If the range is 0...file_len-1 then send the whole file: + */ + if (!req->offset_start && (req->offset_end == req->total_file_len)) + goto out_no_range; + + /* ok, the range is valid, use it: */ + + req->output_len = req->offset_end - req->offset_start; + req->in_file->f_pos = req->offset_start; + return; + +out_no_range: + req->offset_start = 0; + req->offset_end = 0; +} + +static void http_pre_header (tux_req_t *req, int push); +static void http_post_header (tux_req_t *req, int cachemiss); +static void http_send_body (tux_req_t *req, int cachemiss); + +#define DIRLIST_HEAD_1 "\ +<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\ +<HTML><HEAD><TITLE>Index of %s</TITLE></HEAD><BODY>\ +<H1>Index of %s </H1><PRE><HR>\n%s" + +#define DIRLIST_HEAD_2 "\ +<IMG SRC=\"/icons/back.gif\"ALT=\"[DIR]\"> <A HREF=\"../\">Parent Directory</A>\n" + +#define DIRLIST_HEAD_SIZE (sizeof(DIRLIST_HEAD_1) + sizeof(DIRLIST_HEAD_2)) + +static void http_dirlist_head (tux_req_t *req, int cachemiss) +{ + char *buf1, *buf2, *path; + int len; + + buf1 = (char *)__get_free_page(GFP_KERNEL); + buf2 = (char *)__get_free_page(GFP_KERNEL); + if (!buf1 || !buf2) + goto out; + path = tux_print_path(req, req->dentry, req->mnt, buf1, PAGE_SIZE); + if (path[0] == '/' && path[1] == '/' && !path[3]) + path = "/"; + if (2*strlen(path) + DIRLIST_HEAD_SIZE >= PAGE_SIZE) + goto out; + len = sprintf(buf2, DIRLIST_HEAD_1, path, path, req->dentry == req->docroot_dentry ? "" : DIRLIST_HEAD_2); + __send_async_message(req, buf2, 200, len, 0); + +out: + if (buf1) + free_page((unsigned long)buf1); + if (buf2) + free_page((unsigned long)buf2); +} + +#define DIRLIST_TAIL "\ +</PRE><HR><ADDRESS><IMG SRC=\"/icons/tuxlogo.gif\"ALIGN=\"MIDDLE\"ALT=\"[TUX]\">Powered by Linux/TUX 3.0</ADDRESS>\n</BODY></HTML>" + +static void http_dirlist_tail (tux_req_t *req, int cachemiss) +{ + __send_async_message(req, DIRLIST_TAIL, 200, sizeof(DIRLIST_TAIL)-1, 1); +} + +static void http_dirlist (tux_req_t *req, int cachemiss) +{ + int head = (req->method == METHOD_HEAD); + + req->lookup_dir = 3; + clear_keepalive(req); + if (!head) { + add_tux_atom(req, http_dirlist_tail); + add_tux_atom(req, list_directory); + add_tux_atom(req, http_dirlist_head); + } + http_pre_header(req, head); + add_req_to_workqueue(req); +} + +static char *host_path_hash(tux_req_t *req, char *tmp) +{ + if (req->host_len < 2) + return NULL; + + switch (mass_hosting_hash) { + default: + case 0: + return req->host; + case 1: + + // www.ABCDEFG.com => A/ABCDEFG.com + + tmp[0] = req->host[0]; + tmp[1] = '/'; + memcpy(tmp + 2, req->host, req->host_len); + tmp[req->host_len + 2] = 0; + + return tmp; + case 2: + // www.ABCDEFG.com => A/AB/ABCDEFG.com + + tmp[0] = req->host[0]; + tmp[1] = '/'; + tmp[2] = req->host[0]; + tmp[3] = req->host[1]; + tmp[4] = '/'; + memcpy(tmp + 5, req->host, req->host_len); + tmp[req->host_len + 5] = 0; + + return tmp; + case 3: + // www.ABCDEFG.com => A/AB/ABC/ABCDEFG.com + + tmp[0] = req->host[0]; + tmp[1] = '/'; + tmp[2] = req->host[0]; + tmp[3] = req->host[1]; + tmp[4] = '/'; + tmp[5] = req->host[0]; + tmp[6] = req->host[1]; + tmp[7] = req->host[2]; + tmp[8] = '/'; + memcpy(tmp + 9, req->host, req->host_len); + tmp[req->host_len + 9] = 0; + + return tmp; + } +} + +static struct dentry * vhost_lookup (tux_req_t *req, struct nameidata* base, struct vfsmount **mnt) +{ + struct dentry *dentry = NULL; + // 255.255.255.255 + char ip [3+1+3+1+3+1+3 + 2]; + + if (req->virtual >= TUX_VHOST_IP) { + sprintf(ip, "%d.%d.%d.%d", + NIPQUAD(inet_sk(req->sock->sk)->rcv_saddr)); + dentry = __tux_lookup (req, ip, base, mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) + return dentry; + base->dentry = dget(req->proto->main_docroot.dentry); + base->mnt = mntget(req->proto->main_docroot.mnt); + goto lookup_default; + } + if (req->virtual == TUX_VHOST_IP) + goto done; + + // fall through in mixed mode: + } + + if (!req->host_len) { +lookup_default: + *mnt = NULL; + dentry = __tux_lookup (req, tux_default_vhost, base, mnt); + } else { + char tmp [MAX_HOST_LEN*2]; + char *host_path; + + host_path = host_path_hash(req, tmp); + Dprintk("host path hash returned: {%s}\n", host_path); + + dentry = NULL; + if (host_path) { + *mnt = NULL; + dentry = __tux_lookup (req, host_path, base, mnt); + } + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) + return dentry; + base->dentry = dget(req->proto->main_docroot.dentry); + base->mnt = mntget(req->proto->main_docroot.mnt); + if (req->virtual >= TUX_VHOST_IP) { + *mnt = NULL; + dentry = __tux_lookup (req, ip, base, mnt); + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) + return dentry; + base->dentry = dget(req->proto->main_docroot.dentry); + base->mnt = mntget(req->proto->main_docroot.mnt); + } + } + goto lookup_default; + } + } +done: + return dentry; +} + +static void http_lookup_vhost (tux_req_t *req, int cachemiss) +{ + struct dentry *dentry; + struct nameidata base = { }; + struct vfsmount *mnt = NULL; + unsigned int flag = cachemiss ? 0 : LOOKUP_ATOMIC; + + Dprintk("http_lookup_vhost(%p, %d, virtual: %d, host: %s (%d).)\n", req, flag, req->virtual, req->host, req->host_len); + + base.flags = LOOKUP_FOLLOW|flag; + base.last_type = LAST_ROOT; + base.dentry = dget(req->proto->main_docroot.dentry); + base.mnt = mntget(req->proto->main_docroot.mnt); + + dentry = vhost_lookup(req, &base, &mnt); + + Dprintk("looked up dentry %p.\n", dentry); + + if (dentry && !IS_ERR(dentry) && !dentry->d_inode) + TUX_BUG(); + + if (!dentry || IS_ERR(dentry)) { + if (PTR_ERR(dentry) == -EWOULDBLOCKIO) { + add_tux_atom(req, http_lookup_vhost); + queue_cachemiss(req); + return; + } + goto abort; + } + + req->docroot_dentry = dentry; + req->docroot_mnt = mnt; + + add_tux_atom(req, http_process_message); + add_req_to_workqueue(req); + return; +abort: + if (dentry) { + if (!IS_ERR(dentry)) + dput(dentry); + dentry = NULL; + } + if (mnt) { + if (!IS_ERR(mnt)) + mntput(mnt); + mnt = NULL; + } + req_err(req); + add_req_to_workqueue(req); +} + +static void http_process_message (tux_req_t *req, int cachemiss) +{ + tux_attribute_t *attr; + int missed; + unsigned int lookup_flag = cachemiss ? 0 : LOOKUP_ATOMIC; + + Dprintk("handling req %p, cachemiss: %d.\n", req, cachemiss); + + /* + * URL redirection support - redirect all valid requests + * to the first userspace module. + */ + if (tux_all_userspace) { + tcapi_template_t *tcapi = get_first_usermodule(); + if (tcapi) { + req->usermode = 1; + req->usermodule_idx = tcapi->userspace_id; + goto usermode; + } + } + missed = lookup_url(req, lookup_flag); + if (missed == 2) { + if (req->query_str) { + req->error = TUX_ERROR_REDIRECT; + goto error; + } + send_ret_redirect(req, cachemiss); + return; + } + if (req->error) + goto error; + if (missed) { +cachemiss: + if (cachemiss) + TUX_BUG(); + Dprintk("uncached request.\n"); + INC_STAT(static_lookup_cachemisses); + if (req->dentry) + TUX_BUG(); + add_tux_atom(req, http_process_message); + queue_cachemiss(req); + return; + } + /* + * HTML directory indexing. + */ + if (S_ISDIR(req->dentry->d_inode->i_mode)) + return http_dirlist(req, cachemiss); + if (!S_ISREG(req->dentry->d_inode->i_mode)) + TUX_BUG(); + + + attr = req->dentry->d_extra_attributes; + if (!attr) { + attr = lookup_tux_attribute(req); + if (!attr) + TUX_BUG(); + req->dentry->d_extra_attributes = attr; + } + if (attr->mime) + Dprintk("using MIME type %s:%s, %d.\n", attr->mime->type, attr->mime->ext, attr->mime->special); + if (attr->tcapi) { + req->usermode = 1; + req->usermodule_idx = attr->tcapi->userspace_id; + if (req->module_dentry) + TUX_BUG(); + req->module_dentry = dget(req->dentry); + release_req_dentry(req); + goto usermode; + } + + switch (attr->mime->special) { + case MIME_TYPE_MODULE: + req->usermode = 1; + goto usermode; + + case MIME_TYPE_REDIRECT: + req->error = TUX_ERROR_REDIRECT; + goto error; + + case MIME_TYPE_CGI: +#ifdef CONFIG_TUX_EXTCGI + Dprintk("CGI request %p.\n", req); + query_extcgi(req); + return; +#endif + + default: + if (req->query_str) { + req->error = TUX_ERROR_REDIRECT; + goto error; + } + } + req->attr = attr; + switch (req->method) { + case METHOD_GET: + case METHOD_HEAD: + break; + default: + req->error = TUX_ERROR_REDIRECT; + goto error; + } + if (req->usermode) + TUX_BUG(); + + req->output_len = req->total_file_len; + /* + * Do range calculations. + */ + if (req->offset_end || req->offset_start) + handle_range(req); + + if (req->may_send_gzip && !req->offset_start && !req->offset_end) { + if (handle_gzip_req(req, lookup_flag)) + goto cachemiss; + if ((tux_compression >= 2) && !req->content_gzipped) + req->content_gzipped = 2; + } + if (req->parsed_len) + trunc_headers(req); + + if (req->error) + goto error; + + add_tux_atom(req, http_send_body); + add_tux_atom(req, http_post_header); + + http_pre_header(req, req->method == METHOD_HEAD); + + add_req_to_workqueue(req); + return; + +error: + if (req->error) + zap_request(req, cachemiss); + return; + +usermode: + add_req_to_workqueue(req); +} + +static void http_post_header (tux_req_t *req, int cachemiss) +{ +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = req->output_len; +#endif + req->bytes_sent = 0; // data comes now. + + add_req_to_workqueue(req); +} + +static void http_send_body (tux_req_t *req, int cachemiss) +{ + int ret; + + Dprintk("SEND req %p <%p> (sock %p, sk %p) (keepalive: %d, status: %d)\n", req, __builtin_return_address(0), req->sock, req->sock->sk, req->keep_alive, req->status); + + SET_TIMESTAMP(req->output_timestamp); + + if (req->error) { +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + req->in_file->f_pos = 0; + /* + * We are in the middle of a file transfer, + * zap it immediately: + */ + TDprintk("req->error = TUX_ERROR_CONN_CLOSE.\n"); + req->error = TUX_ERROR_CONN_CLOSE; + zap_request(req, cachemiss); + return; + } + +repeat: + ret = 0; + if (!req->status) + req->status = 200; + if (req->method != METHOD_HEAD) { + ret = generic_send_file(req, req->sock, cachemiss); + Dprintk("body send-file returned: %d.\n", ret); + } else { +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + } + + switch (ret) { + case -5: + add_tux_atom(req, http_send_body); + output_timeout(req); + break; + case -4: + add_tux_atom(req, http_send_body); + if (add_output_space_event(req, req->sock)) { + del_tux_atom(req); + goto repeat; + } + break; + case -3: + INC_STAT(static_sendfile_cachemisses); + add_tux_atom(req, http_send_body); + queue_cachemiss(req); + break; + case -1: + break; + default: + req->in_file->f_pos = 0; + add_req_to_workqueue(req); + break; + } +} + +#define DEFAULT_DATE "Wed, 01 Jan 1970 00:00:01 GMT" + +char tux_date [DATE_LEN] = DEFAULT_DATE; + +/* + * HTTP header + */ + +#define HEADER_PART1A \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Type: " + +#define HEADER_PART1B \ + "HTTP/1.1 200 OK" + +#define HEADER_PART1AP \ + "HTTP/1.1 206 Partial Content\r\n" \ + "Content-Type: " + +#define HEADER_PART1BP \ + "HTTP/1.1 206 Partial Content" + +#define HEADER_PART1C \ + "HTTP/1.1 404 Page Not Found\r\n" \ + "Content-Type: " + +#define HEADER_PART1D \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/html\r\n" \ + "Connection: close\r\n" + +#define HEADER_PART2_keepalive "\r\nConnection: Keep-Alive\r\nDate: " + +#define HEADER_PART2_close "\r\nConnection: close\r\nDate: " + +#define HEADER_PART2_none "\r\nDate: " + +// date "%s" + +#define HEADER_PART3A "\r\nContent-Encoding: gzip" +#define HEADER_PART3BX "\r\nContent-Length: " + +/* + * Please acknowledge our hard work by not changing this define, or + * at least please acknowledge us by leaving "TUX/2.0 (Linux)" in + * the ID string. Thanks! :-) + */ +#define HEADER_PART3BY "\r\nServer: TUX/2.0 (Linux)\r\nContent-Length: " +#define HEADER_PART3C "\r\nETag: \"" +#define HEADER_PART3ACC "\r\nAccept-Ranges: bytes" +#define HEADER_PART3L "\r\nLast-Modified: " +#define HEADER_PART3P "\r\nContent-Range: bytes " +#define HEADER_PART3CA "\r\nCache-Control: max-age=" +#define HEADER_PART4 "\r\n\r\n" + +#define MAX_OUT_HEADER_LEN (sizeof(HEADER_PART1AP) + MAX_MIMETYPE_LEN + \ + sizeof(HEADER_PART2_keepalive) + DATE_LEN + \ + sizeof(HEADER_PART3A) + sizeof(HEADER_PART3BY) + \ + 12 + sizeof(HEADER_PART3C) + 21 + sizeof(HEADER_PART3L) + \ + sizeof(HEADER_PART3P) + 32 + \ + DATE_LEN + sizeof(HEADER_PART4) + sizeof(tux_extra_html_header) \ + + sizeof(HEADER_PART3CA) + MAX_CACHE_CONTROL_AGE_LEN) + +static void http_pre_header (tux_req_t *req, int head) +{ + int partial = req->offset_start | req->offset_end; + unsigned long flags; + char *buf, *curr; + mimetype_t *mime = NULL; + int size; + + + if (MAX_OUT_HEADER_LEN > PAGE_SIZE) + TUX_BUG(); + if ((req->attr && req->attr->tcapi) || req->usermode) + TUX_BUG(); + +#define COPY_STATIC_PART(nr,curr) \ + do { \ + memcpy(curr, HEADER_PART##nr, sizeof(HEADER_PART##nr)-1); \ + curr += sizeof(HEADER_PART##nr)-1; \ + } while (0) + + buf = curr = get_abuf(req, MAX_OUT_HEADER_LEN); + + if (req->lookup_dir) { + COPY_STATIC_PART(1D, curr); + goto dir_next; + } + mime = req->attr->mime; + if (!mime) + TUX_BUG(); + + if (req->status == 404) { + COPY_STATIC_PART(1C, curr); + memcpy(curr, mime->type, mime->type_len); + curr += mime->type_len; + } else { + if (tux_noid && (mime == &default_mimetype)) { + if (partial) + COPY_STATIC_PART(1BP, curr); + else + COPY_STATIC_PART(1B, curr); + } else { + if (partial) + COPY_STATIC_PART(1AP, curr); + else + COPY_STATIC_PART(1A, curr); + memcpy(curr, mime->type, mime->type_len); + curr += mime->type_len; + } + } + + if (tux_generate_cache_control && mime->expire_str_len) { + COPY_STATIC_PART(3CA, curr); + memcpy(curr, mime->expire_str, mime->expire_str_len); + curr += mime->expire_str_len; + } + + if (req->keep_alive /* && (req->version == HTTP_1_0) */) + COPY_STATIC_PART(2_keepalive, curr); + else if (!req->keep_alive && (req->version == HTTP_1_1)) + COPY_STATIC_PART(2_close, curr); + else + // HTTP/1.0 default means close + COPY_STATIC_PART(2_none, curr); + +dir_next: + memcpy(curr, tux_date, DATE_LEN-1); + curr += DATE_LEN-1; + + if (req->content_gzipped) + COPY_STATIC_PART(3A, curr); + + /* + * Content-Length: + */ + if (!req->lookup_dir) { + if (tux_noid) + COPY_STATIC_PART(3BX, curr); + else + COPY_STATIC_PART(3BY, curr); + + if (partial) + curr += sprintf(curr, "%Ld", req->output_len); + else { + if (req->content_gzipped) + curr += sprintf(curr, "%Ld", + req->total_file_len); + else { + memcpy(curr, &req->etag, req->lendigits); + curr += req->lendigits; + } + } + if (tux_generate_etags && (req->status != 404)) { + COPY_STATIC_PART(3C, curr); + memcpy(curr, &req->etag, req->etaglen); + curr += req->etaglen; + curr[0] = '"'; + curr++; + } + if (tux_generate_last_mod || tux_generate_etags) + COPY_STATIC_PART(3ACC, curr); + } + if (tux_generate_last_mod && (req->status != 404)) { + COPY_STATIC_PART(3L, curr); + last_mod_time(curr, req->mtime); + curr += DATE_LEN-1; + } + if (partial) { + COPY_STATIC_PART(3P, curr); + curr += sprintf(curr, "%Ld-%Ld/%Ld", req->offset_start, + req->offset_end-1, req->total_file_len); + } + COPY_STATIC_PART(4, curr); + /* + * Possibly add an extra HTML header: + */ + if (tux_extra_html_header_size && mime && !strcmp(mime->type, "text/html")) { + unsigned int len = tux_extra_html_header_size; + + memcpy(curr, tux_extra_html_header, len); + curr += len; + } + + size = curr-buf; + +#ifdef CONFIG_TUX_DEBUG + *curr = 0; + Dprintk("{%s} [%d/%d]\n", buf, size, strlen(buf)); +#endif + + flags = MSG_DONTWAIT; + if (!head) + flags |= MSG_MORE; + send_abuf(req, size, flags); +} + +void http_illegal_request (tux_req_t *req, int cachemiss) +{ + if (req->status == 304) + send_ret_notmodified(req); + else { + if (req->status == 403) + send_async_err_forbidden(req); + else + send_async_err_not_found(req); + } +} + +static int http_check_req_err (tux_req_t *req, int cachemiss) +{ + if ((req->sock->sk->sk_state <= TCP_SYN_RECV) && + !tcp_sk(req->sock->sk)->urg_data) + return 0; + Dprintk("http_check_req_err(%p,%d): 1 (state: %d, urg: %d)\n", + req, cachemiss, req->sock->sk->sk_state, + tcp_sk(req->sock->sk)->urg_data); +#ifdef CONFIG_TUX_DEBUG + req->bytes_expected = 0; +#endif + req->in_file->f_pos = 0; + req->error = TUX_ERROR_CONN_CLOSE; + zap_request(req, cachemiss); + + return 1; +} + +#define COPY_STR(str) \ + do { memcpy(tmp, str, sizeof(str)-1); \ + tmp += sizeof(str)-1; } while (0) + +static char * http_print_dir_line (tux_req_t *req, char *tmp, char *d_name, int d_len, int d_type, struct dentry *dentry, struct inode *inode) +{ + int len, spaces; + loff_t size; + + switch (d_type) { + case DT_DIR: + COPY_STR("<IMG SRC=\"/icons/dir.gif\" ALT=\"[DIR]\">"); + break; + case DT_REG: + if ((d_len >= 3) && + (d_name[d_len-3] == '.') && + (d_name[d_len-2] == 'g') && + (d_name[d_len-1] == 'z')) + COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">"); + else + if ((d_len >= 4) && + (d_name[d_len-4] == '.') && + (d_name[d_len-3] == 't') && + (d_name[d_len-2] == 'g') && + (d_name[d_len-1] == 'z')) + COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">"); + else + if ((d_len >= 4) && + (d_name[d_len-4] == '.') && + (d_name[d_len-3] == 't') && + (d_name[d_len-2] == 'x') && + (d_name[d_len-1] == 't')) + COPY_STR("<IMG SRC=\"/icons/text.gif\" ALT=\"[ ]\">"); + else + if ((d_len >= 4) && + (d_name[d_len-4] == '.') && + (d_name[d_len-3] == 'b') && + (d_name[d_len-2] == 'z') && + (d_name[d_len-1] == '2')) + COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">"); + else + if ((d_len >= 4) && + (d_name[d_len-4] == '.') && + (d_name[d_len-3] == 'z') && + (d_name[d_len-2] == 'i') && + (d_name[d_len-1] == 'p')) + COPY_STR("<IMG SRC=\"/icons/compressed.gif\" ALT=\"[ ]\">"); + else + COPY_STR("<IMG SRC=\"/icons/file.gif\" ALT=\"[ ]\">"); + break; + case DT_LNK: + COPY_STR("<IMG SRC=\"/icons/link.gif\" ALT=\"[LNK]\">"); + break; + default: + if (tux_hide_unreadable) + goto out_dput; + COPY_STR("<IMG SRC=\"/icons/unknown.gif\" ALT=\"[ ]\">"); + break; + } + +#define LIST_1 " <A HREF=\"" +#define LIST_2 "\">" +#define LIST_2_DIR "/\">" +#define LIST_3 "</A> " + + COPY_STR(LIST_1); + memcpy(tmp, d_name, d_len); + tmp += d_len; + if (d_type == DT_DIR) + COPY_STR(LIST_2_DIR); + else + COPY_STR(LIST_2); + spaces = 0; + len = d_len; + + if (len > 25) + len = 25; + memcpy(tmp, d_name, len); + tmp += len; + if (len != d_len) { + *tmp++ = '.'; + *tmp++ = '.'; + } else { + if (d_type == DT_DIR) + *tmp++ = '/'; + else + spaces++; + spaces++; + } + COPY_STR(LIST_3); + while (spaces) { + *tmp++ = ' '; + spaces--; + } +#define FILL 25 + if (d_len < FILL) { + memset(tmp, ' ', FILL-d_len); + tmp += FILL-d_len; + } + + tmp += time_unix2ls(inode->i_mtime.tv_sec, tmp); + *tmp++ = ' '; + + if (d_type != DT_REG) { + COPY_STR(" - "); + goto out_size; + } + size = inode->i_size >> 10; + if (size < 1024) { + tmp += sprintf(tmp, "%8Lik ", size); + goto out_size; + } + size >>= 10; + if (size < 1024) { + tmp += sprintf(tmp, "%8LiM ", size); + goto out_size; + } + size >>= 10; + if (size < 1024) { + tmp += sprintf(tmp, "%8LiG ", size); + goto out_size; + } + size >>= 10; + if (size < 1024) { + tmp += sprintf(tmp, "%8LiT ", size); + goto out_size; + } + size >>= 10; + tmp += sprintf(tmp, "%8LiT ", size); + +out_size: + *tmp++ = '\n'; + *tmp = 0; + + return tmp; +out_dput: + return NULL; +} + +tux_proto_t tux_proto_http = { + .defer_accept = 1, + .can_redirect = 1, + .got_request = http_got_request, + .parse_message = parse_http_message, + .illegal_request = http_illegal_request, + .check_req_err = http_check_req_err, + .print_dir_line = http_print_dir_line, + .name = "http", +}; + Index: latest/net/tux/redirect.c =================================================================== --- /dev/null +++ latest/net/tux/redirect.c @@ -0,0 +1,172 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * redirect.c: redirect requests to other server sockets (such as Apache). + */ + +#include <net/tux.h> +#include <linux/module.h> +#include <linux/init.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +static void nop_destructor(struct request_sock *req) +{ +} + +static struct request_sock_ops tux_req = +{ + .destructor = &nop_destructor, +}; + +static int redirect_sock (tux_req_t *req, const int port) +{ + struct socket *sock = req->sock; + struct request_sock *tcpreq; + struct sock *sk, *oldsk; + int err = -1; + + /* + * Look up (optional) listening user-space socket. + */ + local_bh_disable(); + sk = inet_lookup_listener(&tcp_hashinfo, INADDR_ANY, port, 0); + /* + * Look up localhost listeners as well. + */ + if (!sk) { + u32 daddr; + ((unsigned char *)&daddr)[0] = 127; + ((unsigned char *)&daddr)[1] = 0; + ((unsigned char *)&daddr)[2] = 0; + ((unsigned char *)&daddr)[3] = 1; + sk = inet_lookup_listener(&tcp_hashinfo, daddr, port, 0); + } + local_bh_enable(); + + /* No secondary server found */ + if (!sk) + goto out; + if (sk->sk_family != AF_INET) { + sock_put(sk); + goto out; + } + + /* + * Requeue the 'old' socket as an accept-socket of + * the listening socket. This way we can shuffle + * a socket around. Since we've read the input data + * via the non-destructive MSG_PEEK, the secondary + * server can be used transparently. + */ + oldsk = sock->sk; + lock_sock(sk); + + if (sk->sk_state != TCP_LISTEN) + goto out_unlock; + + tcpreq = reqsk_alloc(&tux_req); + if (!tcpreq) + goto out_unlock; + + unlink_tux_socket(req); + + sock->sk = NULL; + sock->state = SS_UNCONNECTED; + + write_lock_irq(&oldsk->sk_callback_lock); + oldsk->sk_socket = NULL; + oldsk->sk_sleep = NULL; + write_unlock_irq(&oldsk->sk_callback_lock); + + tcp_sk(oldsk)->nonagle = 0; + + inet_csk_reqsk_queue_add(sk, tcpreq, oldsk); + + sk->sk_data_ready(sk, 0); + + /* + * It's now completely up to the secondary + * server to handle this request. + */ + if (req->fd != -1) { + tux_close(req->fd); + req->fd = -1; + } else + sock_release(req->sock); + req->sock = NULL; + req->parsed_len = 0; + err = 0; + Dprintk("req %p redirected to secondary server!\n", req); + +out_unlock: + release_sock(sk); + sock_put(sk); +out: + if (err) + Dprintk("NO secondary server for req %p!\n", req); + return err; +} + +void redirect_request (tux_req_t *req, int cachemiss) +{ + if (tux_TDprintk && (req->status != 304)) { + TDprintk("trying to redirect req %p, req->error: %d, req->status: %d.\n", req, req->error, req->status); + print_req(req); + } + + if (cachemiss) + TUX_BUG(); + if (req->error == TUX_ERROR_CONN_CLOSE) + goto out_flush; + if (!req->sock) + TUX_BUG(); + + if (!req->status) + req->status = -1; + if (!req->proto->can_redirect || (req->status == 304) || redirect_sock(req, tux_clientport)) { + if (req->parsed_len) + trunc_headers(req); + req->proto->illegal_request(req, cachemiss); + return; + } else { + if (req->data_sock) + BUG(); + } +out_flush: + clear_keepalive(req); + if (!tux_redirect_logging) + req->status = 0; + flush_request(req, cachemiss); +} + +int init_tux_request_slabs(void) +{ + tux_req.slab = kmem_cache_create("tux-request", + sizeof(struct request_sock), 0, SLAB_HWCACHE_ALIGN, + NULL, NULL); + + return tux_req.slab == NULL; +} + +void free_tux_request_slabs(void) +{ + kmem_cache_destroy(tux_req.slab); +} Index: latest/net/tux/times.c =================================================================== --- /dev/null +++ latest/net/tux/times.c @@ -0,0 +1,392 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * times.c: time conversion routines. + * + * Original time convserion code Copyright (C) 1999 by Arjan van de Ven + */ + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + +#include <linux/time.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/ctype.h> + + +#include "times.h" + +char *dayName[7] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; + +static char *monthName[12] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +char itoa_h[60]={'0','0','0','0','0','0','0','0','0','0', + '1','1','1','1','1','1','1','1','1','1', + '2','2','2','2','2','2','2','2','2','2', + '3','3','3','3','3','3','3','3','3','3', + '4','4','4','4','4','4','4','4','4','4', + '5','5','5','5','5','5','5','5','5','5'}; + +char itoa_l[60]={'0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9', + '0','1','2','3','4','5','6','7','8','9'}; + +int time_unix2ls(time_t zulu, char *buf) +{ + int Y=0,M=0,D=0; + int H=0,Min=0,S=0,WD=0; + int I,I2; + time_t rest, delta; + + if (zulu > xtime.tv_sec) + zulu = xtime.tv_sec; + + I=0; + while (I<TUX_NUMYEARS) { + if (TimeDays[I][0]>zulu) + break; + I++; + } + + Y=--I; + if (I<0) { + Y=0; + goto BuildYear; + } + I2=0; + while (I2<=12) { + if (TimeDays[I][I2]>zulu) + break; + I2++; + } + + M=I2-1; + + rest=zulu - TimeDays[Y][M]; + WD=WeekDays[Y][M]; + D=rest/86400; + rest=rest%86400; + WD+=D; + WD=WD%7; + H=rest/3600; + rest=rest%3600; + Min=rest/60; + rest=rest%60; + S=rest; + +BuildYear: + Y+=TUX_YEAROFFSET; + + + /* Format: Day, 01 Mon 1999 01:01:01 GMT */ + + delta = xtime.tv_sec - zulu; + if (delta > 6*30*24*60) + // "May 23 2000" + return sprintf( buf, "%s %02i %04i", monthName[M], D+1, Y); + else + // "May 23 10:14" + return sprintf( buf, "%s %02i %02i:%02i", + monthName[M], D+1, H, Min); +} + +static int MonthHash[32] = + {0,0,7,0,0,0,0,0,0,0,0,3,0,0,0,2,6,0,5,0,9,8,4,0,0,11,1,10,0,0,0,0}; + +#define is_digit(c) ((c) >= '0' && (c) <= '9') + +static inline int skip_atoi(char **s) +{ + int i=0; + + while (is_digit(**s)) + i = i*10 + *((*s)++) - '0'; + return i; +} + +time_t mimetime_to_unixtime(char *Q) +{ + int Y,M,D,H,Min,S; + unsigned int Hash; + time_t Temp; + char *s,**s2; + + s=Q; + s2=&s; + + if (strlen(s)<30) return 0; + if (s[3]!=',') return 0; + if (s[19]!=':') return 0; + + s+=5; /* Skip day of week */ + D = skip_atoi(s2); /* Day of month */ + s++; + Hash = (char)s[0]+(char)s[2]; + Hash = (Hash<<1) + (char)s[1]; + Hash = (Hash&63)>>1; + M = MonthHash[Hash]; + s+=4; + Y = skip_atoi(s2); /* Year */ + s++; + H = skip_atoi(s2); /* Hour */ + s++; + Min = skip_atoi(s2); /* Minutes */ + s++; + S = skip_atoi(s2); /* Seconds */ + s++; + if ((s[0]!='G')||(s[1]!='M')||(s[2]!='T')) + { + return 0; /* No GMT */ + } + + if (Y<TUX_YEAROFFSET) Y = TUX_YEAROFFSET; + if (Y>TUX_YEAROFFSET+9) Y = TUX_YEAROFFSET+9; + + Temp = TimeDays[Y-TUX_YEAROFFSET][M]; + Temp += D*86400+H*3600+Min*60+S; + + return Temp; +} + +// writes the full http date, corresponding to time_t received + +void last_mod_time(char * curr, const time_t t) +{ + int day, tod, year, wday, mon, hour, min, sec; + + tod = t % 86400; + day = t / 86400; + if (tod < 0) { + tod += 86400; + --day; + } + + hour = tod / 3600; + tod %= 3600; + min = tod / 60; + sec = tod % 60; + + wday = (day + 4) % 7; + if (wday < 0) + wday += 7; + + day -= 11017; + /* day 0 is march 1, 2000 */ + year = 5 + day / 146097; + day = day % 146097; + if (day < 0) { + day += 146097; + --year; + } + /* from now on, day is nonnegative */ + year *= 4; + if (day == 146096) { + year += 3; + day = 36524; + } else { + year += day / 36524; + day %= 36524; + } + year *= 25; + year += day / 1461; + day %= 1461; + year *= 4; + if (day == 1460) { + year += 3; + day = 365; + } else { + year += day / 365; + day %= 365; + } + + day *= 10; + mon = (day + 5) / 306; + day = day + 5 - 306 * mon; + day /= 10; + if (mon >= 10) { + ++year; + mon -= 10; + } else + mon += 2; + + sprintf(curr, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", dayName[wday], + day+1, monthName[mon], year, hour, min, sec); +} + +// writes the full date in ISO8601 format, +// corresponding to time_t received +// example: 20011126224910 + +int mdtm_time(char * curr, const time_t t) +{ + int day, tod, year, wday, mon, hour, min, sec; + + tod = t % 86400; + day = t / 86400; + if (tod < 0) { + tod += 86400; + --day; + } + + hour = tod / 3600; + tod %= 3600; + min = tod / 60; + sec = tod % 60; + + wday = (day + 4) % 7; + if (wday < 0) + wday += 7; + + day -= 11017; + /* day 0 is march 1, 2000 */ + year = 5 + day / 146097; + day = day % 146097; + if (day < 0) { + day += 146097; + --year; + } + /* from now on, day is nonnegative */ + year *= 4; + if (day == 146096) { + year += 3; + day = 36524; + } else { + year += day / 36524; + day %= 36524; + } + year *= 25; + year += day / 1461; + day %= 1461; + year *= 4; + if (day == 1460) { + year += 3; + day = 365; + } else { + year += day / 365; + day %= 365; + } + + day *= 10; + mon = (day + 5) / 306; + day = day + 5 - 306 * mon; + day /= 10; + if (mon >= 10) { + ++year; + mon -= 10; + } else + mon += 2; + + return sprintf(curr, "213 %.4d%.2d%.2d%.2d%.2d%.2d\r\n", + year, mon+1, day+1, hour, min, sec); +} + +static inline int make_num(const char *s) +{ + if (*s >= '0' && *s <= '9') + return 10 * (*s - '0') + *(s + 1) - '0'; + else + return *(s + 1) - '0'; +} + +static inline int make_month(const char *s) +{ + int i; + + for (i = 0; i < 12; i++) + if (!strncmp(monthName[i], s, 3)) + return i+1; + return 0; +} + +time_t parse_time(const char *str, const int str_len) +{ + int hour; + int min; + int sec; + int mday; + int mon; + int year; + + if (str[3] == ',') { + /* Thu, 09 Jan 1993 01:29:59 GMT */ + + if (str_len < 29) + return -1; + + mday = make_num(str+5); + mon = make_month(str + 8); + year = 100 * make_num(str + 12) + make_num(str + 14); + hour = make_num(str + 17); + min = make_num(str + 20); + sec = make_num(str + 23); + } + else { + const char *s; + s = strchr(str, ','); + if (!s || (str_len - (s - str) < 24)) { + /* Wed Jun 9 01:29:59 1993 */ + + if (str_len < 24) + return -1; + + mon = make_month(str+4); + mday = make_num(str+8); + hour = make_num(str+11); + min = make_num(str+14); + sec = make_num(str+17); + year = make_num(str+20)*100 + make_num(str+22); + } + else { + /* Thursday, 10-Jun-93 01:29:59 GMT */ + + mday = make_num(s + 2); + mon = make_month(s + 5); + year = make_num(s + 9) + 1900; + if (year < 1970) + year += 100; + hour = make_num(s + 12); + min = make_num(s + 15); + sec = make_num(s + 18); + } + } + + if (sec < 0 || sec > 59) + return -1; + if (min < 0 || min > 59) + return -1; + if (hour < 0 || hour > 23) + return -1; + if (mday < 1 || mday > 31) + return -1; + if (mon < 1 || mon > 12) + return -1; + if (year < 1970 || year > 2020) + return -1; + + return mktime(year, mon, mday, hour, min, sec); +} Index: latest/net/tux/userspace.c =================================================================== --- /dev/null +++ latest/net/tux/userspace.c @@ -0,0 +1,27 @@ +/* + * TUX - Integrated Application Protocols Layer and Object Cache + * + * Copyright (C) 2000, 2001, Ingo Molnar <mingo@redhat.com> + * + * userspace.c: handle userspace-module requests + */ + +#include <net/tux.h> + +/**************************************************************** + * 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, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ****************************************************************/ + Index: latest/net/tux/times.h =================================================================== --- /dev/null +++ latest/net/tux/times.h @@ -0,0 +1,26 @@ +static time_t TimeDays[10][13] = { + { 852073200, 854751600, 857170800, 859849200, 862441200, 865119600, 867711600, 870390000, 873068400, 875660400, 878338800, 880930800, 883609200 } , + { 883609200, 886287600, 888706800, 891385200, 893977200, 896655600, 899247600, 901926000, 904604400, 907196400, 909874800, 912466800, 915145200 } , + { 915145200, 917823600, 920242800, 922921200, 925513200, 928191600, 930783600, 933462000, 936140400, 938732400, 941410800, 944002800, 946681200 } , + { 946681200, 949359600, 951865200, 954543600, 957135600, 959814000, 962406000, 965084400, 967762800, 970354800, 973033200, 975625200, 978303600 } , + { 978303600, 980982000, 983401200, 986079600, 988671600, 991350000, 993942000, 996620400, 999298800, 1001890800, 1004569200, 1007161200, 1009839600 } , + { 1009839600, 1012518000, 1014937200, 1017615600, 1020207600, 1022886000, 1025478000, 1028156400, 1030834800, 1033426800, 1036105200, 1038697200, 1041375600 } , + { 1041375600, 1044054000, 1046473200, 1049151600, 1051743600, 1054422000, 1057014000, 1059692400, 1062370800, 1064962800, 1067641200, 1070233200, 1072911600 } , + { 1072911600, 1075590000, 1078095600, 1080774000, 1083366000, 1086044400, 1088636400, 1091314800, 1093993200, 1096585200, 1099263600, 1101855600, 1104534000 } , + { 1104534000, 1107212400, 1109631600, 1112310000, 1114902000, 1117580400, 1120172400, 1122850800, 1125529200, 1128121200, 1130799600, 1133391600, 1136070000 } , + { 1136070000, 1138748400, 1141167600, 1143846000, 1146438000, 1149116400, 1151708400, 1154386800, 1157065200, 1159657200, 1162335600, 1164927600, 1167606000 } +}; +static int WeekDays[10][13] = { + { 3, 6, 6, 2, 4, 0, 2, 5, 1, 3, 6, 1, 4 } , + { 4, 0, 0, 3, 5, 1, 3, 6, 2, 4, 0, 2, 5 } , + { 5, 1, 1, 4, 6, 2, 4, 0, 3, 5, 1, 3, 6 } , + { 6, 2, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5, 1 } , + { 1, 4, 4, 0, 2, 5, 0, 3, 6, 1, 4, 6, 2 } , + { 2, 5, 5, 1, 3, 6, 1, 4, 0, 2, 5, 0, 3 } , + { 3, 6, 6, 2, 4, 0, 2, 5, 1, 3, 6, 1, 4 } , + { 4, 0, 1, 4, 6, 2, 4, 0, 3, 5, 1, 3, 6 } , + { 6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4, 0 } , + { 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5, 1 } +}; +#define TUX_YEAROFFSET 1997 +#define TUX_NUMYEARS 10