File linux-2.6-tux.patch of Package kernel
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