changeset 591:d953d7d4d89d

cp: revamp directory handling The directory is now read from the IPL filesystem. re2c & yacc are used to generate the parser & lexer (although there is still a bit too much code duplication between the config file lexer and the directory file lexer). The CMakeLists.txt file could use some cleanup as well, but that's a different story. This commit also changes the way auth classes are done. Now, they resemble something much like what VM/370 and similar does. Signed-off-by: Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
author Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
date Sat, 26 Nov 2011 22:46:47 -0500
parents ffe037678d57
children 732345b0925c
files cp/.gitignore cp/CMakeLists.txt cp/config/hvf.directory cp/config/system.config cp/guest/init.c cp/include/config.h cp/include/directory.h cp/include/nucleus.h cp/include/shell.h cp/include/slab.h cp/include/util.h cp/mm/slab.c cp/nucleus/.gitignore cp/nucleus/config.c cp/nucleus/config.l cp/nucleus/config.y cp/nucleus/direct.c cp/nucleus/direct.l cp/nucleus/direct.y cp/nucleus/init.c cp/nucleus/objs.cmake cp/nucleus/util.c cp/shell/cmd_beginstop.c cp/shell/cmd_display.c cp/shell/cmd_enable.c cp/shell/cmd_logon.c cp/shell/cmd_query.c cp/shell/cmd_set.c cp/shell/cmd_store.c cp/shell/cmd_system.c cp/shell/directory.c cp/shell/objs.cmake
diffstat 32 files changed, 653 insertions(+), 87 deletions(-) [+]
line wrap: on
line diff
--- a/cp/.gitignore	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/.gitignore	Sat Nov 26 22:46:47 2011 -0500
@@ -1,7 +1,6 @@
 *.rto
 ipl/ipl_rdr_ccws.s
 ipl/ipl_tape.s
-shell/directory_structs.c
 hvf
 loader_rdr.bin
 loader_tape.bin
--- a/cp/CMakeLists.txt	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/CMakeLists.txt	Sat Nov 26 22:46:47 2011 -0500
@@ -20,6 +20,22 @@
 	DEPENDS nucleus/config.l nucleus/config.tab.h
 )
 
+add_custom_command(
+	OUTPUT nucleus/direct.tab.h
+	       nucleus/direct.tab.c
+	COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../build/byacc/yacc
+		-b direct -d -P -p direct_ direct.y
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/nucleus
+	DEPENDS nucleus/direct.y
+)
+add_custom_command(
+	OUTPUT nucleus/direct.lex.c
+	COMMAND ${CMAKE_CURRENT_BINARY_DIR}/../build/re2c/re2c
+		-o direct.lex.c direct.l
+	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/nucleus
+	DEPENDS nucleus/direct.l nucleus/direct.tab.h
+)
+
 set(SUBDIRS nucleus mm fs drivers shell guest)
 
 set(SRCFILES "")
--- a/cp/config/hvf.directory	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/config/hvf.directory	Sat Nov 26 22:46:47 2011 -0500
@@ -1,4 +1,4 @@
-USER OPERATOR A
+USER OPERATOR ABCDEG
     MACHINE ESA 1
     STORAGE 17M
     CONSOLE 0009 3215
@@ -7,7 +7,7 @@
     SPOOL 000E 1403 PRINT
     MDISK 0191 3390 15 100 0192
 
-USER JEFFPC A
+USER JEFFPC ABCDEG
     MACHINE ESA 1
     STORAGE 64M
     CONSOLE 0009 3215
--- a/cp/config/system.config	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/config/system.config	Sat Nov 26 22:46:47 2011 -0500
@@ -1,3 +1,6 @@
+* SPECIFY THE FILE WITH THE USER DIRECTORY
+DIRECTORY HVF DIRECT
+
 * DEFINE OPERATOR ENVIRONMENT
 OPERATOR CONSOLE 0009
 OPERATOR USERID OPERATOR
--- a/cp/guest/init.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/guest/init.c	Sat Nov 26 22:46:47 2011 -0500
@@ -24,18 +24,20 @@
 
 void alloc_guest_devices(struct virt_sys *sys)
 {
+	struct directory_vdev *cur;
 	int ret;
 	int i;
 
 	INIT_LIST_HEAD(&sys->virt_devs);
 
-	for(i=0; sys->directory->devices[i].type != VDEV_INVAL; i++) {
-		ret = alloc_virt_dev(sys, &sys->directory->devices[i],
-				     0x10000 + i);
+	i = 0;
+	list_for_each_entry(cur, &sys->directory->devices, list) {
+		ret = alloc_virt_dev(sys, cur, 0x10000 + i);
 		if (ret)
-			con_printf(sys->con, "Failed to allocate vdev %04X, SCH = %05X (%s)\n",
-				   sys->directory->devices[i].vdev,
-				   0x10000 + i, errstrings[-ret]);
+			con_printf(sys->con, "Failed to allocate vdev %04X, "
+				   "SCH = %05X (%s)\n", cur->vdev, 0x10000 + i,
+				   errstrings[-ret]);
+		i++;
 	}
 }
 
--- a/cp/include/config.h	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/include/config.h	Sat Nov 26 22:46:47 2011 -0500
@@ -31,6 +31,9 @@
 	char			oper_userid[9];
 	struct list_head	rdevs;
 	struct list_head	logos;
+
+	char			direct_fn[8];
+	char			direct_ft[8];
 };
 
 enum {
--- a/cp/include/directory.h	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/include/directory.h	Sat Nov 26 22:46:47 2011 -0500
@@ -10,6 +10,20 @@
 
 #include <console.h>
 
+#define AUTH_A		0x80 /* operations (shutdown, force, ...) */
+#define AUTH_B		0x40 /* device op (attach, detach) */
+#define AUTH_C		0x20 /* sys prog (alter real storage) */
+#define AUTH_D		0x10 /* spool op (start/drain UR, access all spools) */
+#define AUTH_E		0x08 /* system analyst (examine real storage) */
+#define AUTH_F		0x04 /* CE (I/O device error analysis) */
+#define AUTH_G		0x02 /* general user */
+
+/* only useful during directory load */
+struct directory_prop {
+	int got_storage;
+	u64 storage;
+};
+
 enum directory_vdevtype {
 	VDEV_INVAL = 0,			/* invalid */
 	VDEV_CONS,			/* a console */
@@ -20,6 +34,8 @@
 };
 
 struct directory_vdev {
+	struct list_head list;
+
 	enum directory_vdevtype type;	/* device type */
 	u16 vdev;			/* virtual dev # */
 
@@ -53,17 +69,25 @@
 };
 
 struct user {
+	struct list_head list;		/* list of users */
+
 	char *userid;
 	struct task *task;
 
 	/* VM configuration */
 	u64 storage_size;
 
-	struct directory_vdev *devices;
+	struct list_head devices;
 
 	u8 auth;
 };
 
 extern struct user *find_user_by_id(char *userid);
+extern void directory_alloc_user(char *name, int auth,
+				 struct directory_prop *prop,
+				 struct list_head *vdevs);
+extern int load_directory(struct fs *fs);
+extern int direct_lex(void *data, void *yyval);
+extern int direct_parse(struct parser *parser);
 
 #endif
--- a/cp/include/nucleus.h	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/include/nucleus.h	Sat Nov 26 22:46:47 2011 -0500
@@ -64,8 +64,4 @@
 extern int con_printf(struct console *con, const char *fmt, ...)
         __attribute__ ((format (printf, 2, 3)));
 
-/*
- * stdarg.h equivalents
- */
-
 #endif
--- a/cp/include/shell.h	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/include/shell.h	Sat Nov 26 22:46:47 2011 -0500
@@ -31,7 +31,7 @@
 extern int spool_exec(struct virt_sys *sys, struct virt_device *vdev);
 
 #define SHELL_CMD_AUTH(s,a)	do { \
-					if ((s)->directory->auth > (a)) \
+					if (!((s)->directory->auth & AUTH_##a)) \
 						return -EPERM; \
 				} while(0)
 
--- a/cp/include/slab.h	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/include/slab.h	Sat Nov 26 22:46:47 2011 -0500
@@ -31,5 +31,6 @@
 
 extern void *malloc(int size, int type);
 extern void free(void *ptr);
+extern int allocsize(void *ptr);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cp/include/util.h	Sat Nov 26 22:46:47 2011 -0500
@@ -0,0 +1,8 @@
+#ifndef __UTIL_H
+#define __UTIL_H
+
+extern char *strdup(char *s, int flags);
+extern int hex(char *a, char *b, u64 *out);
+extern int bcd2dec(u64 val, u64 *out);
+
+#endif
--- a/cp/mm/slab.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/mm/slab.c	Sat Nov 26 22:46:47 2011 -0500
@@ -258,6 +258,16 @@
 	spin_unlock_intrestore(&slab->first->lock, int_mask);
 }
 
+int allocsize(void *ptr)
+{
+	struct slab *slab;
+
+	/* get the slab object ptr */
+	slab = (struct slab *) (((u64) ptr) & ~0xfff);
+
+	return slab->objsize;
+}
+
 void *malloc(int size, int type)
 {
 	if (!size)
--- a/cp/nucleus/.gitignore	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/.gitignore	Sat Nov 26 22:46:47 2011 -0500
@@ -3,3 +3,7 @@
 config.tab.c
 config.tab.h
 config.lex.c
+
+direct.tab.c
+direct.tab.h
+direct.lex.c
--- a/cp/nucleus/config.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/config.c	Sat Nov 26 22:46:47 2011 -0500
@@ -9,6 +9,7 @@
 #include <bdev.h>
 #include <edf.h>
 #include <slab.h>
+#include <directory.h>
 #include <ebcdic.h>
 #include <sclp.h>
 #include <parser.h>
@@ -80,6 +81,9 @@
 	struct device *dev;
 	struct fs *fs;
 
+	sclp_msg("LOADING CONFIG FROM    '%*.*s' '%*.*s'\n",
+		 8, 8, CONFIG_FILE_NAME, 8, 8, CONFIG_FILE_TYPE);
+
 	memset(&sysconf, 0, sizeof(struct sysconf));
 	INIT_LIST_HEAD(&sysconf.rdevs);
 	INIT_LIST_HEAD(&sysconf.logos);
@@ -111,6 +115,7 @@
 	if (config_parse(&p))
 		return ERR_PTR(-ECORRUPT);
 
+	load_directory(fs);
 	__load_logos(fs);
 
 	return fs;
--- a/cp/nucleus/config.l	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/config.l	Sat Nov 26 22:46:47 2011 -0500
@@ -5,41 +5,13 @@
  * details.
  */
 
+#include <util.h>
 #include <ebcdic.h>
 #include <lexer.h>
+#include <sclp.h>
 
 #include "config.tab.h"
 
-int hex(char *a, char *b, u64 *out)
-{
-	u64 val;
-	char c;
-
-	if (a>=b)
-		return 1;
-
-	val = 0;
-
-	while(a<b) {
-		c = *a;
-
-		if ((c >= '0') && (c <= '9'))
-			val = (val << 4) | (*a - '0');
-		else if ((c >= 'A') && (c <= 'F'))
-			val = (val << 4) | (*a - 'A' + 10);
-		else if ((c >= 'a') && (c <= 'f'))
-			val = (val << 4) | (*a - 'a' + 10);
-		else
-			return 1;
-
-		a++;
-	}
-
-	*out = val;
-
-	return 0;
-}
-
 static int __fill(struct lexer *lexer, int n)
 {
 	char buf[CONFIG_LRECL];
@@ -123,6 +95,7 @@
 		re2c:define:YYLIMIT = &lexer->buf[lexer->filllen];
 		"\n"		{ YYRETURN(NLINE); }
 		"*" [^\n]*	{ YYRETURN(COMMENT); }
+		"DIRECTORY"	{ YYRETURN(DIRECTORY); }
 		"OPERATOR"	{ YYRETURN(OPERATOR); }
 		"RDEV"		{ YYRETURN(RDEV); }
 		"LOGO"		{ YYRETURN(LOGO); }
@@ -136,7 +109,7 @@
 				  len = cur-&lexer->buf[lexer->recoff];
 				  memcpy(lexer->retbuf, &lexer->buf[lexer->recoff], len);
 				  lexer->retbuf[len] = '\0';
-				  val->ptr = lexer->retbuf;
+				  val->ptr = strdup(lexer->retbuf, ZONE_NORMAL);
 				  YYRETURN(WORD);
 				}
 		[ \t]+		{ YYSKIP(); }
--- a/cp/nucleus/config.y	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/config.y	Sat Nov 26 22:46:47 2011 -0500
@@ -76,6 +76,12 @@
 	sysconf.oper_userid[8] = '\0';
 }
 
+static void __direct(char *fn, char *ft)
+{
+	copy_fn(sysconf.direct_fn, fn);
+	copy_fn(sysconf.direct_ft, ft);
+}
+
 %}
 
 %union {
@@ -85,7 +91,7 @@
 
 %token <ptr> WORD
 %token <num> NUM
-%token OPERATOR RDEV LOGO CONSOLE USERID LOCAL
+%token OPERATOR RDEV LOGO CONSOLE USERID LOCAL DIRECTORY
 %token NLINE COMMENT
 
 %%
@@ -95,11 +101,22 @@
       ;
 
 stmt : OPERATOR CONSOLE NUM NLINE	{ __oper_con($3); }
-     | OPERATOR USERID WORD NLINE	{ __oper_userid($3); }
+     | OPERATOR USERID WORD NLINE	{ __oper_userid($3);
+					  free($3);
+					}
      | OPERATOR USERID OPERATOR NLINE	{ __oper_userid("OPERATOR"); }
      | RDEV NUM NUM NLINE		{ __rdev($2, $3); }
-     | LOGO LOCAL NUM WORD WORD NLINE	{ __logo(1, $3, $4, $5); }
-     | LOGO LOCAL NUM WORD LOGO NLINE	{ __logo(1, $3, $4, "LOGO"); }
+     | LOGO LOCAL NUM WORD WORD NLINE	{ __logo(1, $3, $4, $5);
+					  free($4);
+					  free($5);
+					}
+     | LOGO LOCAL NUM WORD LOGO NLINE	{ __logo(1, $3, $4, "LOGO");
+					  free($4);
+					}
+     | DIRECTORY WORD WORD NLINE	{ __direct($2, $3);
+					  free($2);
+					  free($3);
+					}
      | COMMENT NLINE
      | NLINE
      ;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cp/nucleus/direct.c	Sat Nov 26 22:46:47 2011 -0500
@@ -0,0 +1,104 @@
+/*
+ * (C) Copyright 2007-2011  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * This file is released under the GPLv2.  See the COPYING file for more
+ * details.
+ */
+
+#include <errno.h>
+#include <ebcdic.h>
+#include <directory.h>
+#include <shell.h>
+#include <slab.h>
+#include <parser.h>
+#include <lexer.h>
+#include <edf.h>
+#include <sclp.h>
+
+#include "direct.tab.h"
+
+static LIST_HEAD(directory);
+
+int direct_parse(struct parser *);
+
+struct user *find_user_by_id(char *userid)
+{
+	struct user *u;
+
+	if (!userid)
+		return ERR_PTR(-ENOENT);
+
+	list_for_each_entry(u, &directory, list)
+		if (!strcasecmp(u->userid, userid))
+			return u;
+
+	return ERR_PTR(-ENOENT);
+}
+
+static void *_realloc(void *p, size_t s)
+{
+	if (!p)
+		return malloc(s, ZONE_NORMAL);
+
+	if (s <= allocsize(p))
+		return p;
+
+	BUG();
+	return NULL;
+}
+
+static void _error(struct parser *p, char *msg)
+{
+	sclp_msg("directory parse error: %s\n", msg);
+	BUG();
+}
+
+int load_directory(struct fs *fs)
+{
+	struct parser p;
+	struct lexer l;
+
+	sclp_msg("LOADING DIRECTORY FROM '%*.*s' '%*.*s'\n",
+		 8, 8, sysconf.direct_fn, 8, 8, sysconf.direct_ft);
+
+	/* set up the parser */
+	memset(&p, 0, sizeof(p));
+	p.lex = direct_lex;
+	p.lex_data = &l;
+	p.realloc = _realloc;
+	p.free = free;
+	p.error = _error;
+
+	/* set up the lexer */
+	memset(&l, 0, sizeof(l));
+	l.fs = fs;
+	l.init = 0;
+
+	/* parse! */
+	return direct_parse(&p) ? -ECORRUPT : 0;
+}
+
+void directory_alloc_user(char *name, int auth, struct directory_prop *prop,
+			  struct list_head *vdevs)
+{
+	struct user *user;
+
+	assert(name);
+	assert(prop->got_storage);
+
+	user = malloc(sizeof(struct user), ZONE_NORMAL);
+	assert(user);
+
+	memset(user, 0, sizeof(struct user));
+
+	INIT_LIST_HEAD(&user->list);
+	INIT_LIST_HEAD(&user->devices);
+	list_splice(vdevs, &user->devices);
+
+	user->userid = name;
+	user->storage_size = prop->storage;
+	user->auth = auth;
+
+	// FIXME: locking?
+	list_add_tail(&user->list, &directory);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cp/nucleus/direct.l	Sat Nov 26 22:46:47 2011 -0500
@@ -0,0 +1,131 @@
+/*
+ * (C) Copyright 2007-2011  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * This file is released under the GPLv2.  See the COPYING file for more
+ * details.
+ */
+
+#include <util.h>
+#include <ebcdic.h>
+#include <lexer.h>
+#include <directory.h>
+#include <sclp.h>
+
+#include "direct.tab.h"
+
+static int __fill(struct lexer *lexer, int n)
+{
+	char buf[CONFIG_LRECL];
+	int end, i;
+	int ret;
+
+	if (unlikely(!lexer->init)) {
+		/* look up the directory file */
+		lexer->file = edf_lookup(lexer->fs, sysconf.direct_fn, sysconf.direct_ft);
+		if (IS_ERR(lexer->file))
+			return PTR_ERR(lexer->file);
+
+		if ((lexer->file->FST.LRECL != CONFIG_LRECL) ||
+		    (lexer->file->FST.RECFM != FSTDFIX))
+			return -EINVAL;
+
+		lexer->recoff = 0;
+		lexer->buflen = sizeof(lexer->buf);
+		lexer->filllen = 0;
+
+		lexer->init = 1;
+	}
+
+	if (lexer->recoff) {
+		lexer->filllen -= lexer->recoff;
+		memmove(lexer->buf, &lexer->buf[lexer->recoff], lexer->filllen);
+		lexer->recoff = 0;
+	}
+
+	assert(lexer->filllen < CONFIG_LRECL);
+
+	ret = edf_read_rec(lexer->file, buf, lexer->recno++);
+	if (ret)
+		return ret;
+
+	ebcdic2ascii((u8*) buf, CONFIG_LRECL);
+	ascii2upper((u8*) buf, CONFIG_LRECL);
+
+	/* find the last non-blank */
+	for(end=CONFIG_LRECL-1; end>=0; end--)
+		if (buf[end] != ' ')
+			break;
+
+	for(i=0; (i<CONFIG_LRECL) && (i<end+1); i++)
+		lexer->buf[lexer->filllen+i] = buf[i];
+
+	lexer->buf[lexer->filllen+i] = '\n';
+	lexer->filllen += i+1;
+
+	return 0;
+}
+#define YYFILL(n)	do { \
+				int ret; \
+				int off = cur - &lexer->buf[lexer->recoff]; \
+				ret = __fill(lexer, (n)); \
+				if (ret && !(lexer->filllen - lexer->recoff)) \
+					return ret; \
+				cur = &lexer->buf[off]; \
+			} while(0)
+#define YYRETURN(x)	do { \
+				lexer->recoff = cur - lexer->buf; \
+				return (x); \
+			} while(0)
+#define YYSKIP()	do { \
+				lexer->recoff = cur - lexer->buf; \
+				goto next; \
+			} while(0)
+int direct_lex(void *data, void *yyval)
+{
+	struct lexer *lexer = data;
+	YYSTYPE *val = yyval;
+	char *cur;
+
+next:
+	cur = &lexer->buf[lexer->recoff];
+
+	/*!re2c
+		re2c:define:YYCTYPE = "char";
+		re2c:define:YYCURSOR = cur;
+		re2c:define:YYLIMIT = &lexer->buf[lexer->filllen];
+		"\n"		{ YYRETURN(NLINE); }
+		"*" [^\n]*	{ YYRETURN(COMMENT); }
+		"USER"		{ YYRETURN(USER); }
+		"MACHINE"	{ YYRETURN(MACHINE); }
+		"STORAGE"	{ YYRETURN(STORAGE); }
+		"CONSOLE"	{ YYRETURN(CONSOLE); }
+		"SPOOL"		{ YYRETURN(SPOOL); }
+		"READER"	{ YYRETURN(READER); }
+		"PUNCH"		{ YYRETURN(PUNCH); }
+		"PRINT"		{ YYRETURN(PRINT); }
+		"MDISK"		{ YYRETURN(MDISK); }
+		[0-9]+[KMGTP]	{ hex(&lexer->buf[lexer->recoff], cur-1, &val->num);
+				  bcd2dec(val->num, &val->num);
+				  switch(*(cur-1)) {
+					case 'P': val->num *= 1024;
+					case 'T': val->num *= 1024;
+					case 'G': val->num *= 1024;
+					case 'M': val->num *= 1024;
+					case 'K': val->num *= 1024;
+				  }
+
+				  YYRETURN(STORSPEC);
+				}
+		[0-9A-F]+	{ hex(&lexer->buf[lexer->recoff], cur, &val->num);
+				  YYRETURN(NUM);
+				}
+		[A-Z]+		{ int len;
+				  len = cur-&lexer->buf[lexer->recoff];
+				  memcpy(lexer->retbuf, &lexer->buf[lexer->recoff], len);
+				  lexer->retbuf[len] = '\0';
+				  val->ptr = strdup(lexer->retbuf, ZONE_NORMAL);
+				  YYRETURN(WORD);
+				}
+		[ \t]+		{ YYSKIP(); }
+	*/
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cp/nucleus/direct.y	Sat Nov 26 22:46:47 2011 -0500
@@ -0,0 +1,156 @@
+%{
+
+#include <util.h>
+#include <slab.h>
+#include <directory.h>
+
+static void merge_props(struct directory_prop *out, struct directory_prop *a,
+			struct directory_prop *b)
+{
+	out->got_storage = a->got_storage || b->got_storage;
+
+	if (a->got_storage && !b->got_storage)
+		out->storage = a->storage;
+	else if (!a->got_storage && b->got_storage)
+		out->storage = b->storage;
+	else if (a->got_storage && b->got_storage)
+		BUG();
+}
+
+static struct directory_vdev *__alloc_spool_vdev(enum directory_vdevtype type,
+						 u64 devnum, u64 typenum)
+{
+	struct directory_vdev *vdev;
+
+	assert(devnum <= 0xffff);
+	assert(typenum <= 0xffff);
+	assert((type == VDEV_CONS) || (type == VDEV_SPOOL));
+
+	vdev = malloc(sizeof(struct directory_vdev), ZONE_NORMAL);
+	assert(vdev);
+
+	vdev->type = type;
+	vdev->vdev = devnum;
+
+	if (type == VDEV_SPOOL) {
+		vdev->u.spool.type = typenum;
+		vdev->u.spool.model = 0;
+	}
+
+	return vdev;
+}
+
+static struct directory_vdev *__alloc_mdisk_vdev(u64 devnum, u64 typenum,
+						 u64 start, u64 len, u64 rdev)
+{
+	struct directory_vdev *vdev;
+
+	assert(devnum <= 0xffff);
+	assert(typenum <= 0xffff);
+	assert(start <= 0xffff);
+	assert(len <= 0xffff);
+	assert(rdev <= 0xffff);
+
+	vdev = malloc(sizeof(struct directory_vdev), ZONE_NORMAL);
+	assert(vdev);
+
+	vdev->type = VDEV_MDISK;
+	vdev->vdev = devnum;
+	vdev->u.mdisk.cyloff = start;
+	vdev->u.mdisk.cylcnt = len;
+	vdev->u.mdisk.rdev = rdev;
+
+	return vdev;
+}
+
+static int __auth_str(char *in)
+{
+	int a = 0;
+
+	for(; *in; in++) {
+		if (*in == 'A')
+			a |= AUTH_A;
+		else if (*in == 'B')
+			a |= AUTH_B;
+		else if (*in == 'C')
+			a |= AUTH_C;
+		else if (*in == 'D')
+			a |= AUTH_D;
+		else if (*in == 'E')
+			a |= AUTH_E;
+		else if (*in == 'F')
+			a |= AUTH_F;
+		else if (*in == 'G')
+			a |= AUTH_G;
+		else
+			return -1;
+	}
+
+	return a;
+}
+
+static int __auth_int(u64 in)
+{
+	// FIXME
+	return AUTH_G;
+}
+
+%}
+
+%union {
+	struct directory_vdev *vdev;
+	struct list_head list;
+	struct directory_prop prop;
+	char *ptr;
+	u64 num;
+};
+
+%token <ptr> WORD
+%token <num> STORSPEC NUM
+%token USER MACHINE STORAGE CONSOLE SPOOL READER PUNCH PRINT MDISK
+%token NLINE COMMENT
+
+%type <prop> props prop
+%type <list> vdevs
+%type <vdev> vdev
+
+%%
+
+users : users NLINE user
+      | user
+      | NLINE				/* an empty line */
+      ;
+
+user : USER WORD WORD NLINE props vdevs		{ directory_alloc_user($2, __auth_str($3), &$5, &$6); }
+     | USER WORD NUM NLINE props vdevs		{ directory_alloc_user($2, __auth_int($3), &$5, &$6); }
+     ;
+
+props : props prop			{ merge_props(&$$, &$1, &$2); }
+      | prop				{ memcpy(&$$, &$1, sizeof($$)); }
+      ;
+
+vdevs : vdevs vdev			{ INIT_LIST_HEAD(&$$);
+					  list_splice(&$1, &$$);
+					  list_add(&$2->list, &$$);
+					}
+      | vdev				{ INIT_LIST_HEAD(&$$);
+					  list_add(&$1->list, &$$);
+					}
+      ;
+
+prop : MACHINE WORD NUM NLINE		{ memset(&$$, 0, sizeof($$));
+					  free($2);
+					}
+     | STORAGE STORSPEC NLINE		{ $$.got_storage = 1;
+					  $$.storage = $2; }
+     | STORAGE NUM NLINE		{ $$.got_storage = 1;
+					  assert(!bcd2dec($2, &$$.storage));
+					}
+     ;
+
+vdev : CONSOLE NUM NUM NLINE		{ $$ = __alloc_spool_vdev(VDEV_CONS, $2, $3); }
+     | SPOOL NUM NUM READER NLINE	{ $$ = __alloc_spool_vdev(VDEV_SPOOL, $2, $3); }
+     | SPOOL NUM NUM PUNCH NLINE	{ $$ = __alloc_spool_vdev(VDEV_SPOOL, $2, $3); }
+     | SPOOL NUM NUM PRINT NLINE	{ $$ = __alloc_spool_vdev(VDEV_SPOOL, $2, $3); }
+     | MDISK NUM NUM NUM NUM NUM NLINE	{ $$ = __alloc_mdisk_vdev($2, $3, $4, $5, $6); }
+     ;
--- a/cp/nucleus/init.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/init.c	Sat Nov 26 22:46:47 2011 -0500
@@ -17,6 +17,7 @@
 #include <interrupt.h>
 #include <magic.h>
 #include <shell.h>
+#include <sclp.h>
 
 static struct psw new_io_psw = {
 	.ea	= 1,
@@ -101,6 +102,10 @@
 	 */
 	get_parsed_tod(&ipltime);
 
+	sclp_msg("IPL AT %02d:%02d:%02d UTC %04d-%02d-%02d\n\n",
+		   ipltime.th, ipltime.tm, ipltime.ts, ipltime.dy,
+		   ipltime.dm, ipltime.dd);
+
 	opcon = start_oper_console();
 
 	con_printf(opcon, "NOW %02d:%02d:%02d UTC %04d-%02d-%02d\n\n",
--- a/cp/nucleus/objs.cmake	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/nucleus/objs.cmake	Sat Nov 26 22:46:47 2011 -0500
@@ -1,2 +1,3 @@
 set(FILES init.c io.c printf.c int.s ext.c svc.c pgm.c spinlock.c mutex.c
-	sched.c sclp.c ldep.c config.c config.tab.c config.lex.c)
+	sched.c sclp.c ldep.c util.c config.c config.tab.c config.lex.c
+	direct.c direct.tab.c direct.lex.c)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cp/nucleus/util.c	Sat Nov 26 22:46:47 2011 -0500
@@ -0,0 +1,77 @@
+/*
+ * (C) Copyright 2007-2011  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ *
+ * This file is released under the GPLv2.  See the COPYING file for more
+ * details.
+ */
+
+#include <slab.h>
+#include <util.h>
+
+char *strdup(char *s, int flags)
+{
+	char *d;
+	int len;
+
+	for(d=s; *d; d++)
+		;
+
+	len = d - s;
+
+	d = malloc(sizeof(char)*len, flags);
+	if (!d)
+		return NULL;
+
+	memcpy(d, s, len*sizeof(char));
+
+	return d;
+}
+
+int hex(char *a, char *b, u64 *out)
+{
+	u64 val;
+	char c;
+
+	if (a>=b)
+		return 1;
+
+	val = 0;
+
+	while(a<b) {
+		c = *a;
+
+		if ((c >= '0') && (c <= '9'))
+			val = (val << 4) | (*a - '0');
+		else if ((c >= 'A') && (c <= 'F'))
+			val = (val << 4) | (*a - 'A' + 10);
+		else if ((c >= 'a') && (c <= 'f'))
+			val = (val << 4) | (*a - 'a' + 10);
+		else
+			return 1;
+
+		a++;
+	}
+
+	*out = val;
+
+	return 0;
+}
+
+int bcd2dec(u64 val, u64 *out)
+{
+	u64 v;
+	u64 scale;
+
+	for(v=0,scale=1; val; scale*=10, val>>=4) {
+		u64 x = val & 0xf;
+
+		if (x>=10)
+			return 1;
+
+		v += (x*scale);
+	}
+
+	*out = v;
+
+	return 0;
+}
--- a/cp/shell/cmd_beginstop.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_beginstop.c	Sat Nov 26 22:46:47 2011 -0500
@@ -16,6 +16,8 @@
  */
 static int cmd_begin(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	sys->task->cpu->state = GUEST_OPERATING;
 	return 0;
 }
@@ -31,6 +33,8 @@
  */
 static int cmd_stop(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	sys->task->cpu->state = GUEST_STOPPED;
 	return 0;
 }
--- a/cp/shell/cmd_display.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_display.c	Sat Nov 26 22:46:47 2011 -0500
@@ -1,5 +1,5 @@
 /*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
+ * (C) Copyright 2007-2011  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  *
  * This file is released under the GPLv2.  See the COPYING file for more
  * details.
@@ -201,6 +201,8 @@
 	u64 mlen = 0;
 	enum display_fmt fmt;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	switch (cmd[0]) {
 		case 'N': case 'n':
 			/* numeric */
@@ -246,7 +248,7 @@
 	u32 *val;
 	int i;
 
-	SHELL_CMD_AUTH(sys, 'E');
+	SHELL_CMD_AUTH(sys, E);
 
 	val = (u32*) &sys->task->cpu->sie_cb;
 
@@ -271,6 +273,8 @@
  */
 static int cmd_display_gpr(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "GR  0 = %016llX %016llX\n",
 		   sys->task->cpu->regs.gpr[0],
 		   sys->task->cpu->regs.gpr[1]);
@@ -309,6 +313,8 @@
  */
 static int cmd_display_fpcr(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "FPCR  = %08X\n", sys->task->cpu->regs.fpcr);
 	return 0;
 }
@@ -324,6 +330,8 @@
  */
 static int cmd_display_fpr(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "FR  0 = %016llX %016llX\n",
 		   sys->task->cpu->regs.fpr[0],
 		   sys->task->cpu->regs.fpr[1]);
@@ -362,6 +370,8 @@
  */
 static int cmd_display_cr(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "CR  0 = %016llX %016llX\n",
 		   sys->task->cpu->sie_cb.gcr[0],
 		   sys->task->cpu->sie_cb.gcr[1]);
@@ -400,6 +410,8 @@
  */
 static int cmd_display_ar(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "AR  0 = %08X %08X\n",
 		   sys->task->cpu->regs.ar[0],
 		   sys->task->cpu->regs.ar[1]);
@@ -485,6 +497,8 @@
 	int disp = 0;
 	int zarch;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	if (!strcasecmp(cmd, "ALL"))
 		disp = D_PSW_ALL;
 	else if (!strcasecmp(cmd, "RST"))
@@ -586,6 +600,8 @@
 	u64 sch;
 	int all;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	if (strcasecmp(cmd, "ALL")) {
 		cmd = __extract_hex(cmd, &sch);
 		if (IS_ERR(cmd))
--- a/cp/shell/cmd_enable.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_enable.c	Sat Nov 26 22:46:47 2011 -0500
@@ -21,6 +21,8 @@
 	struct device *dev;
 	struct console *con;
 
+	SHELL_CMD_AUTH(sys, A);
+
 	if (!strcasecmp(cmd, "ALL")) {
 		con_printf(sys->con, "ENABLE ALL not yet implemented!\n");
 		return 0;
--- a/cp/shell/cmd_logon.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_logon.c	Sat Nov 26 22:46:47 2011 -0500
@@ -10,7 +10,7 @@
  *!! SYNTAX
  *! \tok{\sc LOGON} <userid>
  *!! XATNYS
- *!! AUTH G
+ *!! AUTH none
  *!! PURPOSE
  *! Log on to a virtual machine.
  */
--- a/cp/shell/cmd_query.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_query.c	Sat Nov 26 22:46:47 2011 -0500
@@ -111,6 +111,8 @@
  */
 static int cmd_query_cplevel(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "HVF version " VERSION "\n");
 	con_printf(sys->con, "IPL at %02d:%02d:%02d UTC %04d-%02d-%02d\n",
 		   ipltime.th, ipltime.tm, ipltime.ts, ipltime.dy,
@@ -132,6 +134,8 @@
 {
 	struct datetime dt;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	get_parsed_tod(&dt);
 
 	con_printf(sys->con, "TIME IS %02d:%02d:%02d UTC %04d-%02d-%02d\n",
@@ -151,7 +155,11 @@
  */
 static int cmd_query_archmode(struct virt_sys *sys, char *cmd, int len)
 {
-	char *mode = (VCPU_ZARCH(sys->task->cpu)) ? "z/Arch" : "ESA390";
+	char *mode;
+
+	SHELL_CMD_AUTH(sys, G);
+
+	mode = (VCPU_ZARCH(sys->task->cpu)) ? "z/Arch" : "ESA390";
 
 	con_printf(sys->con, "ARCHMODE = %s\n", mode);
 
@@ -171,6 +179,8 @@
 {
 	struct virt_device *vdev;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "CPU 00  ID  %016llX %s\n",
 		   sys->task->cpu->cpuid,
 		   __guest_state_to_str(sys->task->cpu->state));
@@ -213,7 +223,7 @@
  *!   <rdev>
  *! \end{stack}
  *!! XATNYS
- *!! AUTH A
+ *!! AUTH B
  *!! PURPOSE
  *! \cbstart
  *! Lists the host's real devices, CPUs, and storage
@@ -231,7 +241,7 @@
 	int what = 0;
 	u64 devnum;
 
-	SHELL_CMD_AUTH(sys, 'A');
+	SHELL_CMD_AUTH(sys, B);
 
 	if (strnlen(cmd, len) == 0) {
 		what = QUERY_CPUS | QUERY_STOR | QUERY_DEVS;
@@ -275,14 +285,14 @@
  *!! SYNTAX
  *! \tok{\sc Query} \tok{\sc Task}
  *!! XATNYS
- *!! AUTH A
+ *!! AUTH E
  *!! PURPOSE
  *! Lists all of the tasks running on the host. This includes guest virtual
  *! cpu tasks, as well as system helper tasks.
  */
 static int cmd_query_task(struct virt_sys *sys, char *cmd, int len)
 {
-	SHELL_CMD_AUTH(sys, 'A');
+	SHELL_CMD_AUTH(sys, E);
 
 	list_tasks(display_task, sys->con);
 
@@ -306,6 +316,8 @@
  */
 static int cmd_query_names(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	list_users(sys->con, display_names);
 
 	return 0;
@@ -322,6 +334,8 @@
  */
 static int cmd_query_userid(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	con_printf(sys->con, "%s\n", sys->directory->userid);
 	return 0;
 }
--- a/cp/shell/cmd_set.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_set.c	Sat Nov 26 22:46:47 2011 -0500
@@ -16,6 +16,8 @@
  */
 static int cmd_set_nots(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	sys->print_ts = 0;
 	return 0;
 }
@@ -31,6 +33,8 @@
  */
 static int cmd_set_ts(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	sys->print_ts = 1;
 	return 0;
 }
--- a/cp/shell/cmd_store.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_store.c	Sat Nov 26 22:46:47 2011 -0500
@@ -22,6 +22,8 @@
 	u64 guest_addr, host_addr;
 	u64 val = 0;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = parse_addrspec(&guest_addr, NULL, cmd);
 	if (IS_ERR(cmd)) {
 		con_printf(sys->con, "STORE: Invalid addr-spec\n");
@@ -70,6 +72,8 @@
 	u64 *ptr = (u64*) &sys->task->cpu->regs.gpr;
 	u64 val, gpr;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = __extract_dec(cmd, &gpr);
 	if (IS_ERR(cmd))
 		return PTR_ERR(cmd);
@@ -103,6 +107,8 @@
 	u64 *ptr = (u64*) &sys->task->cpu->regs.fpr;
 	u64 val, fpr;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = __extract_dec(cmd, &fpr);
 	if (IS_ERR(cmd))
 		return PTR_ERR(cmd);
@@ -135,6 +141,8 @@
 {
 	u64 val;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = __extract_hex(cmd, &val);
 	if (IS_ERR(cmd))
 		return PTR_ERR(cmd);
@@ -162,6 +170,8 @@
 	u64 *ptr = (u64*) &sys->task->cpu->sie_cb.gcr;
 	u64 val, cr;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = __extract_dec(cmd, &cr);
 	if (IS_ERR(cmd))
 		return PTR_ERR(cmd);
@@ -195,6 +205,8 @@
 	u32 *ptr = (u32*) &sys->task->cpu->regs.ar;
 	u64 val, ar;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	cmd = __extract_dec(cmd, &ar);
 	if (IS_ERR(cmd))
 		return PTR_ERR(cmd);
@@ -272,6 +284,8 @@
 	u64 new_words[4] = {0, 0, 0, 0};
 	int cnt;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	for (cnt=0; cnt<4; cnt++) {
 		cmd = __extract_hex(cmd, &new_words[cnt]);
 		if (IS_ERR(cmd))
--- a/cp/shell/cmd_system.c	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/cmd_system.c	Sat Nov 26 22:46:47 2011 -0500
@@ -273,6 +273,8 @@
 	u64 vdevnum = 0;
 	char *c;
 
+	SHELL_CMD_AUTH(sys, G);
+
 	/* get IPL vdev # */
 	c = __extract_hex(cmd, &vdevnum);
 	if (IS_ERR(c))
@@ -334,6 +336,8 @@
  */
 static int cmd_system(struct virt_sys *sys, char *cmd, int len)
 {
+	SHELL_CMD_AUTH(sys, G);
+
 	if (!strcasecmp(cmd, "CLEAR")) {
 		guest_system_reset_clear(sys);
 		con_printf(sys->con, "STORAGE CLEARED - SYSTEM RESET\n");
--- a/cp/shell/directory.c	Sat Nov 26 17:27:19 2011 -0500
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-/*
- * (C) Copyright 2007-2010  Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
- *
- * This file is released under the GPLv2.  See the COPYING file for more
- * details.
- */
-
-#include <errno.h>
-#include <directory.h>
-
-#include "directory_structs.c"
-
-struct user *find_user_by_id(char *userid)
-{
-	struct user *u;
-
-	if (!userid)
-		return ERR_PTR(-ENOENT);
-
-	u = directory;
-
-	for (; u->userid; u++)
-		if (!strcasecmp(u->userid, userid))
-			return u;
-
-	return ERR_PTR(-ENOENT);
-}
--- a/cp/shell/objs.cmake	Sat Nov 26 17:27:19 2011 -0500
+++ b/cp/shell/objs.cmake	Sat Nov 26 22:46:47 2011 -0500
@@ -1,1 +1,1 @@
-set(FILES init.c directory.c cmds.c disassm.c)
+set(FILES init.c cmds.c disassm.c)