/*===========================================================================
  Copyright (C) 1995-2009 European Southern Observatory (ESO)
 
  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 2 of 
  the License, 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 Massachusetts Ave, Cambridge, 
  MA 02139, USA.
 
  Correspondence concerning ESO-MIDAS should be addressed as follows:
	Internet e-mail: midas@eso.org
	Postal address: European Southern Observatory
			Data Management Division 
			Karl-Schwarzschild-Strasse 2
			D 85748 Garching bei Muenchen 
			GERMANY
===========================================================================*/

/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.TYPE        Module
.NAME        osp
.LANGUAGE    C
.AUTHOR      IPG-ESO Garching
.CATEGORY    Host operating system interfaces, Process control.
.COMMENTS    These routines give the basic capabilitites for controlling
             processes.
             Processes are addressed by an integer number called process 
             identifier. The process activation function defines the file 
             with the executable code (possibly in a remote node) and the 
             process name, the routine returns the process identification 
             number to be used in further references. Processes can be 
             activated or aborted, also the process logical name associated 
             to it can be changed.
             At process creation, one can redirect the standard input and/or 
             output of the newly created process.
             Each process has some status information associated to it.
             The status information is defined as a data structure (tag 
             prstatus) consisting of a process id (pid field), 
             background/foreground flag (bflg field) and name (procname field) 
             for checking the status of the child process.
             Functions in this module allow to define and retrieve this 
             status information.
             The routines return always
                   a non-negative integer number on successful return.
                   Otherwise, a value of -1 is set to indicate an error
                   condition and the variable ``oserror'' contains the 
                   symbolic error code.

.VERSION     [0.0] 25-Aug-1986   Definition     B. Pirenne  
.VERSION     [1.0] 10-Nov-1986   Programmation  BP
.VERSION     [1.5] 26-Jun-1987   Add child process termination control BP
.VERSION     [1.6] 07-Jul-1987   Allow redefin. of stdin and/or stdout of child
.VERSION     [1.7] 04-Nov-1987   fix construction of process name 
			         in ospcreate   K. Banse
.VERSION     [1.8] 01-Dec-1987   Add  child status in proc.
                                 struct in private memory  I.Suisalu.
.VERSION     [1.9] 25-May-1988   Cosmetic changes C. Guirao
.VERSION     [1.10]14-OCT-1988   The child signal SIGCLD get SIG_DFL C.G.
.VERSION     [1.11]19-Oct-1989   Conceal interal subroutines 
.VERSION     [1.12]25-Apr-1991   Added signal.h include + VOID
.REMARK      =================== System V version ================

.REMARK      On the management and information retrieval of the processes:
             In order to be able to retrieve information on the processes 
             and in order to give a name to it, a special structure was 
             built(see "osparms.h"). Array of this structures are hold in 
             private memory of the calling program.

.VERSION     [1.13]20-Jan-1992   CG. SHELL variable is used to spawn processes 
.VERSION     [1.14]14-Apr-1992   CG. Change 1.14 is removed. The Bourne-Shell
				 is the only one that do change the environment
.VERSION     [2.0]13-Jul-1992    CG. chcatch is VOID. CG
.VERSION     [2.1]14-Jul-1992    VOID is now called VOIDSIG. CG.

 090324		last modif
-------------------------------------------------------------------------*/
/*
 * Define _POSIX_SOURCE to indicate
 * that this is a POSIX program
 */
#define _POSIX_SOURCE 1

#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <proto_os.h>		/* ANSI-C prototyping */


#ifdef OSERROR_D                        /* oserror already defined (Silicon G)*/
#define oserror midaserror
#endif

static int pid;
extern char *oserrmsg;
extern int oserror;

#ifdef __STDC__
static void alarm_handler(int sig)
#else
static void alarm_handler(sig)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE dummy routine to catch SIGALRM avoiding killing the process
-----------------------------------------------------------*/
int sig;
#endif
{
  kill (pid, SIGKILL);
}

int ospcreate(phname, procname, backgrd, stin, stout, time_out)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Activates a specified process whose name is phname and logical process
	 name is procname. (this last name can be modify using osprename)
	 Enviroment pointer also passed to the process. One argument (backgrd)
	 defines if the process will run in background/foreground.
         Allows redirection of the process' standard input and/or output to
	 specified files if the corresponding file descriptors returned from a
	 previous open (osd-, osa-, osx-, ...) statement are given as last 
	 parameters to this call.
.RETURNS Upon successful completion the 
.RETURNS process identification is returned, otherwise -1.
.REMARKS System dependencies:
 -- UNIX: fork(2), execl(2),open(2), close(2),
	  getuid(2), close(2), dup(2),
------------------------------------------------------------*/

char *phname;		/* IN : physical filename with executable code */
char *procname;		/* IN : process logical name . OBSOLETE*/
int backgrd;		/* Put backgound or not	*/
int  stin;		/* IN : file descr. of the new child's std. input */
int  stout;		/* IN : file descr. of the new child's std. output */
unsigned int time_out;	/* IN : amount of seconds for timeout */
{
  int chstat;
  int dupfd;
  struct sigaction ignore, saveintr, savequit;
  char *ptr_bgr;

  ignore.sa_handler = SIG_IGN;
  sigemptyset(&ignore.sa_mask);
  ignore.sa_flags = 0;
  if (sigaction(SIGINT,&ignore,&saveintr) != 0)  { oserror=errno; return(-1); };
  if (sigaction(SIGQUIT,&ignore,&savequit) != 0) { oserror=errno; return(-1); };

  /* 
   * In the parent process, fork returns the
   * process number of the child. In the
   * child process, 0 is returned.       
   */
  switch (pid = fork()) { 
  case -1:
    oserror =errno;
    return(-1);
  case 0: 		/* In child process */
    /* Restore signals for child process */
    (void) sigaction(SIGINT, &saveintr, (struct sigaction *)NULL);
    (void) sigaction(SIGQUIT, &savequit, (struct sigaction *)NULL);
    if (stin != 0) { 		/* redirected input of child process */
      (void) close (0);		/* close old standard input */
      dupfd = dup(stin);	/* duplicate file descriptor */
      (void) close(stin);	/* close unnecessary old file descr. */
      }

    if (stout != 1) {		/* redirected output of child process */
      (void) close (1);		/* close old standard output */
      dupfd = dup(stout);	/* duplicate file descriptor */
      (void) close(stout);	/* close unnecessary old file descr. */
      }

    if (*phname == '$') { /* system command */
      phname ++;
      /* if background, ignore SIGINT and SIGQUIT */
      if ( (ptr_bgr = strrchr(phname,'&')) != NULL) {
 	ptr_bgr++;
	for ( ; *ptr_bgr && *ptr_bgr == ' '; ptr_bgr++) ;
	if (!*ptr_bgr) {
          sigaction(SIGINT,&ignore,&saveintr);
          sigaction(SIGQUIT,&ignore,&savequit);
          }
        }
      (void) execl("/bin/sh","sh","-c",phname,(char *)0);
      }
    else {	/* Execute the process */
      if (strchr(phname,' ') == (char *)0) 
        (void)execl(phname,phname,(char *)0);
      else { 	/* the phname contains arguments */
	char *arg[9];
	char *log_name, *blank_ptr;
  	int i;
        log_name = malloc ((size_t)strlen(phname)+1);
        strcpy(log_name,phname);
	arg[0]=log_name;
        for (i=0; i < 8 ; i++) {
	  if ( !(blank_ptr = strchr(arg[i],' '))) {
	    arg[i+1] = (char *)0;
	    break;
            }
	  while ( *blank_ptr == ' ' ) *blank_ptr++ = '\0';
	  arg[i+1] = blank_ptr;
	  }
        (void)execl(arg[0],arg[0],arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],arg[7],arg[8],(char *)0);
	}
      }
	 
    /* exit with the errno code + eight bit set, to be recognized by chstat */
    /* Do not print the error. The application could then try in midwork */
    /* printf("ERROR IN CHILD, errno = %d\n",errno); */
    exit(0x80 | errno);  	/* suppress the fork	*/;
	
  default: 	/* In father process */
    if(!backgrd) { 
      struct sigaction act, oalrm;

      if (time_out != 0) {

        /* Catch the signal ALRM to avoid proccess to terminate */
        act.sa_handler = alarm_handler;
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        sigaction(SIGALRM,&act,&oalrm);

	/* Set alarm to time_out */
        (void) alarm(time_out);
	}

      oserror = 0;
      while (waitpid(pid, &chstat, 0) != pid) {
        if (errno != EINTR) {
          oserror= errno;
          break;
	  }
	}

      if (time_out != 0) {
	(void) alarm(0);
	sigaction(SIGALRM,&oalrm,&act);
	}

      /* If the execl failed */
      if ( (chstat & 0x8000) == 0x8000 ) oserror = ( (chstat >> 8 ) & 0x7f );

      if (!oserror) {
        if ( WIFEXITED(chstat) ) oserror = WEXITSTATUS(chstat);
        if ( WIFSIGNALED(chstat) ) {
          int int_signal;
          char errmsg[80]; 
          int_signal = WTERMSIG(chstat);
          sprintf(errmsg,"Child killed by signal %d",int_signal);
   	  oserrmsg = errmsg;
	  oserror = -1;
          }
	}
      }
    /* restore previous signal actions */
    sigaction(SIGINT,&saveintr,(struct sigaction *)NULL);
    sigaction(SIGQUIT,&savequit,(struct sigaction *)NULL);
    if (oserror) pid = -1;
  }
  return(pid);
}

void ospexit(stat)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Normal termination of a process.
.RETURNS Nothing
.REMARKS System dependencies:
 -- UNIX: exit(2)
------------------------------------------------------------*/
int stat;
{
  exit(stat);                    /* Terminates the process.	*/
}

int ospwait(time)
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Delays the execution of a process for a time interval.
         Resolution of time interval is in seconds.
.RETURNS Always 0
.REMARKS System dependencies:
 -- UNIX: sleep(2)
------------------------------------------------------------*/
unsigned int time;			/* IN : time delay in seconds */
{
  sleep(time);
  return(0);
}

/* CG. I favor to removed this, and use getpid directly
 * Currently used by gui applications: XSpectra XFilter XEchelle & stella 
 */
int osppid()
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
.PURPOSE Obtains current process identification.
.RETURNS The process identification as a integer.
-------------------------------------------------------------*/
{
        return(getpid());
}
