#
/*
*	SCCS File Type - 
*
* 	What identification string
*/
static char *_S_dpeutilc =      "@(#)dpe_util.c	1.13";
#include "DPE.epl"
#include "KERcalls.h"
#include "NKSRdefs.h"
#include "NKedit.h"
#include "KSOSpaths.h"


#define IO_EOF         0
#define IO_FAIL       -1
#define LPD_EVENT     '\151'
#define NON_KCALL     " "
#define NO_ERR        XOK
#define PRTMODE        0666
#define XpostEh        10601

openDescriptor prt_od;
seid           prt_seid;
K_err_num      error;
ioStatus       errs;
seid           lpd_seid;
int            devfun_read;
p_block        null_Pblk;
char notvalid = ENDSTRING;
char *yesorno();
/*****************************************************************************

COPYRIGHT:  1979, by Ford Aerospace and Communications Corp.
ADDRESS:    Ford Aerospace and Communications Corp.
			Western Development Laboratories
			3939 Fabian Way
			Palo Alto, California 94303
			Attention: Software Technology Dept.
			Mail Stop: V02 

CPCI:  KSOS NKSR
CP:
TAG:  DPE
MODULE NAME:
				DEVICE PROFILE EDITOR
REFERENCES:
  A SPECS:  None
  B5 SPECS: None

HISTORY:
  AUTHOR:  Nanette E. Harter
  VERSION:  1.13 of 10/21/81
  MODULE TYPE: 12:59:13

******************************************************************************

PURPOSE
		The editor is a record editor which
		will edit a specific file of records, devpdb--
		device profile database, modifying
		the records or fields in the records.
		The purpose of the editor is:
		a) To have an editor that runs on KSOS without an Emulator.
		b) To have a field editor that does not allow the entry of
		   incorrect records.
		c) To have a editor that provides a nice user interface.
		   Such as using a name for a security compartment rather than
		   using the bits representation as input.


DESCRIPTION
		The main procedure of the editor will
		create a temporary file to be used in editing,
		accept the the commands from the user,
		and call the correct procedure for action.

ASSERT
		Dpe is invoked by the secure server and assumes
		security checks have already been performed

CALLED BY       None

CALLS           dpefiles, add, change, delete, find, print, view, quit
		printf

*****************************************************************************/
main()
{
	extern  char      *Tempfile;
	extern  dpstruct  Dprecord;
	extern  namearray Devicelist[];
	extern  int       Numofdevs;
	extern  char      Edtor_comds[];
	char 	command[20];

	/*
	 * open dpfile and create tempfile
	 * read in first set of blocks and
	 * create lists
	 */
	Tempfile = "/sys/tmp/dpdbtemp";
	dpefiles();

	while (FOREVER)
	{
		/* print prompt and receive command */
		printf ("> ");
		scaner(command);

		switch (command[0])
		{
			case 'a': dpeadd();    break;
			case 'c': dpechange(); break;
			case 'p': dpeprint();  break;
			case 'v': dpeview();   break;
			case 'd': delete(Devicelist,Numofdevs); break;
			case 'f': find(Devicelist,Numofdevs);   break;
			case 'n': next(Devicelist,Numofdevs);   break;
			case 'q': quit(Devicelist,Numofdevs,dpdbPath,&Dprecord,sizeof(Dprecord));
			default:  printf("%s", Edtor_comds);
		}
	}
}

/*****************************************************************************

PURPOSE
		Add is a module to handle the addition of a new
		record to the dpdb file

DESCRIPTION
		The add command will prompt the user for
		the info to complete the new record,
		check the info for feasibility,
		and append new record to end of file.

CALLED BY       main

CALLS           getname, getseid, getdpetii, wrtnewrec
		getadmin, getgid, getda, getaccesslev,

*****************************************************************************/
dpeadd()
{
	extern  dpstruct  Dprecord;
	extern  namearray Devicelist[];
	extern  int       Numofdevs;
	extern  int       Index;
	extern  int       Changed_flag;
	char    *name;
	char    *password;

	Index = Numofdevs++;

	Devicelist[Index].Name[0] = ENDSTRING;
	blankout (Dprecord.deviceName,NAMELENGTH);
	name = getname(Devicelist,Numofdevs);
	strcopy (Dprecord.deviceName,name,NAMELENGTH);
	strcopy (Devicelist[Index].Name,name,NAMELENGTH);

	getseid(&Dprecord.deviceSeid);

	Dprecord.device_tii.tii_owner = getadmin(OWNER);

	Dprecord.device_tii.tii_group = getgid();

	printf("\nDoes this device allow Valid_required ? (y or n) : ");
	Dprecord.valid_required = yesorno();

	printf("\nCan user assign with assign function ? (y or n) : ");
	Dprecord.assign_required = yesorno();

	Dprecord.device_tii.da = getda();

	Dprecord.device_tii.tii_priv = NOTAPPLICABLE;

	printf("\nENTER ACCESS LEVEL DEFAULT\n");
	getaccesslev(&Dprecord.device_tii.access_level);

	wrtnewrec(&Dprecord,sizeof(Dprecord));

	Changed_flag = ON;

	return;
}

/*****************************************************************************

PURPOSE
		Print is a module which will cause printing of the
		information in dpdb records to the lineprinter.

CALLED BY       main

CALLS           none

*****************************************************************************/
dpeprint()
{
	printf("Printing Device Profile Data Base\n");
	find_LPD();
	printf("Calling print_dpdb\n");
	print_dpdb();
	printf("Completed printing\n");
}

/*****************************************************************************

PURPOSE
		View is a module which will cause printing of the
		information in database records to the standard output.

DESCRIPTION     Check for empty database,read in the desired record,
		print it

CALLED BY       main

CALLS           dview

*****************************************************************************/
dpeview()
{
	extern  dpstruct Dprecord;
	extern  int       Index;

	if (Index == ERROR)
	{
		printf ("No record.\n");
		return;
	}

	/* read record from file */
	readarec(&Dprecord,sizeof(Dprecord));

	/* print the record */
	dview();
}
dview()
{
	extern  dpstruct Dprecord;

	printf("\nName: %-8s\n",Dprecord.deviceName);
	printseid (Dprecord.deviceSeid);
	printf("Owner name: %-8s  ", findusername(Dprecord.device_tii.tii_owner));
	printf("Owner id: %d\n",Dprecord.device_tii.tii_owner);
	printf("Group name: %-8s  ", findgroupname(Dprecord.device_tii.tii_group));
	printf("Group id: %d\n",Dprecord.device_tii.tii_group);
	printf("Valid_required: %c\n",Dprecord.valid_required);
	printf("Assignment required: %c\n",Dprecord.assign_required);
	printf("Discretionary access: ");
	printda(Dprecord.device_tii.da);
	printf("Access level:");
	prtlevel(&Dprecord.device_tii.access_level);
}
printda(da)
int	da;
{
	int	mask;
	int	i;
	
	for (mask = 0400, i = 8; i >= 0; mask >>= 1, i--)
	{
		if ( da & mask )
			switch (i % 3)
			{
				case 0: printf ("x"); break;
				case 1: printf ("w"); break;
				case 2: printf ("r"); break;
			}
		else
			printf ("-");
	}
	printf ("\n");
	return;
}

/*****************************************************************************


PURPOSE
		Change is a module to allow a record from the dpdb file
		to be modified.

DESCRIPTION
		The first field of the record currently pointed to
		will be echoed,
		The user is prompted for the field to edit,
		The data is asked for, and
		The changes made.

CALLED BY       main

CALLS           readarec, wrtarec, prtlevel, getuid, dview
		getadmin, findusername, findgroupname, getaccesslev,
		printf,scaner

*****************************************************************************/
dpechange()
{
	extern  dpstruct Dprecord;
	extern  char      Dpchang_comds[];
	extern	namearray Devicelist[];
	extern  int       Numofdevs;
	extern	int	 Index;
	extern  int	 Changed_flag;
	char    command[LINELENGTH];
	char    *name;

	if (Index == ERROR)
	{
		printf ("No record.\n");
		return;
	}

	readarec(&Dprecord,sizeof(Dprecord));

	printf ("Changing record %-8s\n\n", Dprecord.deviceName);
	printf ("x will terminate change\n\n");

	while(FOREVER)
	{
		printf ("\nEnter field name to change : ");
		scaner(command);

		switch (command[0])
		{
			case 'x':              /* carriage return */
				wrtarec(&Dprecord,sizeof(Dprecord));
				Changed_flag = ON;
				return;

			case 'v':
				dview();
				break;;

			case 'n':
				printf("Current name: %-8s\n",Dprecord.deviceName);
				Devicelist[Index].Name[0] = ENDSTRING;
				blankout (Dprecord.deviceName,NAMELENGTH);
				name = getname(Devicelist,Numofdevs);
				strcopy (Dprecord.deviceName,name,NAMELENGTH);
				strcopy (Devicelist[Index].Name,name,NAMELENGTH);
				break;
			
			case 'o':
				printf("Owner name: %-8s  ",
				findusername(Dprecord.device_tii.tii_owner));
				printf("Owner id: %d\n",Dprecord.device_tii.tii_owner);
				Devicelist[Index].Uid  = -1;
				Devicelist[Index].Uid =
				Dprecord.device_tii.tii_owner = getadmin(OWNER);
				break;
			
			case 'g':
				printf("Group name: %-8s  ",
				findgroupname(Dprecord.device_tii.tii_group));
				printf("Group id: %d\n",Dprecord.device_tii.tii_group);
				Dprecord.device_tii.tii_group = getgid();
				break;
			
			case 'a':
				printf("Current access level:\n");
				prtlevel(&Dprecord.device_tii.access_level);
				getaccesslev(&Dprecord.device_tii.access_level);
				break;
			
			case 'r':
				printf("\nValid_required: %c\n",Dprecord.valid_required);
				printf("Does this device allow Valid_required ? (y or n) : ");
				Dprecord.valid_required = yesorno();
				break;


			case 'l':
				printf("\nAssignment required: %c\n",Dprecord.assign_required);
				printf("Can user assign with assign function ? (y or n) : ");
				Dprecord.assign_required = yesorno();
				break;

			case 'd':
				printf("Discretionary access: ");
				printda(Dprecord.device_tii.da);
				Dprecord.device_tii.da = getda();
				break;
			
			case 's':
				printseid (Dprecord.deviceSeid);
				getseid(&Dprecord.deviceSeid);
				break;
			
			default: printf ("%s", Dpchang_comds);
		}
	}

}

/*****************************************************************************

PURPOSE
		Set up dpfile and temp dp file

DESCRIPTION
		open uaadb and create namelist of user names and ids,
		open gaadb and create grouplist of group names and ids,
		open dpdb, create a tempfile and create a devicelist of
		devices and ids, read in first buffer.

ASSUMES         assumes dpdb, uaadb and gaadb name field filler is blanks and
		not a null char

CALLED BY       main

CALLS           mkseclist, mknamelist, mkgrplist, mkdptemp
		printf

*****************************************************************************/
dpefiles()
{
	mkseclist();
	mknamelist();
	mkgrplist();
	mkdptemp();
}
mkdptemp()
{
	extern  char      *Tempfile;
	extern  dpstruct  Dprecord;     /* stores a uaadb record */
	extern  int       Index;        /* offset into input buffer */
	extern  namearray Devicelist[];
	extern  int       Numofdevs;    /* number of entry in the devicelist */
	extern  int       F1, F2;       /* file descriptors */
	int     n;
	int     lindex;                 /* local index */

	/* open dpdb */
	if ((F1 = N_open(dpdbPath, READ)) == -1)
	{
		printf("Can't open device profile data base.\n");
		printf("Do you wish to create it (y or n) ?");
		if ( yesorno() == YES )
		{
			if ((F1 = N_creat(dpdbPath, READ, PMODE)) == -1)
			{
				printf("Can't create device data base.\n");
				K_release_process (null_seid);
			}
		}
		else    /* NO - don't create data base */
			K_release_process (null_seid);
	}

	/* create temporary file of dpdb which is to be used for editing */

	if ((F2 = N_create (Tempfile, RW, PMODE)) == -1)
		printf("can't create dpdbtemp\n");

	if ( F2 == -1 || F1 == -1 )
		K_release_process (null_seid);


	/* copy file */
	lindex = 0;
	while((n = N_read(F1,&Dprecord,sizeof(Dprecord))) > 0)
		if(N_write(F2,&Dprecord,n) != n)
			printf("write error while creating temp file\n");
                else
		{
			strcopy(Devicelist[lindex].Name, Dprecord.deviceName,NAMELENGTH);
			Devicelist[lindex++].Uid = Dprecord.tii.tii_owner;
		}
  
	if ((Numofdevs = lindex) == ZERO)
		Index = -1;
	else
		Index = 0;

	N_flush(F2);
	N_close(F1);
}

/*****************************************************************************

PURPOSE
		Get seid defines a new seid for the new device

CALLED BY       add

CALLS           none

*****************************************************************************/
getseid(dev_seid)
	seid *dev_seid;
{
	int  num;
	char cc;

	*dev_seid = null_seid;
	while (FOREVER)
	{
		printf ("\nEnter desired device name space: ");
		if (getdec (&num, &cc))
		{
			dev_seid->nsp = num & 0377;
		printseid (*dev_seid);
			break;
		}
		else
			printf ("not a number\n");

	}

	while (FOREVER)
	{
		printf ("\nEnter desired device type (high byte): ");
		if (getdec (&num, &cc))
		{
			dev_seid->uniq_id0 = num & 0377;
		printseid (*dev_seid);
			break;
		}
		else
			printf ("not a number\n");

	}

	while (FOREVER)
	{
		printf ("\nEnter desired device unit number (low word): ");
		if (getdec (&num, &cc))
		{
			dev_seid->uniq_id1 = num;
		printseid (*dev_seid);
			break;
		}
		else
			printf ("not a number\n");

	}

}

/***************************************************************************

PURPOSE
	Print a seid in a readable format.

CALLED BY
	dview, dpechange.

****************************************************************************/
printseid  (dev_seid)
	seid dev_seid;
{

	printf ("Seid: %d-%d-%d\n", namespace((dev_seid)), dev_seid.uniq_id0,
				     dev_seid.uniq_id1);
}

/*****************************************************************************

PURPOSE
		Getda returns the octal discretionary access code

DESCRIPTION     Prompts the user for owner, group, and all desired
		descretionary access. Expects r,w,x, or a carriage
		return for null. Checks and continues asking until
		valid input is received. Converts information to octal
		and returns that value.

CALLED BY       add, change

CALLS           readda, dacommands, printf

*****************************************************************************/
getda()
{
	int	mode;
	int	access;

	printf ("\nEnter desired discretionary access for owner: ");
	while (readda(&access))
		dacommands();
	mode = access * USERDA;

	printf ("\nEnter desired discretionary access for group: ");
	while (readda(&access))
		dacommands();
	mode += access * GROUPDA;

	printf ("\nEnter desired discretionary access for all: ");
	while (readda(&access))
		dacommands();
	mode += access;

	return(mode);
}

dacommands()
{
	printf ("\nValid discretionary modes are r)ead, w)rite, and x)ecute.\n");
	printf ("A carriage return for no permissions\n");
	printf ("Enter desired access mode: ");
}

/*****************************************************************************

PURPOSE
		Readda set the octal value of the individual part of
		da and returns an error flag.

DESCRIPTION     Flags are turned off, the user input line is scaned
		character by character, invalid input cause an error flag
		of 1 to be returned, valid input is converted to octal
		and 0 returned.

CALLED BY       getda

CALLS           getchar

*****************************************************************************/
readda(da)
int	*da;
{
	int     r,w,x;            /* flags */
	char    c;                /* input character */

	/* zero discretionary access */
	*da = 0;
	r = w = x = OFF;

	while ((c = getchar()) != NEWLINE)
	{
		switch(c)
		{
			case 'R': r = ON; break;
			case 'r': r = ON; break;
			case 'W': w = ON; break;
			case 'w': w = ON; break;
			case 'X': x = ON; break;
			case 'x': x = ON; break;
			case ' ': break;
			case '/': break;
			case ',': break;
			default:
			{
				while (getchar() != NEWLINE);
				printf ("\nUnrecognized mode.\n");
				return(ON);
			}
		}
	}
	if (r)
		*da += 4;
	if (w)
		*da += 2;
	if (x)
		*da += 1;

	return(OFF);
}

/***************************************************************
		The following routines are in support of the
		dpeprint module and cause the Device Profile
		Data Base to be written to the file /sys/tmp/prtdp
		and the seid of that file to be sent via an
		IPC message to the Printer Daemon.
***************************************************************/

find_LPD()
{
	int     result;                 /* result returned by kernel */
	seid    lpd_file[128];      /* contents of /lpdseid */
	openDescriptor lpd_od;      /* open descriptor to /lpdseid */
	char *lpd_path = "/lpdseid";
	
	if ((lpd_od = N_open(lpd_path, READ)) == IO_FAIL)
		abort("Cannot open LPD seid file", NON_KCALL, NO_ERR);
	printf("Found /lpdseid\n");
	if ((result = N_read(lpd_od, &lpd_file, 512)) == IO_FAIL)
		abort("Cannot read LPD seid file", NON_KCALL, NO_ERR);
	lpd_seid = lpd_file[0];
	if ((result = N_close(lpd_od)) == IO_FAIL)
		abort("Cannot close LPD seid file", NON_KCALL, NO_ERR);
}

print_dpdb()
{
	dpstruct entry;              /* entry in the dp data base */
	openDescriptor dp_opendes;     /* open descriptor to dpdbtemp */
	int     result;                 /* value returned by buffered io */
	char *dp_path = "/sys/tmp/dpdbtemp";
	
	zero_prt();                     /* clear print file, get prt_od */
	buffString("The Device Profile Data Base\n\n");
	if ((dp_opendes = N_open(dp_path, READ)) == IO_FAIL)
		abort("Cannot open the dp data base", NON_KCALL, NO_ERR);
	while(TRUE)
	{
		if ((result = N_read(dp_opendes, &entry, sizeof(entry))) == IO_FAIL)
			abort("Cannot read from the dp data base", NON_KCALL, NO_ERR);
		if (result == IO_EOF)
			break;
		put_dp_entry(&entry);
	}
	if ((result = N_flush(prt_od)) == IO_FAIL)
		abort("Cannot write to print file", NON_KCALL, NO_ERR);
	if ((result = N_close(dp_opendes)) == IO_FAIL)
		abort("Cannot close the dp data base", NON_KCALL, NO_ERR);
	if ((result = N_close(prt_od)) == IO_FAIL)
		abort("Cannot close the print file", NON_KCALL, NO_ERR);
	send_to_LPD();  /* dispatch IPC message to Printer Daemon */
}


put_dp_entry (record)
dpstruct *record;
{
	buffString("Name :  ");
	buffString((*record).deviceName);
	buffString("  ");
	buffSeid((*record).deviceSeid);
	buffString(",   Discretionary Access :  ");
	buffDa((*record).device_tii.da);
	buffString("\n    Owner Name :  ");
	buffString(findusername((*record).device_tii.tii_owner));
	buffString(",   Id :  ");
	buffDecC((*record).device_tii.tii_owner);
	buffString(",   Group Name :  ");
	buffString(findgroupname((*record).device_tii.tii_group));
	buffString(",   Id :  ");
	buffDecC((*record).device_tii.tii_owner);
	buffString(",   Valid ?  ");
	buffChar((*record).valid_required);
	buffString(",   Assign ?  ");
	buffChar((*record).assign_required);
	buffString("\n    Access Level :  ");
	buffLevel(&(*record).device_tii.access_level);
}

send_to_LPD()
{
	msg_struct  ipc;                /* IPC message buffer */
	
	ipc[0] = LPD_EVENT;
	ipc[1] = '\0';
	ipc[2] = prt_seid.nsp & 0377;
	ipc[3] = prt_seid.uniq_id0;
	ipc[4] = prt_seid.uniq_id1 % 256;
	ipc[5] = prt_seid.uniq_id1 / 256;
	printf("Sending msg to LPD\n");
	error = K_post(lpd_seid, FALSE, &ipc);
	if (error != XOK)
		if (error == XpostEh)
			abort("LPD queue is full", "K_post", error);
		else
			abort("Printer Daemon not running", "K_post", error);
}

zero_prt()
{
	cardinal32 size;                /* size for K_devfun call */
	int  result;
	char *prt_file_path = "/sys/tmp/prtdp";
	
	size = 0;
	if ((prt_od = N_open(prt_file_path, RW)) != IO_FAIL)
	{
		printf("Erasing print file\n");
		error = K_device_function(prt_od, ERASEFILE, size,
			      &null_Pblk, &null_Pblk, 0, &errs, &devfun_read);
		if (error != NO_ERR)
			abort("Cannot erase print file", "K_device_function", error);
	}
	else
	{
		if ((prt_od = N_create(prt_file_path, RW, PRTMODE)) == IO_FAIL)
			abort("Cannot create print file", NON_KCALL, NO_ERR);
	}
	if ((result = N_tell_seid(prt_od, &prt_seid)) == IO_FAIL)
		abort("Cannot get seid of print file", NON_KCALL, NO_ERR);
}

buffLevel(level)
access_level_type *level;
{
	extern secarray seclist[];
	int    i;
	
	buffString(" Security Category :  ");
	i = (*level).security_category;
	buffString(seclist[i].shortname);
	buffString(",   Integrity Category :  ");
	i = (*level).integrity_category;
	buffString(seclist[NUMSECCAT+(i)].shortname);
	buffString("\n        Security Compartments :\n            ");
	buffComp(&level->security_compart);
	buffString("\n\n");
}

buffComp(comp)
int *comp;
{
	int compartment[2];
	extern secarray seclist[];
	int any=OFF;
	int mask;
	int j;
	int order;
	
	compartment[0] = *comp;
	compartment[1] = *(comp+1);
	for (mask=1,j=COMPLOW,order=0; j<COMPHIGH; mask <<= 1, j++)
	{
		if (j == HALFWAY)
		{
			order = 1;
			mask = 1;
		}
		if ((compartment[order] & mask) && (seclist[j].activ == ACTIVE))
		{
			buffString(seclist[j].shortname);
			buffString(" = ");
			buffDecC(j-NUMCAT);
			buffString("  ");
			any = ON;
		}
	}
	if (any == OFF)
	buffString("NULL");
}

buffSeid (aseid)
seid aseid;
{
	buffDecC(aseid.nsp & 0377);
	buffString("/");
	buffDecC(aseid.uniq_id0);
	buffString("/");
	buffDecC(aseid.uniq_id1);
}

buffDa (da)
int da;
{
	int i, mask;

	for (mask = 0400, i = 8; i >= 0; mask >>= 1, i--)
	{
		if (da & mask)
			switch (i % 3)
			{
				case 0: buffString("x"); break;
				case 1: buffString("w"); break;
				case 2: buffString("r"); break;
			}
		else
			buffString("-");
	}
}

buffChar (bchar)
char bchar;
{
	char str[2];

	str[0] = bchar;
	str[1] = '\0';
	buffString(str);
}

buffDecC (num)
int num;                          /* decimal integer to be printed */
{
	char    *str;
	
	itoa(num, &str, 10);
	buffString(&str);
}

buffC32 (num)
cardinal32 num;                   /* double word to be printed */
{
	char    *str;
	int     num1;
	
	num1 = num / 65536;
	itoa(num1, &str, 10);
	buffString(&str);
	buffString("/");
	num1 = num % 65536;
	itoa(num1, &str, 10);
	buffString(&str);
}

itoa (num, str, base)
int num;                          /* number to be converted */
char str[];                        /* ascii string */
int base;                         /* base for conversion */
{
	int     i, j, val;
	char    str1[6];
	char *digits = "0123456789";
	
	val = num;
	for (i=0; i<=5; i++)
		str[i] = '\0';
	for (i=0; i<=5; i++)
	{
		j = val % base;
		str1[i] = digits[j];
		val = val / base;
		if (val == 0)
			break;
	}
	for (j=0; j<=5; j++)
	{
		str[i--] = str1[j];
		if (i < 0)
			break;
	}
}


buffString (string)
char *string;                     /* string to be printed */
{
	int     n, result;
	
	n = strlen(string);
	if ((result = N_write(prt_od, string, n)) == IO_FAIL)
		abort("Cannot write to print file", NON_KCALL, NO_ERR);
}

abort(err_msg, call_name, exception)
char *err_msg;                        /* error message to print */
char *call_name;                      /* kernel call, if any */
int exception;                        /* error returned from kernel call */
{
	printf("%s\n", err_msg);
	if (exception != NO_ERR)
		printf("%s returns %d\n", call_name, exception);
	printf("Aborting DPE\n");
	K_release_process(null_seid);
}


strlen(s)
char s[];                             /* string to be sized */
{
	int i;
	
	i = 0;
	while (s[i] != '\0')
		++i;
	return(i);
}
