Patch for 2.1.20 to try: "kernel environment"

David Hinds (dhinds@hyper.stanford.edu)
5 Jan 1997 18:26:34 GMT


Here's an update to the kernel environment patch I posted the other
day, that fixes a memory leak. Also I was told the original patch
didn't go cleanly against 2.1.20. It's possible I posted a patch
against 2.1.16 by mistake... in any case, this one is definitely for
2.1.20. I've also put the patch on hyper.stanford.edu in
/pub/pcmcia/patches/kenv-2.1.20 just in case.

-- Dave Hinds

--- linux/init/main.c.orig Thu Jan 2 22:32:15 1997
+++ linux/init/main.c Sun Jan 5 10:29:47 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,65 @@
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;
+ unsigned long flags;
+
+ 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 *));
+ save_flags(flags);
+ cli();
+ if ((unsigned long)envp_init > memory_start)
+ kfree(envp_init);
+ envp_init = e;
+ restore_flags(flags);
+ 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 +555,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 +689,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 +887,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