Patch for 2.1.20 to try: "kernel environment"

David Hinds (dhinds@hyper.stanford.edu)
4 Jan 1997 17:40:35 GMT


This patch creates a kernel "environment" almost just like a regular
user mode process's environment. It is initialized from the boot
command line, and gets passed to init. There are two functions,
"getkenv()" and "putkenv()", for reading and modifying the
environment, analogous to "getenv()" and "putenv()". There is also a
new proc entry, /proc/kenv, for reading out the environment. The
patch also adds functionality to the boot line parser, so that it will
respect double quotes and so the backslash can be used to escape
quotes or spaces. That's about all the patch does... in the future I
hope to rewrite a lot of the driver initialization code, which should
be more interesting, but I'd appreciate if some people could give this
a try before I send it to Linus.

-- Dave Hinds
dhinds@hyper.stanford.edu

--- linux/init/main.c.orig Thu Jan 2 22:32:15 1997
+++ linux/init/main.c Fri Jan 3 23:54:35 1997
@@ -6,6 +6,7 @@
* GK 2/5/95 - Changed to support mounting root fs via NFS
* Added initrd & change_root: Werner Almesberger & Hans Lermen, Feb '96
* Moan early if gcc is old, avoiding bogus kernels - Paul Gortmaker, May '96
+ * Rewrite of boot parameter parsing - Dave Hinds, Dec '96
*/

#define __KERNEL_SYSCALLS__
@@ -29,6 +30,7 @@
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/blk.h>
+#include <linux/malloc.h>
#ifdef CONFIG_ROOT_NFS
#include <linux/nfs_fs.h>
#endif
@@ -205,7 +207,6 @@
* Boot command-line arguments
*/
#define MAX_INIT_ARGS 8
-#define MAX_INIT_ENVS 8

extern void time_init(void);

@@ -235,11 +236,59 @@
extern void dquot_init(void);

static char * argv_init[MAX_INIT_ARGS+2] = { "init", NULL, };
-static char * envp_init[MAX_INIT_ENVS+2] = { "HOME=/", "TERM=linux", NULL, };
+char ** envp_init;

static char * argv_rc[] = { "/bin/sh", NULL };
static char * envp_rc[] = { "HOME=/", "TERM=linux", NULL };

+char *getkenv(const char *name)
+{
+ char **s, *t;
+ int i;
+ for (s = envp_init; (t = *s); s++) {
+ for (i = 0; (t[i] != '=') && name[i]; i++)
+ if (t[i] != name[i]) break;
+ if ((t[i] == '=') && (name[i] == '\0'))
+ return t+i+1;
+ }
+ return NULL;
+}
+
+int putkenv(const char *str)
+{
+ char **e, *s, *t;
+ int i, j;
+
+ if (strchr(str, '=') == NULL)
+ return -1;
+ /* See if the tag is already in the environment */
+ for (i = 0; (t = envp_init[i]); i++) {
+ for (j = 0; (t[j] != '=') && (str[j] != '='); j++)
+ if (t[j] != str[j]) break;
+ if ((t[j] == '=') && (str[j] == '='))
+ break;
+ }
+ if (!t) {
+ /* Extend the environment table if necessary */
+ e = kmalloc((i+2) * sizeof(char *), GFP_KERNEL);
+ if (e == NULL)
+ return -1;
+ memcpy(e, envp_init, (i+1) * sizeof(char *));
+ envp_init = e;
+ e[i+1] = NULL;
+ }
+ /* Copy the string to a safe place and add to table */
+ s = kmalloc(strlen(str)+1, GFP_KERNEL);
+ if (s == NULL)
+ return -1;
+ strcpy(s, str);
+ envp_init[i] = s;
+ /* If the old string was kmalloc()ed, then free it */
+ if (t && ((unsigned long)t > memory_start))
+ kfree(t);
+ return 0;
+}
+
char *get_options(char *str, int *ints)
{
char *cur = str;
@@ -500,27 +549,25 @@

#endif

-static int checksetup(char *line)
+static void checksetup(char *line)
{
- int i = 0;
+ int i;
int ints[11];

#ifdef CONFIG_BLK_DEV_IDE
/* ide driver needs the basic string, rather than pre-processed values */
if (!strncmp(line,"ide",3) || (!strncmp(line,"hd",2) && line[2] != '=')) {
ide_setup(line);
- return 1;
+ return;
}
#endif
- while (bootsetups[i].str) {
+ for (i = 0; bootsetups[i].str; i++) {
int n = strlen(bootsetups[i].str);
if (!strncmp(line,bootsetups[i].str,n)) {
bootsetups[i].setup_func(get_options(line+n,ints), ints);
- return 1;
+ return;
}
- i++;
}
- return 0;
}

/* this should be approx 2 Bo*oMips to start (note initial shift), and will
@@ -636,88 +683,79 @@
* as appropriate. Any cmd-line option is taken to be an environment
* variable if it contains the character '='.
*
- *
- * This routine also checks for options meant for the kernel.
- * These options are not given to init - they are for internal kernel use only.
+ * Options can be enclosed in quotation marks, and the backslash can
+ * be used to escape spaces and quotation marks.
*/
-static void parse_options(char *line)
+static unsigned long parse_options(char *line, unsigned long memory_start)
{
- char *next;
- int args, envs;
+ char *last, *next, *s;
+ int quot = 0, args, envs;

- if (!*line)
- return;
- args = 0;
- envs = 1; /* TERM is set to 'linux' by default */
- next = line;
- while ((line = next) != NULL) {
- if ((next = strchr(line,' ')) != NULL)
- *next++ = 0;
- /*
- * check for kernel options first..
- */
- if (!strncmp(line,"root=",5)) {
- parse_root_dev(line+5);
- continue;
- }
-#ifdef CONFIG_ROOT_NFS
- if (!strncmp(line, "nfsroot=", 8)) {
- int n;
- line += 8;
- ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
- if (line[0] == '/' || line[0] == ',' || (line[0] >= '0' && line[0] <= '9')) {
- strncpy(nfs_root_name, line, sizeof(nfs_root_name));
- nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
+ envp_init = (char **) memory_start;
+ envp_init[0] = "HOME=/";
+ envp_init[1] = "TERM=linux";
+ envs = 2;
+ args = 1;
+ for (;;) {
+ while (*line == ' ') line++;
+ if (*line == '\0') break;
+ /* Interpret quotes, backslashes */
+ for (last = next = line; *next != '\0'; next++) {
+ if (*next == '"') {
+ quot = !quot;
continue;
}
- n = strlen(line) + strlen(NFS_ROOT);
- if (n >= sizeof(nfs_root_name))
- line[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
- sprintf(nfs_root_name, NFS_ROOT, line);
- continue;
- }
- if (!strncmp(line, "nfsaddrs=", 9)) {
- line += 9;
- strncpy(nfs_root_addrs, line, sizeof(nfs_root_addrs));
- nfs_root_addrs[sizeof(nfs_root_addrs)-1] = '\0';
- continue;
+ if (!quot && (*next == ' '))
+ break;
+ if (*next == '\\')
+ next++;
+ *(last++) = *next;
}
-#endif
- if (!strcmp(line,"ro")) {
+ if (*next != '\0') next++;
+ *last = '\0';
+ if (strcmp(line, "ro") == 0)
root_mountflags |= MS_RDONLY;
- continue;
- }
- if (!strcmp(line,"rw")) {
+ else if (strcmp(line, "rw") == 0)
root_mountflags &= ~MS_RDONLY;
- continue;
- }
- if (!strcmp(line,"debug")) {
+ else if (strcmp(line, "debug") == 0)
console_loglevel = 10;
- continue;
- }
- if (!strncmp(line,"init=",5)) {
- line += 5;
- execute_command = line;
- continue;
- }
- if (checksetup(line))
- continue;
- /*
- * Then check if it's an environment variable or
- * an option.
- */
- if (strchr(line,'=')) {
- if (envs >= MAX_INIT_ENVS)
- break;
- envp_init[++envs] = line;
+ else if (strchr(line,'=')) {
+ envp_init[envs++] = line;
+ checksetup(line);
} else {
- if (args >= MAX_INIT_ARGS)
+ if (args > MAX_INIT_ARGS)
break;
- argv_init[++args] = line;
+ argv_init[args++] = line;
+ }
+ line = next;
+ }
+ argv_init[args] = NULL;
+ envp_init[envs] = NULL;
+
+ if ((s = getkenv("root")) != NULL)
+ parse_root_dev(s);
+#ifdef CONFIG_ROOT_NFS
+ if ((s = getkenv("nfsroot")) != NULL) {
+ int n;
+ ROOT_DEV = MKDEV(UNNAMED_MAJOR, 255);
+ if (s[0] == '/' || s[0] == ',' || (s[0] >= '0' && s[0] <= '9')) {
+ strncpy(nfs_root_name, s, sizeof(nfs_root_name));
+ nfs_root_name[sizeof(nfs_root_name)-1] = '\0';
+ } else {
+ n = strlen(s) + strlen(NFS_ROOT);
+ if (n >= sizeof(nfs_root_name))
+ s[sizeof(nfs_root_name) - strlen(NFS_ROOT) - 1] = '\0';
+ sprintf(nfs_root_name, NFS_ROOT, s);
}
}
- argv_init[args+1] = NULL;
- envp_init[envs+1] = NULL;
+ if ((s = getkenv("nfsaddrs")) != NULL) {
+ strncpy(nfs_root_addrs, line, sizeof(nfs_root_addrs));
+ nfs_root_addrs[sizeof(nfs_root_addrs)-1] = '\0';
+ }
+#endif
+ execute_command = getkenv("init");
+
+ return (memory_start + (envs+1) * sizeof(char *));
}


@@ -843,7 +881,7 @@
init_IRQ();
sched_init();
time_init();
- parse_options(command_line);
+ memory_start = parse_options(command_line, memory_start);
#ifdef CONFIG_MODULES
init_modules();
#endif
--- linux/kernel/ksyms.c.orig Wed Jan 1 22:09:13 1997
+++ linux/kernel/ksyms.c Fri Jan 3 23:55:34 1997
@@ -70,7 +70,6 @@
#include <linux/smp.h>
#endif

-extern char *get_options(char *str, int *ints);
extern void set_device_ro(int dev,int flag);
extern struct file_operations * get_blkfops(unsigned int);
extern void blkdev_release(struct inode * inode);
@@ -93,6 +92,8 @@
#ifdef CONFIG_KERNELD
EXPORT_SYMBOL(kerneld_send);
#endif
+EXPORT_SYMBOL(getkenv);
+EXPORT_SYMBOL(putkenv);
EXPORT_SYMBOL(get_options);

#ifdef CONFIG_PCI
--- linux/include/linux/kernel.h.orig Sun Dec 1 01:06:55 1996
+++ linux/include/linux/kernel.h Fri Jan 3 23:54:36 1997
@@ -62,6 +62,10 @@
#define pr_info(fmt,arg...) \
printk(KERN_INFO fmt,##arg)

+extern char *get_options(char *str, int *ints);
+extern char *getkenv(const char *name);
+extern int putkenv(const char *str);
+
/*
* "suser()" checks against the effective user id, while "fsuser()"
* is used for file permission checking and checks against the fsuid..
--- linux/include/linux/proc_fs.h.orig Wed Jan 1 22:08:27 1997
+++ linux/include/linux/proc_fs.h Fri Jan 3 23:56:23 1997
@@ -46,7 +46,8 @@
PROC_MD,
PROC_RTC,
PROC_LOCKS,
- PROC_ZORRO
+ PROC_ZORRO,
+ PROC_KENV
};

enum pid_directory_inos {
--- linux/fs/proc/array.c.orig Wed Jan 1 22:08:20 1997
+++ linux/fs/proc/array.c Fri Jan 3 23:54:36 1997
@@ -322,6 +322,16 @@
return sprintf(buffer, "%s\n", saved_command_line);
}

+static int get_kenv(char * buffer)
+{
+ extern char **envp_init;
+ char **p;
+ int len = 0;
+ for (p = envp_init; *p; p++)
+ len += sprintf(buffer+len, "%s\n", *p);
+ return len;
+}
+
static struct task_struct ** get_task(pid_t pid)
{
struct task_struct ** p;
@@ -1081,6 +1091,9 @@
#endif
case PROC_CMDLINE:
return get_cmdline(page);
+
+ case PROC_KENV:
+ return get_kenv(page);

case PROC_MTAB:
return get_filesystem_info( page );
--- linux/fs/proc/root.c.orig Wed Jan 1 22:08:21 1997
+++ linux/fs/proc/root.c Fri Jan 3 23:54:36 1997
@@ -496,6 +496,10 @@
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
};
+static struct proc_dir_entry proc_root_kenv = {
+ PROC_KENV, 6, "kenv",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+};
#ifdef CONFIG_RTC
static struct proc_dir_entry proc_root_rtc = {
PROC_RTC, 3, "rtc",
@@ -562,6 +566,7 @@
proc_register(&proc_root, &proc_root_dma);
proc_register(&proc_root, &proc_root_ioports);
proc_register(&proc_root, &proc_root_cmdline);
+ proc_register(&proc_root, &proc_root_kenv);
#ifdef CONFIG_RTC
proc_register(&proc_root, &proc_root_rtc);
#endif