diff -u --recursive --new-file --exclude=zImage --exclude=config.ini --exclude=.* --exclude=*.patch --exclude=*~ --exclude=*.rej --exclude=buildit --exclude=defconfig linux-v2.00b11-v236/arch/arm/special/Makefile linux-v2.00b11-v236-exec/arch/arm/special/Makefile
--- linux-v2.00b11-v236/arch/arm/special/Makefile	Mon Mar 11 15:56:18 2002
+++ linux-v2.00b11-v236-exec/arch/arm/special/Makefile	Mon Mar 11 16:24:05 2002
@@ -86,7 +86,7 @@
 endif
 
 ifdef CONFIG_EMPEG_DISPLAY
-  L_OBJS	+= empeg_display.o hijack.o notify.o
+  L_OBJS	+= empeg_display.o hijack.o hijack_exec.o notify.o
 endif
 
 ifdef CONFIG_NET_ETHERNET
diff -u --recursive --new-file --exclude=zImage --exclude=config.ini --exclude=.* --exclude=*.patch --exclude=*~ --exclude=*.rej --exclude=buildit --exclude=defconfig linux-v2.00b11-v236/arch/arm/special/hijack.c linux-v2.00b11-v236-exec/arch/arm/special/hijack.c
--- linux-v2.00b11-v236/arch/arm/special/hijack.c	Mon Mar 11 15:56:19 2002
+++ linux-v2.00b11-v236-exec/arch/arm/special/hijack.c	Mon Mar 11 16:25:13 2002
@@ -32,6 +32,7 @@
 extern int real_input_append_code(unsigned long data);			// arch/arm/special/empeg_input.c
 extern int empeg_state_dirty;						// arch/arm/special/empeg_state.c
 extern void state_cleanse(void);					// arch/arm/special/empeg_state.c
+extern int hijack_exec (char * path );	// arch/arm/special/hijack_exec.c
 extern void hijack_serial_insert (const char *buf, int size, int port);	// drivers/char/serial_sa1100.c
 extern void hijack_voladj_intinit(int, int, int, int, int);		// arch/arm/special/empeg_audio3.c
 extern void hijack_beep (int pitch, int duration_msecs, int vol_percent);// arch/arm/special/empeg_audio3.c
@@ -4274,6 +4275,7 @@
 hijack_get_options (unsigned char *buf)
 {
 	static const char menu_delete[] = "menu_remove=";
+	static const char exec_option[] = "exec=";
 	int errors;
 	unsigned char *s;
 
@@ -4329,6 +4331,17 @@
 				char saved = *s;
 				*s = '\0';
 				remove_menu_entry(label);
+				*s = saved;
+				goto nextline;
+			}
+		}
+		if (!strxcmp(s, exec_option, 1)) {
+			unsigned char *path = s += sizeof(exec_option)-1;
+			s = findchars(s, "\n;\r");
+			if (s != path) {
+				char saved = *s;
+				*s = '\0';
+				hijack_exec(path);
 				*s = saved;
 				goto nextline;
 			}
diff -u --recursive --new-file --exclude=zImage --exclude=config.ini --exclude=.* --exclude=*.patch --exclude=*~ --exclude=*.rej --exclude=buildit --exclude=defconfig linux-v2.00b11-v236/arch/arm/special/hijack_exec.c linux-v2.00b11-v236-exec/arch/arm/special/hijack_exec.c
--- linux-v2.00b11-v236/arch/arm/special/hijack_exec.c	Thu Jan  1 08:00:00 1970
+++ linux-v2.00b11-v236-exec/arch/arm/special/hijack_exec.c	Mon Mar 11 16:31:39 2002
@@ -0,0 +1,165 @@
+/*
+	hijack_exec, adapted from kmod by
+	Kim Covil
+
+	kmod, the new module loader (replaces kerneld)
+	Kirk Petersen
+
+	Reorganized not to be a daemon by Adam Richter, with guidance
+	from Greg Zornetzer.
+
+	Modified to avoid chroot and file sharing problems.
+	Mikael Pettersson
+*/
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/smp_lock.h>
+
+#include <asm/uaccess.h>
+
+extern void show_message (const char *message, unsigned long time);
+
+static inline void
+use_init_fs_context(void)
+{
+	struct fs_struct * fs;
+
+	/*
+	 * Don't use the user's fs context, use init's instead.
+	 * Note that we can use "init_task" (which is not actually
+	 * the same as the user-level "init" process) because we
+	 * started "init" with a CLONE_FS
+	 */
+
+	lock_kernel();
+
+	fs = current->fs;
+	dput(fs->root);
+	dput(fs->pwd);
+	fs->root = dget(init_task.fs->root);
+	fs->pwd = dget(init_task.fs->pwd);
+
+	unlock_kernel();
+}
+
+static int exec_path(void * path)
+{
+	static char * envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
+	char *argv[] = { (char *) path, NULL };
+	int i;
+
+	/* Prevent parent user process from sending signals to child.
+	   Otherwise, if the program does not exist, it might
+	   be possible to get a user defined signal handler to execute
+	   as the super user right after the execve fails if you time
+	   the signal just right.
+	*/
+	/*
+	spin_lock_irq(&current->sigmask_lock);
+	flush_signals(current);
+	flush_signal_handlers(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	*/
+
+	/* Copy root dir and cwd from init */
+	use_init_fs_context();
+
+	/* Close our copies of user's open files */
+	/*
+	for (i = 0; i < current->files->max_fds; i++ ) {
+		if (current->files->fd[i]) close(i);
+	}
+	*/
+
+	/* Drop the "current user" thing */
+	free_uid(current);
+
+	/* Give hijack_exec all privileges.. */
+	/*
+	current->uid = current->euid = current->fsuid = 0;
+	cap_set_full(current->cap_inheritable);
+	cap_set_full(current->cap_effective);
+	*/
+
+	/* Allow execve args to be in kernel space. */
+	set_fs(KERNEL_DS);
+
+	/* Go, go, go... */
+	printk(KERN_ERR
+			"hijack_exec: executing %s", (char *) path);
+	if (execve((char *)path, argv, envp) < 0) {
+		char buf[256];
+		sprintf(buf, "failed to exec %s, %d", (char *)path, errno);
+		show_message(buf, 200);
+		printk(KERN_ERR
+		       "hijack_exec: failed to exec %s, errno = %d\n",
+		       (char*) path, errno);
+		return -errno;
+	}
+	return 0;
+}
+
+/*
+	hijack_exec: the function that everyone calls when they need
+	to execute a program.
+*/
+int hijack_exec(const char * path)
+{
+	int pid;
+	int waitpid_result;
+	sigset_t tmpsig;
+
+	char buf[256];
+
+	/* Don't allow hijack_exec() before the root fs is mounted!  */
+	if ( ! current->fs->root ) {
+		show_message("Root fs not mounted", 200);
+
+		printk(KERN_ERR "hijack_exec[%s]: Root fs not mounted\n",
+			path);
+		return -EPERM;
+	}
+
+	printk(KERN_ERR "hijack_exec: calling exec_path with %s\n", path);
+	pid = kernel_thread(exec_path, (void*) path, 0);
+	if (pid < 0) {
+		sprintf(buf, "fork failed, %d", -pid);
+		show_message(buf, 200);
+
+		printk(KERN_ERR "hijack_exec[%s]: fork failed, errno %d\n", path, -pid);
+		return pid;
+	}
+
+	/* Block everything but SIGKILL/SIGSTOP */
+	/*
+	spin_lock_irq(&current->sigmask_lock);
+	tmpsig = current->blocked;
+	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	*/
+
+	waitpid_result = waitpid(pid, NULL, __WCLONE);
+
+	/* Allow signals again.. */
+	/*
+	spin_lock_irq(&current->sigmask_lock);
+	current->blocked = tmpsig;
+	recalc_sigpending(current);
+	spin_unlock_irq(&current->sigmask_lock);
+	*/
+
+	if (waitpid_result != pid) {
+		sprintf(buf, "waitpid(%d) failed, %d", pid, waitpid_result);
+		show_message(buf, 200);
+		printk (KERN_ERR "hijack_exec: waitpid(%d,NULL,0) failed, returning %d.\n",
+			pid, waitpid_result);
+	}
+
+	sprintf(buf, "Exec %s", path);
+	show_message(buf, 200);
+	return 0;
+}
diff -u --recursive --new-file --exclude=zImage --exclude=config.ini --exclude=.* --exclude=*.patch --exclude=*~ --exclude=*.rej --exclude=buildit --exclude=defconfig linux-v2.00b11-v236/arch/arm/special/kftpd.c linux-v2.00b11-v236-exec/arch/arm/special/kftpd.c
--- linux-v2.00b11-v236/arch/arm/special/kftpd.c	Mon Mar 11 15:56:19 2002
+++ linux-v2.00b11-v236-exec/arch/arm/special/kftpd.c	Mon Mar 11 16:24:05 2002
@@ -1845,6 +1845,14 @@
 			append_path(path, p, parms->tmp3);
 			response = kftpd_do_chmod(parms, mode, path);
 		}
+	} else if (!strxcmp(buf, "SITE EXEC ", 1)) {
+		unsigned char *p = &buf[10];
+		extern int hijack_exec(const char * path);
+		if (hijack_exec(p) < 0) {
+			response = 501;
+		} else {
+			response = 200;
+		}
 	} else if (!strxcmp(buf, "SITE ", 1)) {
 		response = hijack_do_command(NULL, &buf[5]) ? 541 : 200;
 	} else if (!strxcmp(buf, "PORT ", 1)) {
