static char dqs_exec_job_rcsid[]="$Id: dqs_exec_job.c,v 1.5 1998/10/26 18:47:08 green Exp $";

/*----------------------------------------------------
 * dqs_exec_job.c Tom Green Mon Jan 31 10:42:42 1994
 *
 * Copyright 1993
 *
 * SUPER COMPUTER COMPUTATIONS RESEARCH INSTITUTE
 *            FLORIDA STATE UNIVERSITY
 *
 *
 * SCRI representatives make no claims about the
 * suitability of this software for any purpose.
 * It is provided "as is" without express or
 * implied warranty.
 *
 * $Log: dqs_exec_job.c,v $
 * Revision 1.5  1998/10/26 18:47:08  green
 * fixed exit status bug
 *
 * Revision 1.4  1998/10/13 15:08:48  green
 * Solaris 2.6 support - hopefully, as I don't have access to one...
 *
 * Revision 1.3  1998/10/13 13:49:50  green
 * setenv NUM_HOSTS
 *
 * Revision 1.2  1998/10/13 13:38:45  green
 * write out a host file if more than one queue is utilized
 * environmental variable HOSTS_FILE set to reflect name of file
 *
 *
 * Revision 1.1.1.1  1998/08/18 14:39:11  green
 * DQS 3.2.0.5 WIP Import
 *
 * Revision 1.4  1997/11/04 18:48:36  decker
 * Modifications and patches by Doug G
 *
 * Revision 1.3  1997/08/08 19:51:20  decker
 * Patch by Janssen for Linux
 *
 * Revision 1.2  1997/08/06 15:24:29  decker
 * Added Curtis Janssen Patch for Linux.
 *
 * Revision 1.1.1.1  1997/04/10 15:10:32  green
 * DQS 3.1.3.4.1 Distribution
 *
 * Revision 3.42  1996/11/20 23:03:38  nrl
 * Several fixes submitted by or as a result of investigations by
 * Ron Lee, Bodo Bechenback, Guntram Wolski and Frank Dwyyer.
 *
 * Revision 3.41  1996/06/27  01:55:49  nrl
 * changes to accomodate osf gcc
 *
 * Revision 3.40  1996/06/26  14:03:26  nrl
 * Added pvmcleanup.sh to install.. added osf2.3 detection
 *
 * Revision 3.37  1996/06/23  19:41:08  nrl
 * added missing templates for dqs_write_consumable_list,
 * added message types to log entries
 * setup GNU base for makefile.proto.in
 *
 * Revision 3.36  1996/06/17  02:28:49  nrl
 * Updtaes from Guntram Wolski, Ron Lee, John Makosky and
 * Bodo Beckebach
 *
 * Revision 3.35  1996/04/12  16:17:08  nrl
 * ron lee found error in LEAVE_OUTPUT_FILE test
 *
 * Revision 3.34  1996/03/30  21:32:42  nrl
 * Cleanup prior to tagging to 3.1.3
 *
 * Revision 3.33  1996/03/22  04:20:22  nrl
 * Added error cataloguing number to all routines
 *
 * Revision 3.32  1996/03/21  17:06:47  nrl
 * added fortran and "c" syntax to resource requests
 *
 * Revision 3.31  1996/03/17  02:13:19  nrl
 * merged in "Frank Dwyer Fair Play" scheduling scheme to
 * qsub prevalidation process. Removed redundant mail at begining
 * case.
 *
 * Revision 3.30  1996/03/12  17:12:00  nrl
 * removed aborts and replaced with an error messaging scheme
 * to send email to the dqs adminsitrator and wait for
 * actions by that administrator
 *
 * Revision 3.29  1996/02/07  13:07:57  nrl
 * Added "process leader" and TMP_FILES link capability
 *
 * Revision 3.26  1995/05/29  18:08:49  nrl
 * More solaris stuff GAGGHH had to differentiate more cases of
 * solaris2.3 and solaris2.4 stuff
 *
 * Revision 3.25  1995/05/26  19:07:34  nrl
 * Cleaned up signal handling and the notify option with the
 * help of Ron Lee.
 *
 * Revision 3.24  1995/05/03  18:40:29  nrl
 * Forgot to set "nice" for hpux systems
 *
 * Revision 3.23  1995/03/05  03:47:12  nrl
 * Included Axel Brandes job scheduling mechanism to keep one
 * user from hawging the queue.
 *
 * Revision 3.22  1995/02/19  22:37:51  nrl
 * Simplified pvm and tcgmsg interface to a minimalist scheme requiring
 * user scripts to start the daemons
 *
 * Revision 3.21  1995/02/17  02:56:53  nrl
 * Added formal skeletons for PVM and TCGMSG parallel modes
 *
 * Revision 3.20  1995/01/30  15:21:50  nrl
 * added "tid" verification between execd and qmaster to prevent
 * "ghost" jobs from persisting in visible queue. Changed ERROR messages
 * which were for information only to DEBUG messages.
 *
 * Revision 3.19  1995/01/17  16:32:05  nrl
 * completed mailer feature
 *
 * Revision 3.18  1994/11/14  21:18:16  green
 * DQS_O_LOG_NAME changed to DQS_O_LOGNAME as per the docs
 *
 * Revision 3.17  1994/06/23  20:01:39  green
 * Solaris porting mods...
 *
 * Revision 3.16  1994/06/19  18:38:34  green
 * changed "DQS_P4_STR" to "REMOTE_INFO" for portability
 *
 * Revision 3.15  1994/06/18  12:33:31  green
 * start MPI same as P4
 *
 * Revision 3.14  1994/06/16  22:56:12  green
 * yikes, a number od WAPs when setting the env variables for the
 * parallel packages
 *
 * Revision 3.13  1994/06/16  11:57:22  green
 * minor syntax error...
 *
 * Revision 3.12  1994/06/16  11:13:24  green
 * modified dqs_am_chdir() to return actual cwd vs "qsub"ed cwd.  This
 * bug only affected "#ifdef AUTO_MOUNT"
 *
 * Revision 3.11  1994/06/15  19:17:21  green
 * added exec support for GENERIC_ALL and GENERIC_SLA
 *
 * Revision 3.10  1994/06/09  20:03:44  green
 * added "-par generic_sla" and "-par generic_all" to dqs_parse.c,
 * dqs_exec_job.c and dqs_utility.c
 *
 * forgot a couple ";" on my last checkin of dqs_sec.c(knew I should
 * have compiled before checking in...)
 *
 * Revision 3.9  1994/06/08  17:48:05  green
 * added P4 support(with the help of Ralph Butler - Thanks Ralph!)
 *
 * backed down to Rev. 3.3 of dqs_check_to_do_list.c
 *
 * Revision 3.8  1994/06/06  23:29:33  green
 * fixed bug in generating Makefile.h(actually not a bug - but AIX
 * couldn't dijest it...)
 *
 * added dqs_parse_exec_str.c(and added in Makefile.proto)
 *
 * built a more "generic" conf_file and resolve_file
 *
 * updated dqs_execd.c and dqs_start_p4.c to utilize dqs_parse_exec_str()
 *
 * had DQS_DSHD_BIN and DQS_DSH_BIN misnamed in prognames.h
 *
 * Revision 3.7  1994/06/06  01:23:02  green
 * added "dqs_dshd_service" to the DQS config
 *
 * add "-par pvm" - though we do NOT intend to support it
 * (see dqs_start_pvm.c for a more thorough discussion)
 *
 * Revision 3.6  1994/06/05  16:53:52  green
 * added DQS_DSHD_SERVICE and dqs_dshd_service to required support files
 *
 * forced a SIGQUIT to children on death of parent(eg: "master") in
 * dqs_sig_handlers.c
 *
 * Revision 3.5  1994/06/04  17:38:28  green
 * added support for dqs_start_p4()
 *
 * Revision 3.4  1994/06/03  03:44:03  green
 * updated to support p4/mpi
 *
 * Revision 3.3  1994/05/31  14:27:33  green
 * added the environmental variable "ENVIRONMENT" = "BATCH" in
 * dqs_exec_job.c
 *
 * Revision 3.2  1994/03/23  19:29:08  green
 * forgot to force automount of dirs before the chdir() - rdl
 *
 * Revision 3.1  1994/03/23  15:21:43  green
 * added automount support via dqs_exec_job.c:dqs_am_chdir()
 * and an AUTOMOUNT def in dqs.h
 *
 * Revision 3.0  1994/03/07  04:13:45  green
 * 3.0 freeze
 *
 * Revision 1.6  1994/03/04  16:59:06  green
 * had "stderr_path" and "stdout_path" reversed causing the "-e" and "-o"
 * options to be reversed.
 *
 * Revision 1.5  1994/02/17  14:49:00  green
 * added MAX_KLOG_TIME to def.h
 *
 * added some strategic dqs_set_coresize_2_0() as CYAs
 *
 * nuked some dqs_set_coresize_back_normal()
 *
 * dinked with ALRM handlers for "robustness"
 *
 * NOTE: HPUX does not support core limit size --- sad...
 *
 * Revision 1.4  1994/02/17  14:14:58  green
 * yanked the sleep out of dqs_reauth_job() and placed in qmaster,
 * dqs_execd and dqs_exec_job() such that the sleep only occurs at
 * startup.  This minimizes "re-authing" overhead.
 *
 * The AFS reauthing will have to be rewritten anyway since AFS
 * behaves differently on different platforms.(eg: requires reauthing
 * process to be of the same pgroup on "non-AIX" platforms. (UGH! a
 * sheepherder process being required...)
 *
 * Revision 1.3  1994/02/10  20:58:42  green
 * moved "LOG_FILE" and "ERR_FILE" out of def.h to dqs.h
 *
 * fixed "-Passwd passwd_file" option
 *
 * Revision 1.2  1994/02/10  18:24:14  green
 * removed the following options from "qsub"
 *
 *      -k
 *      -p
 *      -u
 *      -z
 *
 * fixed the following options in "qsub"
 *
 *      -e
 *      -o
 *      -N
 *      -v
 *
 * synced with docs
 *
 * Revision 1.1.1.1  1994/02/01  17:57:40  green
 * DQS 3.0 ALPHA
 *
 *--------------------------------------------------*/


#include "h.h"
#include "def.h"
#include "dqs.h"
#include "struct.h"
#include "func.h"
#include "globals.h"
#include "dqs_errno.h"

#include <errno.h>                                                  
extern int errno, sys_nerr;                                         
/*Patch from Curtis Janssen*/
#if !(defined(__linux__) && defined (__GLIBC__) && __GLIBC__ >= 2)
extern char *sys_errlist[];                                         
#endif
FILE *dbf2;

int dbl;



/************************************************************************/
int dqs_exec_job(listel_ptr)
     dqs_list_type *listel_ptr;
     
     /*
       dqs_exec_job - is used soley by the dqs_execd.
       
       this routine is responsible for setting up the process environment
       prior to an execl.
       
     */
     
{
  
  int i,t;
  int one=1;
  int fd;
  int in;
  int out;
  int err;
  char *stdout_path;
  char *stderr_path;
  char *hosts_path;
  char *shell_path;
  char *account_str;
  char dqs_mail_term_msg[128];
  char dqs_mail_subj[1024];
  char dqs_mail_body[1024];
  char dqs_mail_start[128];
  char dqs_mail_end[128];
  dqs_list_type *err_rpt;
  dqs_list_type *mail_users;
  int            mail_options;
  char *cwd;
  FILE *f;
  u_long32 now;
  sigset_t o_mask;
  sigset_t t_mask;
  string str;
  string num_hosts;
  string dir_str;
  struct passwd *pw;
  dqs_job_type *job;
  dqs_queue_type *qconf;
  dqs_list_type  *lp;
  
  dqs_list_type *tmp_sp;                                         
  char          *tmp_cp, **tmp_argv;                             
  int           tmp_argc, tmp_job_pid;                           
  int           retval; /*Change via Doug Gibson*/
  string        tmp_localdir, tmp_exec_file, tmp_pid_local,      
    tmp_slave_local, tmp_stdout_path,                
    tmp_stderr_path;                                 
  int           tmp_pipeok, tmp_pfd[2];                          
  string        tmp_stdout_net, tmp_stderr_net,  /*ab950421*/
    tmp_stdout_local, tmp_stderr_local,              
    tmp_info_local, tmp_info_path;                   
#if (SVR3) /* DAMN this is irratating */
  union wait status;
#else
  int status;
#endif

  char *tmp_input;                                               
  int  tmp_cpu, tmp_ilen, tmp_gid;                          
  FILE  *tmp_ifp;                                                 
  
  
  struct stat test_buf;
  int    test_i;
  
  DENTER((DQS_EVENT,"dqs_exec_job"));
  
  now=dqs_get_gmt();
  tmp_gid= GETPGRP;
  DPRINTF((DQS_EVENT,"dqs_execd gid=%d",tmp_gid));
  
  if ((i=fork())!=0)
    {
      
      listel_ptr->job->hard_wallclock_gmt=0; /* in case we are restarting! */
      listel_ptr->job->pending_signal=0;
      listel_ptr->job->pending_signal_delivery_time=0;
      DEXIT;
      return(i);
    }
  
  alarm(0);
  
  job=listel_ptr->job;
  job->reauth_gmt=0;
  
#ifdef AFSSYS
  /* set a setpag in the child process */
  if (lsetpag() == -1 ) {
    CRITICAL((DQS_EVENT,"DQS_ERROR_0210 error: JID %d  setpag failed\n",job->job_number));
  }
  if (dqs_reauth(job))
    sleep(MAX_KLOG_TIME); 
#endif
  
  qconf=listel_ptr->queue;
  DQS_ASSERT((job));
  DQS_ASSERT((qconf));
  
  if (EXT_DEBUG_ON)
    {
      dqs_show_job(job);
      dqs_show_queue(qconf);
    }
  
  me.qualified_prog_name=dqs_string_insert(me.qualified_prog_name,job->job_name);
  me.unqualified_prog_name=dqs_string_insert(me.unqualified_prog_name,job->job_name);
  
  me.pid=getpid();
  me.ppid=getppid();
  me.pgrp=GETPGRP;
  
  pw=getpwnam(job->owner);
  
  umask(022);
  qconf->tmpdir=dqs_make_tmpdir(qconf,job);
  
  sprintf (tmp_localdir, "%s/local", conf.execd_spool_dir);           
  dqs_mkdir (tmp_localdir, me.uid, me.gid, 1777);                
  chmod (tmp_localdir, 01777);                                   
  
  if ( conf.output_handling!=LEAVE_OUTPUT_FILES) {
    if ((tmp_pipeok = pipe (tmp_pfd)) == -1) {                     
      ERROR ((DQS_EVENT, "DQS_ERROR_0211 (process leader) cannot make pipe (%d %s)",
	      errno, STRERROR ));                           
    }                                                              
  }
  switch (tmp_job_pid = fork ()) {                               
    
  case -1:                                                     
    CRITICAL ((DQS_EVENT,                                      
	       "dqs process leader cannot fork job (%d %s)",    
	       errno, STRERROR ));
    err_rpt=(dqs_list_type *)dqs_malloc(sizeof(dqs_list_type));
    bzero((char *)&err_rpt,sizeof(err_rpt));
    err_rpt->job= job;
    err_rpt->str1= dqs_string_insert(NULL,
				     " dqs process leader cannot fork job ");
    dqs_report_problem(err_rpt, TRUE);                           
    abort ();                                                  
    break;                                                     
    
  case 0 :                     /* I'm the job */
    i=GETPGRP;
    
    DPRINTF(( DQS_EVENT,"I'm the job gid= %d",i));
    me.pid=getpid();                                           
    me.ppid=getppid();                                         
    me.pgrp=GETPGRP;                                           
    
    DPRINTF((DQS_EVENT,                                        
	     "**********************CHILD*********************"));
    DPRINTF((DQS_EVENT,"CHILD - About to exec ->%s< under queue -<%s<",
	     job->job_name,qconf->qname));                     
    
    i= SETPGRP;         
    DPRINTF((DQS_EVENT, "setpgrp() returned %d", i));
    
    dqs_setrlimits(qconf);                                     
    
    sprintf (tmp_slave_local, "%s/%s.s%s.%d", tmp_localdir,    
	     job->job_name, job->dqs_job_name, me.pid);        
    break;                                                     
    /***********************************************************************/
    /***********************  session leader *******************************/
    /**********************************************************************/
    
  default:
    
    
    DPRINTF ((DQS_EVENT," I'm the leader"));
    sprintf (tmp_slave_local, "%s/%s.s%s.%d", tmp_localdir,    
	     job->job_name, job->dqs_job_name, tmp_job_pid);   
    sprintf (tmp_pid_local, "%s/%s.p%s.%d", tmp_localdir,      
	     job->job_name, job->dqs_job_name, me.pid);        
    sprintf (str, "%d", tmp_job_pid);                          
    if ((out = open (tmp_pid_local,                            
		     O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1 ||
	write (out, str, strlen (str)) != strlen (str) ||      
	close (out) == -1) {                                   
      ERROR ((DQS_EVENT,                                       
	      "%s cannot write signal data file (%d %s)",      
	      "(process leader)", errno, STRERROR ));
    }                                                          
    
    
    if( conf.output_handling!= LEAVE_OUTPUT_FILES) {
      if (tmp_pipeok == 0 &&                                     
	  ((tmp_pipeok = read (tmp_pfd[0], tmp_stdout_path,      
			       sizeof (tmp_stdout_path))) != sizeof (tmp_stdout_path) ||
	   (tmp_pipeok = read (tmp_pfd[0], tmp_stdout_local,     
			       sizeof (tmp_stdout_local))) != sizeof (tmp_stdout_local) ||
	   (tmp_pipeok = read (tmp_pfd[0], tmp_stderr_path,      
			       sizeof (tmp_stderr_path))) != sizeof (tmp_stderr_path) ||
	   (tmp_pipeok = read (tmp_pfd[0], tmp_stderr_local,     
			       sizeof (tmp_stderr_local))) != sizeof (tmp_stderr_local))) {
	ERROR ((DQS_EVENT,                                       
		"(process leader) cannot read pipe (%d %s) ",
		errno, STRERROR ));                       
      }                                                          
    }
    
    if( conf.addon_script){
      sprintf (str, "%s -slave startup %s %s %d %d %d %s",       
	       conf.addon_script, tmp_slave_local,               
	       job->dqs_job_name, tmp_job_pid,              
	       qconf->s_cpu, qconf->h_cpu, qconf->qname);   
      system (str);                                              
      
      tmp_input = (char *) NULL;                                 
    }
    
    for (;;) {
      
      if ((i = waitpid (-1, &status, WNOHANG)) == -1) {           
	ERROR ((DQS_EVENT, "DQS_ERROR_0212 (process leader) waitpid error (%d %s)",
		errno, STRERROR ));
	err_rpt=(dqs_list_type *)dqs_malloc(sizeof(dqs_list_type));
	bzero((char *)&err_rpt,sizeof(err_rpt));
	err_rpt->job= job;
	err_rpt->str1= dqs_string_insert(NULL,
					 "dqs_execd process  leader waitpid error ");
	dqs_report_problem(err_rpt, FALSE);      
	exit (-1);
      }                                                        
      if (i == tmp_job_pid) {
	
	DPRINTF((DQS_EVENT,"job exiting dead_childern=%d",dead_children));
	break;
	
      }                              
      
#ifdef AFSSYS
      if (dqs_reauth(job))
	sleep(MAX_KLOG_TIME); /* UGH!  I know - I know! */
#endif
      
      if(conf.addon_script){
	sprintf (str, "%s -slave loop %s %s %d %d %d %s %s",     
		 conf.addon_script, tmp_slave_local,             
		 job->dqs_job_name, tmp_job_pid,            
		 qconf->s_cpu, qconf->h_cpu,                
		 qconf->qname, tmp_input?tmp_input:"");                  /*Patch applied for linux on above line*/
	
	if (tmp_ifp = popen (str, "r")) {                        
	  fgets (str, 10, tmp_ifp);                              
	  tmp_ilen = atoi (str);                                 
	  tmp_input = (char *) malloc (tmp_ilen + 1);            
	  fgets (tmp_input, tmp_ilen + 1, tmp_ifp);              
	  switch (pclose (tmp_ifp) >> 8) {                       
	  case 2 :                                             
	    
	    ERROR((DQS_EVENT,"DQS_ERROR_0213 ###%s exceeded hard_cpulimit",   
		   job->dqs_job_name));                        
	    ERROR((DQS_EVENT,                                  
		   "###delivering SIGKILL (%d) to %s pid %d",  
		   SIGKILL, job->dqs_job_name, tmp_job_pid));  
	    dqs_kill (-tmp_job_pid, DQS_SIGKILL);              
	    break;                                             
	  case 1 :                                             
	    ERROR((DQS_EVENT,"DQS_ERROR_0214 ###%s exceeded soft_cpulimit",   
		   job->dqs_job_name));                        
	    ERROR((DQS_EVENT,                                  
		   "###delivering SIGUSR2 (%d) to %s pid %d",  
		   SIGUSR2, job->dqs_job_name, tmp_job_pid));  
	    dqs_kill (-tmp_job_pid, DQS_SIGUSR2);              
	    break;                                             
	  }                                                      
	} else {                                                 
	  tmp_input = (char *) NULL;                             
	}                                                        
      }
      DPRINTF((DQS_EVENT,"Slave Sleep"));
      sleep (atoi(SLAVE_LOOP_TIME));                                 
    }  /*  for(;;)    */
    
    
    if(conf.addon_script) {
      sprintf (str, "%s -slave cleanup %s %s %d %d %d %s",       
	       conf.addon_script, tmp_slave_local,               
	       job->dqs_job_name, tmp_job_pid,              
	       qconf->s_cpu, qconf->h_cpu, qconf->qname);   
      system (str);
      
    }
    
    if (unlink (tmp_slave_local) == -1) {                      
      DPRINTF ((DQS_EVENT, "%s cannot unlink data file (%d %s)",
		"(process leader)", errno, STRERROR));
    }                                                          
    if (unlink (tmp_pid_local) == -1) {                        
      DPRINTF((DQS_EVENT, "%s cannot unlink pid data file (%d %s)",
	       "(process leader)", errno, STRERROR ));
    }                                                          
    
    
    if( conf.output_handling != LEAVE_OUTPUT_FILES) {
      if (tmp_pipeok > 0) {
	dqs_set_uid_gid(job);                                    
	sprintf (str, "rm -f %s && cp -fp %s %s && rm %s",       
		 tmp_stdout_path,                                
		 tmp_stdout_local, tmp_stdout_path,              
		 tmp_stdout_local);                              
	system (str);                                            
	if (! job->merge_stderr) {                               
	  sprintf (str, "rm -f %s && cp -fp %s %s && rm %s",     
		   tmp_stderr_path,                              
		   tmp_stderr_local, tmp_stderr_path,            
		   tmp_stderr_local);                            
	  system (str);                                          
	}                                                        
	
	if(conf.addon_info) {
	  strcpy (tmp_info_path, tmp_stdout_path);                 
	  *(strrchr (tmp_info_path, '/')) = '\0';                  
	  sprintf (str, "%s.%s%s.%d", job->job_name,               
		   conf.addon_info, job->dqs_job_name, tmp_job_pid);
	  sprintf (tmp_info_path, "%s/%s", tmp_info_path, str);    
	  sprintf (tmp_info_local, "%s/%s", tmp_localdir, str);    
	  sprintf (str, "cp -fp %s %s && rm %s",
		   tmp_info_local, tmp_info_path,  tmp_info_local);
	  system (str);                                            
	}
      }
    }
    exit (WEXITSTATUS(status));   /*  exiting  job leader   */
  } /*  switch tmp_job_pid */
  
  /*************************************************************************/
  /**********    this is the actual job execution   child process          */
  /************************************************************************/
  DPRINTF((DQS_EVENT,"In Job"));
  dqs_set_uid_gid(job);
  
  /*Change via Doug Gibson*/
  envnew = (char **) malloc(sizeof (char *) * (256));
  envnew[0]=NULL;
  environ = envnew;
  
  if (job->dqs_o_home) 
    dqs_setenv("DQS_O_HOME=",job->dqs_o_home,1);
  if (job->dqs_o_log_name)
    dqs_setenv("DQS_O_LOGNAME=",job->dqs_o_log_name,1);
  if (job->dqs_o_path)
    dqs_setenv("DQS_O_PATH=",job->dqs_o_path,1);
  if (job->dqs_o_mail)
    dqs_setenv("DQS_O_MAIL=",job->dqs_o_mail,1);
  if (job->dqs_o_shell)
    dqs_setenv("DQS_O_SHELL=",job->dqs_o_shell,1);
  if (job->dqs_o_tz)
    dqs_setenv("DQS_O_TZ=",job->dqs_o_tz,1);
  if (job->dqs_o_workdir)
    dqs_setenv("DQS_O_WORKDIR=",job->dqs_o_workdir,1);
  if (job->dqs_o_host)
    dqs_setenv("DQS_O_HOST=",job->dqs_o_host,1);
  
  dqs_setenv("DQS_CELL=", me.default_cell, 1);
  dqs_setenv("HOME=", pw->pw_dir, 1);
  dqs_setenv("SHELL=", pw->pw_shell, 1);
  dqs_setenv("USER=", pw->pw_name, 1);
  dqs_setenv("LOGNAME=", pw->pw_name, 1);
  dqs_setenv("JOB_NAME=",job->job_name,1); 
  dqs_setenv("HOSTNAME=",qconf->qhostname,1);
  dqs_setenv("PATH=", "/usr/local/bin:/usr/ucb:/bin:/usr/bin:", 1);
  dqs_setenv("QUEUE=",qconf->qname,1);
  dqs_setenv("JOB_ID=",job->dqs_job_name,1);
  dqs_setenv("JOB_NAME=",job->job_name,1);
  dqs_setenv("ENVIRONMENT=","BATCH",1);
  
  dqs_setenv("DQS_SDATA=",tmp_slave_local,1);
  
  if (qconf->tmpdir)
    {
      dqs_setenv("TMPDIR=",qconf->tmpdir,1);
      dqs_setenv("TMP=",qconf->tmpdir,1);
    }
  
  if (job->variable_list)
    {
      lp=job->variable_list;
      while (lp)
	{
	  dqs_setenv(lp->str0,lp->str1,1);
	  lp=lp->next;
	}
    }
  
  if (job->env_list)
    {
      lp=job->env_list;
      while (lp)
	{
	  dqs_setenv(lp->str0,lp->str1,1);
	  lp=lp->next;
	}
    }
  
  if(conf.output_handling == LEAVE_OUTPUT_FILES){
    for (i=0; i<_NFILE-1; i++)  close(i);
  }
  
  sprintf (tmp_exec_file, "%s/dqs_execd/%s/%s",                  
	   conf.execd_spool_dir, me.unqualified_hostname, job->exec_file);
  
  in=open(tmp_exec_file,O_RDONLY);
  
  if(in<0 ){
    
    
    sprintf (str, "error: cannot open script file \"%s\" - DQS job \"%s\" aborting",
	     tmp_exec_file,job->dqs_job_name);
    ERROR((DQS_EVENT,str));                    
    dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
    DEXITE;
    exit(DQS_ENOENT);
  }
  
  
  /* if they set "-cwd" lets get there */     
  if (job->cwd) 
    {
      
      /*Change via DG to help manage NFS*/       
      if (dqs_am_chdir(job->cwd,job->dqs_o_host,dir_str))
	{
	  sprintf(str,"error: cannot chdir(%s) - DQS job \"%s\" aborting",
		  job->cwd,job->dqs_job_name);
	  ERROR((DQS_EVENT,str));             
	  dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	  DEXITE;
	  exit(DQS_ENOENT);
	}
      
      dqs_setenv("PWD=",dir_str,1);
      cwd=dir_str;
    }
  else
    {
      
      if (dqs_am_chdir(pw->pw_dir,job->dqs_o_host,dir_str))
	{
	  sprintf(str,"error: cannot chdir(%s) - DQS job \"%s\" aborting",
		  pw->pw_dir,job->job_name);
	  ERROR((DQS_EVENT,str));
	  dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	  DEXITE;
	  exit(DQS_ENOENT);
	}
      
      dqs_setenv("PWD=",dir_str,1);
      cwd=dir_str;
    }
  
  
  account_str=dqs_get_path(job->account_list,job->job_name,
			   job->job_number,DQS_ACCOUNT);
  
  dqs_setenv("DQS_ACCOUNT=",account_str,1);
  
  DTRACE;
  /* write out granted resources - use */
  
  if (dqs_length_of_list(job->granted_destin_identifier_list)>1) { 
    hosts_path=dqs_get_path(job->stdout_path_list,job->job_name,
			    job->job_number,DQS_HOSTS);
    if (hosts_path[0]!='/')
      {
	sprintf(str,"%s/%s",cwd,hosts_path);
	hosts_path=dqs_string_insert(hosts_path,str);
      }
    f=fopen(hosts_path,"w");
    if (!f)
      {
	sprintf(str,"error: cannot open output file \"%s\"",hosts_path);
	ERROR((DQS_EVENT,str));
	dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	DEXITE;
	exit(DQS_ENOENT);
      }
    dqs_setenv("HOSTS_FILE=",hosts_path,1);
    sprintf(num_hosts,"%d",dqs_length_of_list(job->granted_destin_identifier_list));
    dqs_setenv("NUM_HOSTS=",num_hosts,1);
    lp=job->granted_destin_identifier_list;
    while (lp)
      {
	if (lp->str0)
	  fprintf(f,"%s\n",lp->str1);
	lp=lp->next;
      }
    fclose(f);
  }
  if (job->merge_stderr)
    {
      stdout_path=dqs_get_path(job->stdout_path_list,job->job_name,
			       job->job_number,DQS_STDOUT);
      if (stdout_path[0]!='/')
	{
	  sprintf(str,"%s/%s",cwd,stdout_path);
	  stdout_path=dqs_string_insert(stdout_path,str);
	}
      
      if (conf.output_handling!= LEAVE_OUTPUT_FILES) {
	strcpy  (tmp_stdout_path, stdout_path);                   
	sprintf (stdout_path, "%s/%s.o%s.%d",                     
		 tmp_localdir, job->job_name, job->dqs_job_name, me.pid);
      }
      
      dqs_setenv("DQS_STDOUT=",stdout_path,1);
      dqs_setenv("DQS_STDERR=",stdout_path,1);
      dqs_setenv("DQS_MERGED_STDERR=","TRUE",1);
      out =open(stdout_path,O_WRONLY|O_CREAT|O_TRUNC,0744);
      if (out<0) 
	{
	  sprintf(str,"error: cannot open output file \"%s\"",stdout_path);
	  ERROR((DQS_EVENT,str));
	  dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	  DEXITE;
	  exit(DQS_ENOENT);
	}
      dup2(1,2);
      
    }
  else     /*  job->cwd       */
    {
      stdout_path=dqs_get_path(job->stdout_path_list,job->job_name,
			       job->job_number,DQS_STDOUT);
      stderr_path=dqs_get_path(job->stderr_path_list,job->job_name,
			       job->job_number,DQS_STDERR);
      
      
      if (stdout_path[0]!='/')
	{
	  sprintf(str,"%s/%s",cwd,stdout_path);
	  stdout_path=dqs_string_insert(stdout_path,str);
	}
      if (stderr_path[0]!='/')
	{
	  sprintf(str,"%s/%s",cwd,stderr_path);
	  stderr_path=dqs_string_insert(stderr_path,str);
	}
      
      if( conf.output_handling!=LEAVE_OUTPUT_FILES){
	strcpy  (tmp_stdout_path, stdout_path);                   
	sprintf (stdout_path, "%s/%s.o%s.%d",                     
		 tmp_localdir, job->job_name, job->dqs_job_name, me.pid);
	strcpy  (tmp_stderr_path, stderr_path);                   
	sprintf (stderr_path, "%s/%s.e%s.%d",                     
		 tmp_localdir, job->job_name, job->dqs_job_name, me.pid);
      }
      dqs_setenv("DQS_STDOUT=",stdout_path,1);
      dqs_setenv("DQS_STDERR=",stderr_path,1);
      
      out=open(stdout_path,O_WRONLY|O_CREAT|O_TRUNC,0744);
      if (out<0)
	{
	  sprintf(str,"error: cannot open output file \"%s\"",stdout_path);
	  ERROR((DQS_EVENT,str));
	  dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	  DEXITE;
	  exit(DQS_ENOENT);
	}
      
      DTRACE;
      err=open(stderr_path,O_WRONLY|O_CREAT|O_TRUNC,0744);
      if (err<0)
	{
	  sprintf(str,"error: cannot open output file \"%s\"",stderr_path);
	  ERROR((DQS_EVENT,str));
	  dqs_exec_mail(qconf,job,MAIL_AT_ABORT,str);
	  DEXITE;
	  exit(DQS_ENOENT);
	}
    }  /*  else  job->cwd   */
  
  if(conf.output_handling!= LEAVE_OUTPUT_FILES) {
    strcpy (tmp_stdout_local, stdout_path);
    strcpy (tmp_stderr_local, stderr_path);
    if (tmp_pipeok == 0 &&
	((tmp_pipeok = write (tmp_pfd[1], tmp_stdout_path,
			      sizeof (tmp_stdout_path))) != sizeof (tmp_stdout_path) ||
	 (tmp_pipeok = write (tmp_pfd[1], tmp_stdout_local,
			      sizeof (tmp_stdout_local))) != sizeof (tmp_stdout_local) ||
	 (tmp_pipeok = write (tmp_pfd[1], tmp_stderr_path,
			      sizeof (tmp_stderr_path))) != sizeof (tmp_stderr_path) ||
	 (tmp_pipeok = write (tmp_pfd[1], tmp_stderr_local,
			      sizeof (tmp_stderr_local))) != sizeof (tmp_stderr_local))) {
      ERROR ((DQS_EVENT,
	      "(process leader) cannot write pipe (%d %s)",
	      errno, STRERROR));
    }
    
    unlink (stdout_path);
    if (! job->merge_stderr) unlink (stderr_path);
    for (i=0; i<_NFILE-1; i++) close(i);
    in  = open ("/dev/null", O_RDONLY);
    out = open (stdout_path, O_WRONLY|O_CREAT|O_TRUNC, 0744);      
    
    if (! job->merge_stderr) {                                     
      err = open (stderr_path, O_WRONLY|O_CREAT|O_TRUNC, 0744);    
    } else {                                                       
      dup2 (1, 2);                                                 
    }                                                              
  }
  
  
  if(conf.output_handling== LINK_OUTPUT_FILES) {
    /***---------------------------------------------------------------*/
    /*** create symbolic links to local output files                   */
    /***---------------------------------------------------------------*/
    sprintf (str, "%s/%s",conf.execd_spool_dir, LINK_FILE_PATH);
    f = fopen (str, "r");                                          
    bzero((char *)str, sizeof (str));                                     
    fgets (str, sizeof (str), f);                                  
    fclose (f);                                                    
    sprintf (tmp_stdout_net, "%s/local/%s.o%s.%d",                 
	     str, job->job_name, job->dqs_job_name, me.pid);       
    symlink (tmp_stdout_net, tmp_stdout_path);                     
    if (! job->merge_stderr) {                                     
      sprintf (tmp_stderr_net, "%s/local/%s.e%s.%d",               
	       str, job->job_name, job->dqs_job_name, me.pid);     
      symlink (tmp_stderr_net, tmp_stderr_path);                   
    }                                                              
  }
  
  if(conf.output_handling== COPY_OUTPUT_FILES) {
    /***---------------------------------------------------------------*/
    /*** fork process to copy local output files every                 */
    /*** TMP_COPY_OUTPUT_FILES seconds                                 */
    /***---------------------------------------------------------------*/
    switch (fork ()) {                                             
    case -1:                                                     
      CRITICAL ((DQS_EVENT,                                      
		 "%s cannot fork: COPY_OUTPUT_FILES (%d %s)",
		 "(dqs_execd job)", errno, STRERROR ));   
      abort ();                                                  
      break;                                                     
      
    case 0 :                                                     
      sprintf (str, "cp -fp %s %s", stdout_path, tmp_stdout_path);
      if (! job->merge_stderr)                                   
	sprintf (str, "%s; cp -fp %s %s",                        
		 str, stderr_path, tmp_stderr_path);        
      for (;;) {                                                 
	ERROR((DQS_EVENT, "DQS_ERROR_0215 %s", str));                  
	sleep (atoi(COPY_FILE_DELAY));
      }                                                          
      break;                                                     
      
    default:                                                     
      break;                                                     
    }  /*          switch (fork ())             */
  }
  
  shell_path=dqs_get_path(job->shell_list,job->job_name,
			  job->job_number,DQS_SHELL);
  if (!shell_path)
    shell_path=qconf->shell;
  dqs_setenv("SHELL=",shell_path,1);
  
  sigprocmask(SIG_SETMASK,&exec_mask,&o_mask);
  
  if ((job->parallel_package==P4)||(job->parallel_package==MPI))
    {
      dqs_start_p4(shell_path,job,qconf);
      if (job->granted_destin_identifier_list->str3)
	dqs_setenv("REMOTE_INFO=",job->granted_destin_identifier_list->str3,1);
      else
	dqs_setenv("REMOTE_INFO=","NULL",1);
      lp=job->granted_destin_identifier_list;
      i=0;
      
      while (lp)
	{
	  bzero((char *)str,sizeof(str));
	  sprintf(str,"P4_NODE%d=",i);
	  if (lp->str3)
	    dqs_setenv(str,lp->str3,1);
	  else
	    dqs_setenv(str,"NULL_EXEC_STR",1);
	  i++;
	  lp=lp->next;
	}
      
    }
  if (job->parallel_package==PVM)
    {
      dqs_start_PVM(shell_path,job,qconf);
      if (job->granted_destin_identifier_list->str3)
	dqs_setenv("PVM_HOSTS=",job->granted_destin_identifier_list->str3,1);
      else
	dqs_setenv("PVM_HOSTS=","NULL",1);
      
    }
  if (job->parallel_package==TCGMSG)
    {
      dqs_start_TCGMSG(shell_path,job,qconf);
      if (job->granted_destin_identifier_list->str3)
	dqs_setenv("TCGMSG_HOSTS=",job->granted_destin_identifier_list->str3,1);
      else
	dqs_setenv("TCGMSG_HOSTS=","NULL",1);
      
    }
  if ((job->parallel_package==GENERIC_ALL)||(job->parallel_package==GENERIC_SLA))
    {
      dqs_start_generic(shell_path,job,qconf);
      if (job->granted_destin_identifier_list->str3)
	dqs_setenv("REMOTE_INFO=",job->granted_destin_identifier_list->str3,1);
      else
	dqs_setenv("REMOTE_INFO=","NULL",1);
      
      lp=job->granted_destin_identifier_list;
      i=0;
      while (lp)
	{
	  bzero((char *)str,sizeof(str));
	  sprintf(str,"GENERIC_NODE%d=",i);
	  if (lp->str3)
	    dqs_setenv(str,lp->str3,1);
	  else
	    dqs_setenv(str,"NULL_EXEC_STR",1);
	  i++;
	  lp=lp->next;
	}
    }
  /*
    if (tmp_cp = strrchr (shell_path, '/')) {                      
    sprintf (str, "-[DQS]%s", tmp_cp + 1);                       
    } else {                                                       
    sprintf (str, "-[DQS]%s", shell_path);                       
    }
  */
  sprintf (str,"%s", shell_path);     
  sprintf (tmp_exec_file, "%s/dqs_execd/%s/%s",                  
	   conf.execd_spool_dir, me.unqualified_hostname, job->exec_file);
  
  if (job->script_argv_list) {                                   
    tmp_argc = job->script_argv_list->int0;                      
  } else {                                                       
    tmp_argc = 0;                                                
  }                                                              
  
  tmp_argv = (char **) malloc (sizeof (char *) * (tmp_argc + 3));
  tmp_argv[0] = (char *) malloc (strlen (str) + 1);              
  strcpy (tmp_argv[0], str);                                     
  tmp_argv[1] = (char *) malloc (strlen (tmp_exec_file) + 1);    
  strcpy (tmp_argv[1], tmp_exec_file);                           
  tmp_argv[tmp_argc+2] = (char *) NULL;                          
  
  tmp_sp = job->script_argv_list;                                
  while (tmp_sp) {                                               
    tmp_argv[tmp_argc+1] = (char *) malloc (strlen (tmp_sp->str0) + 1);
    strcpy (tmp_argv[tmp_argc+1], tmp_sp->str0);                 
    tmp_argc--;                                                  
    tmp_sp = tmp_sp->next;                                       
  }                                                              
  
  sprintf (str," \n");
  
  test_i=0;
  DPRINTF((DQS_EVENT, "TEST: filehandle %d --> %d", test_i, fstat (test_i,&test_buf)));
  
  /*  Begin Doug Gibson change  */
  /*  Added this call to addon script.  Job gets nuked if the script */
  /*  returns a non-zero retval.  I use this to check whether jobs   */
  /*  should be run on a job-by-job, user-by-user basis.             */
  if(conf.addon_script) {
    sprintf (str, "%s -slave security %s %s %d",       
	     conf.addon_script, tmp_slave_local,               
	     job->dqs_job_name, me.pid
	     );   
    retval=system (str);
    if (WEXITSTATUS(retval)!=0) {
      DEXITE;
      exit(0);
    }
  }
  /*  End Doug Gibson change  */
  
  
  dqs_exec_mail (qconf, job, MAIL_AT_BEGINNING, str);            
  DPRINTF((DQS_EVENT,"just before execve"));
  execve (shell_path, tmp_argv, environ);                        
  
  sprintf (str,                                                  
	   "JID %d execve (%s,[%s,%s,...,0],%s) - failed\n\n%d %d <%s>\n",
	   job->job_number, shell_path,                          
	   tmp_argv[0], tmp_argv[1], "environ",                  
	   errno, sys_nerr, sys_errlist[errno]);                 
  ERROR((DQS_EVENT, "DQS_ERROR_0216 %s", str));                                 
  dqs_exec_mail (qconf, job, MAIL_AT_ABORT, str);                
  
  DEXITE;
  exit(-99);
}


/**************************************************************************/
void dqs_setrlimits(qconf)
     dqs_queue_type *qconf;
     
     /*
       dqs_setrlimits - set process environment based on a queue configuration.
     */
     
{
  
#if _UNICOS
  long clock_tick;
  
  DENTER((DQS_EVENT,"dqs_setrlimits"));
  
  SETPRIORITY((int)qconf->priority);
  
  /* Let's play a game: UNICOS doesn't support hard and soft limits */
  /* but it has job and process limits. Let the soft limit setting  */
  /* in the queue configuration represent the per-process limit     */
  /* while the hard limit is the per-job limit. OK, so it's crude.  */
  
  /* Per-process limits */
  DTRACE;
  clock_tick = sysconf (_SC_CLK_TCK);
  limit (C_PROC, 0, L_CPU,  qconf->s_cpu*clock_tick);
  limit (C_PROC, 0, L_CORE, qconf->s_core/NBPC);
  limit (C_PROC, 0, L_MEM,  qconf->s_data/NBPC);
  
  /* Per-job limits */
  DTRACE;
  limit (C_JOB, 0, L_CPU,   qconf->h_cpu*clock_tick);
  limit (C_JOB, 0, L_MEM,   qconf->h_data/NBPC);
  limit (C_JOB, 0, L_FSBLK, qconf->h_fsize/(NBPC*NCPD));
  
  /* Too bad they didn't have a sysconf call to get bytes/click */
  /* and clicks/disk block. */
  DTRACE;
  
#else
#if __hpux                                                     
  
  struct rlimit rlp;
  
  DENTER((DQS_EVENT,"dqs_setrlimits"));
  
  SETPRIORITY((int)qconf->priority - SETPRIORITY(0));       
  
#if __hppa                                                     
  
  if (qconf->s_cpu != 0x4321 || qconf->h_cpu != 0x1234) {   
    
    if (qconf->s_cpu != RLIM_INFINITY ||                    
	qconf->h_cpu != RLIM_INFINITY) {                    
      rlp.rlim_cur = qconf->s_cpu;                          
      rlp.rlim_max = qconf->h_cpu;                          
      setrlimit(RLIMIT_CPU,&rlp);                           
    }                                                       
    
    if (qconf->s_fsize != RLIM_INFINITY ||                  
	qconf->h_fsize != RLIM_INFINITY) {                  
      rlp.rlim_cur = qconf->s_fsize;                        
      rlp.rlim_max = qconf->h_fsize;                        
      setrlimit(RLIMIT_FSIZE,&rlp);                         
    }                                                       
    
    if (qconf->s_data != RLIM_INFINITY ||                   
	qconf->h_data != RLIM_INFINITY) {                   
      rlp.rlim_cur = qconf->s_data;                         
      rlp.rlim_max = qconf->h_data;                         
      setrlimit(RLIMIT_DATA,&rlp);                          
    }                                                       
    
    if (qconf->s_stack != RLIM_INFINITY ||                  
	qconf->h_stack != RLIM_INFINITY) {                  
      rlp.rlim_cur = qconf->s_stack;                        
      rlp.rlim_max = qconf->h_stack;                        
      setrlimit(RLIMIT_STACK,&rlp);                         
    }                                                       
    
    if (qconf->s_core != RLIM_INFINITY ||                   
	qconf->h_core != RLIM_INFINITY) {                   
      rlp.rlim_cur = qconf->s_core;                         
      rlp.rlim_max = qconf->h_core;                         
      setrlimit(RLIMIT_CORE,&rlp);                          
    }                                                       
    
    if (qconf->s_rss != RLIM_INFINITY ||                    
	qconf->h_rss != RLIM_INFINITY) {                    
      rlp.rlim_cur = qconf->s_rss;                          
      rlp.rlim_max = qconf->h_rss;                          
      setrlimit(RLIMIT_RSS,&rlp);                           
    }                                                       
    
  }                                                         
  
#endif                                                         
  
#else
  
  struct rlimit rlp;
  
  DENTER((DQS_EVENT,"dqs_setrlimits"));
  
  SETPRIORITY((int)qconf->priority);
  
  rlp.rlim_cur = qconf->s_cpu;
  rlp.rlim_max = qconf->h_cpu;
  setrlimit(RLIMIT_CPU,&rlp);
  
  rlp.rlim_cur = qconf->s_fsize;
  rlp.rlim_max = qconf->h_fsize;
  setrlimit(RLIMIT_FSIZE,&rlp);
  
  rlp.rlim_cur = qconf->s_data;
  rlp.rlim_max = qconf->h_data;
  setrlimit(RLIMIT_DATA,&rlp);
  
  rlp.rlim_cur = qconf->s_stack;
  rlp.rlim_max = qconf->h_stack;
  setrlimit(RLIMIT_STACK,&rlp);
  
  rlp.rlim_cur = qconf->s_core;
  rlp.rlim_max = qconf->h_core;
  setrlimit(RLIMIT_CORE,&rlp);
  
  
#if (defined(sun) || defined(solaris)|| defined(SOLARIS23_UP) ) && !defined(RLIMIT_RSS)
#define RLIMIT_RSS 6
#endif
  rlp.rlim_cur = qconf->s_rss;
  rlp.rlim_max = qconf->h_rss;
  setrlimit(RLIMIT_RSS,&rlp);
#endif
#endif
  
  DEXIT;
  return;
  
}

/************************************************************************/
/*Several changes suggested by Doug Gibson*/
int dqs_am_chdir(cp,host,cp_out)
     char *cp;
     char *host;
     char *cp_out;
     
{
  
  string dir_str;
  struct stat statbuf;
  
  DENTER_EXT((DQS_EVENT,"dqs_am_chdir"));
  
  if (!cp)
    {
      ERROR((DQS_EVENT,"DQS_ERROR_0217 NULL path passwd to dqs_am_chdir()"));
      DEXITE;
      return -1;
    }
  
  stat(cp,&statbuf); /* force automount - rdl */
  
  if (!chdir(cp))
    {
      sprintf(cp_out,"%s",cp);
      DEXIT;
      return(0);
    }
  
#ifdef AUTO_MOUNT
  
  bzero((char *)dir_str,sizeof(dir_str));
  /*  Begin Doug Gibson change  */
  /*  This segment detects that the target directory was local to the */
  /*  submitting system and is nfs mounted to this system.            */
  sprintf(dir_str,"/net/%s%s",host,cp);
  stat(dir_str,&statbuf);
  if (!chdir(dir_str))
    {
      sprintf(cp_out,"%s",dir_str);
      DEXIT;
      return(0);
    }
  /*  End Doug Gibson change  */
  
  if (!strncmp(cp,AUTO_MOUNT,strlen(AUTO_MOUNT)))
    { /* might be local - strip AUTO_MOUNT */
      sprintf(dir_str,"%s",(char *) cp + strlen(AUTO_MOUNT));
    }
  else
    { /* prepend AUTO_MOUNT */
      sprintf(dir_str,"%s%s",AUTO_MOUNT,cp);
    }
  
  stat(dir_str,&statbuf);
  
  if (!chdir(dir_str))
    {
      sprintf(cp_out,"%s",dir_str);
      DEXIT;
      return(0);
    }
  
#endif
  
  sprintf(cp_out,"%s",cp);
  
  DEXITE;
  return(-1);
  
}

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

int dqs_exec_mail(qconf,job,tag,str)
     dqs_queue_type *qconf;
     dqs_job_type   *job;
     int            tag;
     string         str;
     
     
{
  string subject, body;
  string         dqs_mail_start;
  if ((job->mail_options & tag) == tag) {
    switch(tag)
      {
      case MAIL_AT_BEGINNING:
        strcpy(dqs_mail_start,dqs_ctime(job->start_time) );
        sprintf(subject,"Start of job %d",job->job_number);
        sprintf(body, "Your job %d %s\n\nHost:  %s\nQueue: %s at %s\n%s\n",
		job->job_number, "has been started:",
		qconf->qhostname, qconf->qname, dqs_mail_start, str);
        break;
      case MAIL_AT_ABORT:
        sprintf(subject,"Abort of job %d",job->job_number);
        strcpy(body,str);
        break;
      }
    dqs_mail (job->mail_list,subject,body);
  }
}
