Here is support for a "generic" interpreter, via kerneld!
(Would you expect anything but a kerneld solution from me? :-) )
This is a "proof of concept", although one that works rather well...
As shipped, it already understands how to handle executable java,
java-applet, PostScript and GIF files as Linux executables.
Goal: Just make a file executable with "chmod +x", and away you go...
The kernel patch is extremely small, and could become even smaller!
It is really just one "if"-statement, which could replace e.g. the
java-specific "binfmt_java.c" in "linux/fs".
All policy decisions are made in user space, for the moment in the
"binfmt" binary, although the division of labour between "binfmt"
and "kerneld" might become slightly different in the future...
Note that all knowledge is (for now) hard-coded into the "binfmt" source,
but I'm sure that someone could add a lot of features to it.
A configuration file would be nice. That might even make it trivial
to keep all the user level code within kerneld...
The following patch consists of three parts:
1. A tiny patch to "linux/fs/exec.c" (yes, it _should_ be that small)
2. A small patch to "kerneld.c" (preferably a recent one!)
3. Source for the basic "binfmt" executable.
Apply each patch, compile, install and enjoy...
I appreciate all comments!
Cheers,
Bjorn <bj0rn@blox.se>
=======================================
Patch to the kernel
=======================================
--- linux/fs/exec.c.org Fri May 17 17:19:40 1996
+++ linux/fs/exec.c Fri May 17 18:07:29 1996
@@ -579,8 +579,16 @@
break;
#ifdef CONFIG_KERNELD
}else{
+# ifndef KERNELD_BINFMT
+# define KERNELD_BINFMT 10 /* KERNELD_BINFMT in kerneld.h soon? */
+# endif
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
char modname[20];
+ if (kerneld_send(KERNELD_BINFMT,
+ sizeof(bprm->buf) | KERNELD_WAIT,
+ sizeof(bprm->buf), bprm->buf, bprm->buf) == 0)
+ continue; /* interpreter found! */
+ /* should we cache unknown binformats? */
if (printable(bprm->buf[0]) &&
printable(bprm->buf[1]) &&
printable(bprm->buf[2]) &&
=======================================
Patch to kerneld
=======================================
--- kerneld/kerneld.c.org Mon May 13 17:06:29 1996
+++ modules-1.3.69k/kerneld/kerneld.c Fri May 17 18:17:18 1996
@@ -336,8 +336,13 @@
int pipes[2];
int status;
int pid = 0;
+ int do_execl = 0;
int op;
+#ifndef KERNELD_BINFMT
+#define KERNELD_BINFMT 10
+#endif
+
switch (op = newjob->msg.mtype) {
case KERNELD_CANCEL_RELEASE_MODULE:
if (keep)
@@ -354,6 +359,9 @@
}
break;
+ case KERNELD_BINFMT:
+ do_execl = 1;
+ /* FALLTHRU */
case KERNELD_SYSTEM:
if (newjob->msg.id) /* reply wanted */
pipe(pipes);
@@ -374,8 +382,23 @@
do_putenv(newjob);
/* signal(SIGCHLD, SIG_DFL); */
- status = system(newjob->msg.text);
- exit(WEXITSTATUS(status));
+ if (do_execl) {
+ /*
+ * The program /sbin/binfmt should
+ * write a string on stdout:
+ * "#!interpreter optional_arg"
+ * and exit with status 0 if successful.
+ *
+ * FSSTND may dicate a better place than /sbin
+ */
+ execl("/sbin/binfmt", "binfmt",
+ newjob->msg.text, NULL);
+ exit(1);
+ }
+ else {
+ status = system(newjob->msg.text);
+ exit(WEXITSTATUS(status));
+ }
}
if ((pid > 0) && newjob->msg.id) { /* reply wanted */
newjob->reply_fd = pipes[0];
=============================================
New file: compile and install as /sbin/binfmt
=============================================
--- /dev/null Sun Aug 29 23:48:28 1992
+++ modules-1.3.69k/kerneld/binfmt.c Fri May 17 17:17:18 1996
@@ -0,0 +1,60 @@
+/*
+ * Generic interpreter interpreter
+ *
+ * This program tries to understand what type of file it has
+ * been given by looking at the 128 (at most) first bytes of
+ * the file.
+ *
+ * These bytes are either put into argv[1], or read from stdin.
+ *
+ * If an interpreter can be found, the corresponding "#!" line
+ * is written on stdout.
+ * Otherwise _nothing_ should be written!
+ *
+ *
+ * You may do whatever you want with this source.
+ * You may even acknowledge the fact that I wrote it...
+ *
+ * Bjorn Ekwall <bj0rn@blox.se> in May 1996
+ */
+
+
+#include <stdio.h>
+
+char java_magic[] = {0xca, 0xfe, 0xba, 0xbe, '\0'};
+
+struct fmt {
+ char *magic;
+ int size;
+ char *interp;
+} def_fmt[] = {
+ { "GIF", 3, "#!/usr/X11/bin/xv"},
+ { "%!PS", 4, "#!/usr/local/bin/gs"},
+ { "<!--applet", 10, "#!/bin/sh /usr/bin/appletviewer"},
+ { java_magic, 4, "#!/bin/sh /usr/bin/java"},
+ { "bj0rn_magic", 11, "#!/usr/bin/wc"},
+ { NULL, 0, NULL}
+};
+
+/* This is what we get from the kernel, via kerneld */
+char buf[128];
+
+main(int argc, char **argv)
+{
+ struct fmt *f;
+ char *bin_buf = buf;
+
+ if (argc > 1)
+ bin_buf = argv[1];
+ else /* test by doing "/sbin/binfmt < some_file"
+ read(0, buf, 128);
+
+ for (f = def_fmt; f->magic; ++f) {
+ if (memcmp(bin_buf, f->magic, f->size) == 0) {
+ puts(f->interp); /* That's it! */
+ exit(0);
+ }
+ }
+ /* Important! No output _at_all_ if the binfmt is still unknown!!! */
+ exit(1);
+}