/*
 *	Network Queueing System (NQS)
 *  This version of NQS is Copyright (C) 1992  John Roman
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 1, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
/*
*  PROJECT:     Network Queueing System
*  AUTHOR:      John Roman
*
*  Modification history:
*
*       Version Who     When            Description
*       -------+-------+---------------+-------------------------
*       V01.10  JRR                     Initial version.
*       V01.20  JRR     16-Jan-1992	Added support for RS6000.
*       V01.30  JRR     12-Feb-1992	Fixed reference to static routines.
*	V01.4	JRR	17-Jun-1992	Added header.
*	V01.5	JRR	09-Mar-1993	Added Boeing enhancement for files.
*	V01.6	JRR	28-Feb-1994	Added support for SOLARIS.
*/
/*++ nqsmktrans.c - Network Queueing System
 *
 * $Source: /usr2/jrroma/nqs/nqs-3.35.6/utility/RCS/nqsmktrans.c,v $
 *
 * DESCRIPTION:
 *
 *
 *	Construct all of the necessary transaction descriptors as
 *	determined by the MAX_TRANSACTS parameter in nqs.h.
 *
 *
 *	WARNING:  If the bitwise definition of a transaction
 *		  descriptor is ever changed (see ../lib/transact.c),
 *		  then this program must be changed appropriately!
 *
 *
 *	Author:
 *	-------
 *	Brent A. Kingsbury, Sterling Software Incorporated.
 *	March 20, 1986.
 *
 *
 * STANDARDS VIOLATIONS:
 *   None.
 *
 * REVISION HISTORY: ($Revision: 1.6 $ $Date: 1994/03/30 20:45:59 $ $State: Exp $)
 * $Log: nqsmktrans.c,v $
 * Revision 1.6  1994/03/30  20:45:59  jrroma
 * Version 3.35.6
 *
 * Revision 1.5  93/07/13  21:35:00  jrroma
 * Version 3.34
 * 
 * Revision 1.4  92/12/22  15:54:26  jrroma
 * Version 3.30
 * 
 * Revision 1.3  92/02/12  10:52:58  jrroma
 * Fix reference to static routine.
 * 
 * Revision 1.2  92/01/17  13:03:53  jrroma
 * Added support for RS6000.
 * 
 * Revision 1.1  92/01/17  13:03:05  jrroma
 * Initial revision
 * 
 *
 */

#include "nqs.h"			/* NQS types and definitions */
#include <grp.h>
#include <pwd.h>
#include <errno.h>
#include <string.h>
#include "nqsdirs.h"			/* NQS directory and file hierarchy */
#if	BSD43 | ULTRIX | DECOSF
#include <sys/time.h>
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
struct utimbuf {
	time_t acctime;			/* Access time */
	time_t modtime;			/* Modification time */
};
#else
BAD SYSTEM TYPE
#endif
#endif
#include <sys/stat.h>			/* Must be included after nqs.h */
					/* since nqs.h includes sys/types.h */

#ifndef __CEXTRACT__
#if __STDC__

static int set_mtime ( char *path, unsigned long ulongval );

#else /* __STDC__ */

static int set_mtime (/* char *path, unsigned long ulongval */);

#endif /* __STDC__ */
#endif /* __CEXTRACT__ */

#if	SGI
#else
extern char *sys_errlist[];
#endif

/*** main
 *
 *
 *	int main():
 */
main ()
{
	struct stat stat_buf;		/* Stat buffer */
	char path [MAX_PATHNAME+1];	/* NQS pathname */
	char linkpath [MAX_PATHNAME+1];	/* NQS link pathname */
	struct group *group;		/* Group entry for NQS_GROUP */
	struct passwd *passwd;		/* Password entry for NQS_OWNER */
	register int i;			/* Iteration counter */
	int fd;				/* File-descriptor */
        char *base_dir;

        if ( ! buildenv()) {
                fprintf (stderr, "Unable to establish directory ");
                fprintf (stderr, "independent environment.\n");
                exit(1);                /* Exit */
        }
	if (getuid() != 0) {
		/*
		 *  You must be running as root to run this utility.
		 */
		printf ("Utility [nqsmktrans]:  User is not root.\n");
		printf ("Utility [nqsmktrans]:  Aborting.\n");
		fflush (stdout);
		fflush (stderr);
		exit (1);
	}
	/*
	 *  We are definitely running as root.
	 *  Verify that the NQS_GROUP as defined in the NQS Makefile
	 *  exists, and set our real group-id to be NQS_GROUP.
	 */
#ifndef	NQS_GROUP
The symbol NQS_GROUP must be exported from the Makefile invocation.
#else
	group = getgrnam (NQS_GROUP);
#endif
	if (group == (struct group *) 0) {
		/*
		 *  The NQS_GROUP as configured is not known on the
		 *  local system!
		 */
		printf ("Utility [nqsmktrans]:  NQS_GROUP def invalid.\n");
		printf ("Utility [nqsmktrans]:  Aborting.\n");
		fflush (stdout);
		fflush (stderr);
		exit (2);
	}
	if (setgid (group->gr_gid) == -1) {
		/*
		 *  The setgid() call failed!
		 */
		printf ("Utility [nqsmktrans]:  Unable to setgid (%1d).\n",
			group->gr_gid);
		printf ("Utility [nqsmktrans]:  Reason: %s.\n",
			sys_errlist [errno]);
		printf ("Utility [nqsmktrans]:  Aborting.\n");
		fflush (stdout);
		fflush (stderr);
		exit (3);
	}
	/*
	 *  Verify that the NQS_OWNER as defined in the NQS Makefile
	 *  exists.
	 */
#ifndef	NQS_OWNER
The symbol NQS_OWNER must be exported from the Makefile invocation.
#else
	passwd = getpwnam (NQS_OWNER);
#endif
	if (passwd == (struct passwd *) 0) {
		/*
		 *  The NQS_OWNER as configured is not known on the
		 *  local system!
		 */
		printf ("Utility [nqsmktrans]:  NQS_OWNER def invalid.\n");
		printf ("Utility [nqsmktrans]:  Aborting.\n");
		fflush (stdout);
		fflush (stderr);
		exit (4);
	}
        /*
         *  Change our directory to the NQS root directory to begin
         *  our work.  Construct the root directory name by prefixing
         *  the Nqs_root definition with the first argument specified
         *  on the command line.
         */

        base_dir = getfilnam (Nqs_root, SPOOLDIR);
        if (base_dir == (char *)NULL) {
                printf ("Utility [nqsmktrans]: Unable to determine ");
                printf ("root directory name.\n");
                exit (5);
        }

        if (chdir (base_dir) == -1) {
                /*
                 *  Unable to change directory to the NQS root directory.
                 */
                printf ("Utility [nqsmktrans]:  Unable to chdir to %s.\n", 
			base_dir);
                printf ("Utility [nqsmktrans]:  Reason: %s.\n",
                        sys_errlist [errno]);
                printf ("Utility [nqsmktrans]:  Aborting.\n");
                relfilnam (base_dir);
                fflush (stdout);
                fflush (stderr);
                exit (5);
        }
        relfilnam (base_dir);
	/*
	 *  The transaction descriptors that we are going to create, should be
	 *  created with a mode of 777.  Therefore, we clear the umask....
	 */
	umask (0);
	/*
	 *  Build the necessary transaction descriptors, being very careful
	 *  not to disturb any existing transaction descriptors.  We are
	 *  also very careful to construct things in an order in which we
	 *  can gracefully recover from a system crash during the construction
	 *  process.
	 */
	printf ("Utility [nqsmktrans]:  Beginning transaction descriptor ");
	printf ("construction:\n\n");
	fflush (stdout);
	for (i=1; i <= MAX_TRANSACTS; i++) {
		/*
		 *  Remove any leftover temporary link to a transaction
		 *  descriptor element #0 for a transaction descriptor
		 *  that was under construction.
		 */
		pack6name (linkpath, Nqs_transact,
			  (int) ((i-1) % MAX_TDSCSUBDIRS), "TEMP_",
			  (long) i-1, 6, 0L, 0, 0, 1);
		unlink (linkpath);
		/*
		 *  Find out whether or not the transaction descriptor we
		 *  are trying to create already exists.  If it does, then
		 *  DO NOT change it.  The transaction descriptor MIGHT be
		 *  describing an existing NQS request!
		 */
		pack6name (path, Nqs_transact, (int) ((i-1) % MAX_TDSCSUBDIRS),
			  (char *) 0, (long) i-1, 6, 0L, 0, 0, 1);
		if (stat (path, &stat_buf) == -1) {
			/*
			 *  We were unable to stat element #0 of the
			 *  transaction descriptor that we were testing
			 *  to see if it already existed.
			 */
			if (errno != ENOENT) {
				/*
				 *  Aaaak!  Bill the cat.
				 */
				printf ("\nUtility [nqsmktrans]:  Stat() ");
				printf ("error.\n");
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (6);
			}
			/*
			 *  This transaction descriptor does not exist.
			 *  Create it as an unallocated descriptor.
			 *
			 *  Note that we create elements #1, #2, and #3
			 *  first.  Element #0 is created last under
			 *  a temp name, utimed, and then linked in
			 *  under the permanent name so that when a
			 *  transaction descriptor completely appears,
			 *  it is also completely initialized as
			 *  unallocated.
			 */
			pack6name (path, Nqs_transact,
				  (int) ((i-1) % MAX_TDSCSUBDIRS),
				  (char *) 0, (long) i-1, 6, 0L, 0, 1, 1);
			if ((fd = creat (path, 0777)) == -1 ||
			    chown (path, (int) passwd->pw_uid,
				  (int) group->gr_gid) == -1) {
				/*
				 *  This should have worked.
				 */
				printf ("\nUtility [nqsmktrans]:  Error ");
				printf ("creating element #1 of trans: %1d.\n",
					i);
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (7);
			}
			close (fd);
			pack6name (path, Nqs_transact,
				  (int) ((i-1) % MAX_TDSCSUBDIRS),
				  (char *) 0, (long) i-1, 6, 0L, 0, 2, 1);
			if ((fd = creat (path, 0777)) == -1 ||
			    chown (path, (int) passwd->pw_uid,
				  (int) group->gr_gid) == -1) {
				/*
				 *  This should have worked.
				 */
				printf ("\nUtility [nqsmktrans]:  Error ");
				printf ("creating element #2 of trans: %1d.\n",
					i);
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (8);
			}
			close (fd);
			pack6name (path, Nqs_transact,
				  (int) ((i-1) % MAX_TDSCSUBDIRS),
				  (char *) 0, (long) i-1, 6, 0L, 0, 3, 1);
			if ((fd = creat (path, 0777)) == -1 ||
			    chown (path, (int) passwd->pw_uid,
				  (int) group->gr_gid) == -1) {
				/*
				 *  This should have worked.
				 */
				printf ("\nUtility [nqsmktrans]:  Error ");
				printf ("creating element #3 of trans: %1d.\n",
					i);
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (9);
			}
			close (fd);
			if (i == MAX_TRANSACTS) {
				/*
				 *  We are completing the construction of
				 *  the final transaction descriptor.  Since
				 *  the NQS daemon when booting tests for
				 *  the existence of all transaction descr-
				 *  iptors (indicated by the MAX_TRANSACTS
				 *  parameter), by reading the very last
				 *  transaction descriptor (descr number
				 *  MAX_TRANSACTS), we MUST make sure that
				 *  ALL previously created transaction
				 *  descriptors are safely written to disk,
				 *  before continuing.
				 */
				sync();	/* Schedule all dirty disk cache */
					/* buffers for writing */
				printf ("\nUtility [nqsmktrans]:  Waiting ");
				printf ("for permanent storage update.\n");
				printf ("Utility [nqsmktrans]:  Wait time ");
				printf ("= 10 seconds.\n");
				fflush (stdout);
				nqssleep (10);
					/* Wait a conservative length of */
					/* time for the permanent storage */
					/* of the system to record the */
					/* state of the previously */
					/* created transaction descriptors */
				printf ("Utility [nqsmktrans]:  Completing ");
				printf ("construction of final transaction ");
				printf ("descriptor.\n");
				fflush (stdout);
			}
			/*
			 *  Now, create what will become element #0 of
			 *  the transaction descriptor under a temporary
			 *  name.
			 */
			pack6name (path, Nqs_transact,
				  (int) ((i-1) % MAX_TDSCSUBDIRS), "TEMP_",
				  (long) i-1, 6, 0L, 0, 0, 1);
			if ((fd = creat (path, 0777)) == -1 ||
/*
 *	Note that we set the modification time of element 0 to reflect
 *	an unallocated transaction descriptor.  This is CRITICAL.
 *	If the format of a transaction descriptor ever changes (see
 *	../lib/transact.c), then this code must change appropriately.
 */
			    set_mtime (path, (unsigned long)
				      (TRA_UNALLOCATED << 30)) == -1 ||
			    chown (path, (int) passwd->pw_uid,
				  (int) group->gr_gid) == -1) {
				/*
				 *  This should have worked.
				 */
				printf ("\nUtility [nqsmktrans]:  Error ");
				printf ("creating element #0 of trans: %1d.\n",
					i);
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (10);
			}
			close (fd);
			/*
			 *  Now, link-in the last element of the descriptor
			 *  being created.
			 */
			pack6name (linkpath, Nqs_transact,
				  (int) ((i-1) % MAX_TDSCSUBDIRS),
				  (char *) 0, (long) i-1, 6, 0L, 0, 0, 1);
			if (link (path, linkpath) == -1 ||
			    unlink (path) == -1) {
				/*
				 *  This should have worked.
				 */
				printf ("\nUtility [nqsmktrans]:  Error ");
				printf ("completing construction of\n");
				printf ("trans-descr: %1d.\n", i);
				printf ("Utility [nqsmktrans]:  Reason: %s.\n",
					sys_errlist [errno]);
				printf ("Utility [nqsmktrans]:  Aborting.\n");
				fflush (stdout);
				fflush (stderr);
				sync();	/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* and the transaction descrs them- */
					/* selves), are securely written to */
					/* disk */
				exit (11);
			}
		}
		printf ("%6d", i);
		if (i % 13 == 0) putchar ('\n');
		fflush (stdout);
	}
	printf ("\nUtility [nqsmktrans]:  NQS transaction descriptor ");
	printf ("construction complete.\n");
	printf ("Utility [nqsmktrans]:  Exiting.\n");
	fflush (stdout);
	fflush (stderr);
	sync();				/* Schedule all dirty system I/O */
					/* buffers for writing (so that */
					/* directory blocks referring to the */
					/* newly created transaction descrs, */
					/* (and the transation descrs them- */
					/* selves), are securely written to */
					/* disk */
	exit (0);
}


/*** set_mtime
 *
 *
 *	int set_mtime():
 *
 *	Set the modification time of the specified transaction
 *	descriptor element inode.
 *
 *	Returns:
 *		 0: if successful.
 *		-1: if unsuccessful (errno has reason).
 */
static int set_mtime (path, ulongval)
char *path;				/* Transaction descriptor path */
unsigned long ulongval;			/* Unsigned long datum */
{
#if	BSD43 | ULTRIX | DECOSF
	struct timeval utimbuf [2];	/* Utimes() buffer */
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	struct utimbuf utimbuf;		/* utime() buffer */
#else
BAD SYSTEM TYPE
#endif
#endif
#if	BSD43 | ULTRIX | DECOSF
	time (&utimbuf [0].tv_sec);	/* Access time = current time */
	utimbuf [0].tv_usec = 0;
	utimbuf [1].tv_sec = ulongval;
	utimbuf [1].tv_usec = 0;
	return (utimes (path, utimbuf));
#else
#if	HPUX | SGI | SOLARIS | SYS52 | IBMRS | LINUX
	time (&utimbuf.acctime);	/* Access time = current time */
	utimbuf.modtime = ulongval;
	return (utime (path, &utimbuf));
#else
BAD SYSTEM TYPE
#endif
#endif
}
