DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T s

⟦70f471dda⟧ TextFile

    Length: 17199 (0x432f)
    Types: TextFile
    Names: »shell.c«

Derivation

└─⟦2d1937cfd⟧ Bits:30007241 EUUGD22: P.P 5.0
    └─⟦dc59850a2⟧ »EurOpenD22/pp5.0/pp-5.tar.Z« 
        └─⟦e5a54fb17⟧ 
            └─⟦this⟧ »pp-5.0/Chans/shell/shell.c« 

TextFile

/* shell.c: shell or program channel */

# ifndef lint
static char Rcsid[] = "@(#)$Header: /cs/research/pp/hubris/pp-beta/Chans/shell/RCS/shell.c,v 5.0 90/09/20 15:53:38 pp Exp Locker: pp $";
# endif

/*
 * $Header: /cs/research/pp/hubris/pp-beta/Chans/shell/RCS/shell.c,v 5.0 90/09/20 15:53:38 pp Exp Locker: pp $
 *
 * $Log:	shell.c,v $
 * Revision 5.0  90/09/20  15:53:38  pp
 * rcsforce : 5.0 public release
 * 
 */



#include "util.h"
#include "expand.h"
#include "head.h"
#include "qmgr.h"
#include "q.h"
#include "dr.h"
#include "prm.h"
#include "prog.h"
#include	<sys/stat.h>
#include	<sys/file.h>
#include	<sys/wait.h>
#include	<signal.h>

#ifdef __STDC__
#define VOLATILE volatile
#else
#define VOLATILE
#endif

extern void	sys_init(), err_abrt(), rd_end();
extern struct type_Qmgr_DeliveryStatus	*delivery_resetDRs();
extern char	*mquedir;
extern char 	*quedfldir;
extern char	*chndfldir;
extern char	*logdfldir;
extern char	*ad_getlocal();
extern CHAN 	*ch_nm2struct();
extern char	*expand_dyn ();
CHAN 		*mychan;
char		*this_msg = NULL, *this_chan = NULL;
static int	first_successDR, first_failDR, fatal = FALSE;
static char	buf[BUFSIZ], *fatalbuf = NULLCP;

static struct type_Qmgr_DeliveryStatus *process ();
static int toProcess ();
static int processMsg ();
static int doProcess ();
static int write_to_child ();
static int send_to_child();
static int do_child_logging();
static void set_success();
static int initialise ();
static char *get_adrstr();
static int security_check ();
static void dirinit ();
static ADDR *getnthrecip ();
static int	pipe_signaled;
static jmp_buf	toplevel;

static sig_pipe()
{
	pipe_signaled = TRUE;
	longjmp (toplevel, DONE);
}

/* \f

 */
/* main routine */
 
main (argc, argv)
int 	argc;
char	**argv;
{
	sys_init(argv[0]);
	or_myinit();
	dirinit ();
#ifdef PP_DEBUG
	if (argc>1 && (strcmp(argv[1],"debug") == 0))
		debug_channel_control(argc,argv,initialise,process, NULLIFP);
	else
#endif
		channel_control (argc, argv, initialise, process, NULLIFP);
}

/* \f

 */
/* routine to move to correct place in file system */

static void dirinit ()
{
	if (chdir (quedfldir) < 0)
		err_abrt (RP_LIO, " Unable to change directory to '%s'",
			  quedfldir);
}

/* \f

 */
/* channel initialise routine */

static int initialise (arg)
struct type_Qmgr_Channel *arg;
{
	char *name;
	
	name = qb2str(arg);

	if ((mychan = ch_nm2struct(name)) == NULLCHAN) {
		PP_OPER(NULLCP, ("Chan/shell : Channel '%s' not known",name));
		if (name != NULL) free(name);
		return NOTOK;
	}

	(void) signal (SIGCHLD, SIG_DFL);
	if (name != NULL) free(name);
	return OK;
}

/* \f

 */
/* routine to check if allowed to filter this message through this channel */
static int security_check (msg)
struct type_Qmgr_ProcMsg *msg;
{
 	char 		*msg_file = NULL, *msg_chan = NULL;
	int		result;

	result = TRUE;
	msg_file = qb2str (msg->qid);	
	msg_chan = qb2str (msg->channel);

	if ((mychan == NULLCHAN) || (strcmp(msg_chan,mychan->ch_name) != 0)) {
		PP_LOG(LLOG_EXCEPTIONS,
		       ("Chans/shell channel err: '%s'",msg_chan));
		result = FALSE;
	}

	/* free all storage used */
	if (msg_file != NULL) free(msg_file);
	if (msg_chan != NULL) free(msg_chan);
	return result;
}

/* \f

 */
/* routine called to do shell */

Expand expand_macros[] = {
	"SENDER", NULLCP,	/* 0 */
	"822SENDER", NULLCP,	/* 1 */
	"400SENDER", NULLCP,	/* 2 */
	"QID", NULLCP,		/* 3 */
	"UA-ID", NULLCP,	/* 4 */
	"P1-ID", NULLCP,	/* 5 */
	"RECIP", NULLCP,	/* 6 */
	"822RECIP", NULLCP,	/* 7 */
	"400RECIP", NULLCP,	/* 8 */
	"RECIPIENT", NULLCP,	/* 9 */
	"USERID", NULLCP,	/* 10 */
	"GROUPID", NULLCP,	/* 11 */
	"SIZE",	NULLCP,		/* 12 */
	"CHANNELNAME", NULLCP,	/* 13 */
	NULLCP, NULLCP};

static struct type_Qmgr_DeliveryStatus *process (arg)
struct type_Qmgr_ProcMsg *arg;
{
	struct prm_vars	prm;
	Q_struct	que;
	char	sizebuf[20];
	char	uidbuf[20], gidbuf[20];
	ADDR		*sender = NULL;
	ADDR		*recips = NULL;
	int 		rcount, retval;
	struct type_Qmgr_UserList 	*ix;
	ADDR				*adr;
	static char		*local = NULLCP;
	char *key;
	ProgInfo	*info = NULLPROG;

	bzero((char *) &que,sizeof(que));
	bzero((char *) &prm,sizeof(prm));

	delivery_init(arg->users);
	delivery_setall(int_Qmgr_status_messageFailure);
	first_successDR = TRUE;
	first_failDR = TRUE;
	if (security_check(arg) != TRUE)
		return deliverystate;

	if (this_msg != NULL) free(this_msg);
	if (this_chan != NULL) free(this_chan);

	this_msg = qb2str(arg->qid);
	this_chan = qb2str(arg->channel);

	PP_LOG(LLOG_NOTICE,
	       ("%s processing msg '%s'",mychan->ch_name,this_msg));

	if (rp_isbad(rd_msg(this_msg,&prm,&que,&sender,&recips,&rcount))) {
		PP_LOG(LLOG_EXCEPTIONS,
		       ("%s rd_msg err: '%s'",mychan->ch_name,this_msg));
		rd_end();
		return delivery_setallstate(int_Qmgr_status_messageFailure,
					    "Can't read message");
	}

	expand_macros[0].expansion = que.Oaddress->ad_value;
	expand_macros[1].expansion = que.Oaddress->ad_r822adr;
	expand_macros[2].expansion = que.Oaddress->ad_r400adr;
	expand_macros[3].expansion = this_msg;
	expand_macros[4].expansion = que.ua_id;
	expand_macros[5].expansion = que.msgid.mpduid_string;
	/* 6-11 filled in below */
	(void) sprintf (sizebuf, "%d", que.msgsize);
	expand_macros[12].expansion = sizebuf;
	expand_macros[13].expansion = mychan -> ch_name;

	if (arg->users == NULL)
		PP_LOG(LLOG_NOTICE,
			("%s : passed a NULL user list for message '%s'",
			mychan->ch_name,
			this_msg));

	/* check each recipient for processing */
	for (ix = arg->users; ix; ix = ix->next) {
		fatal = FALSE;
		if (fatalbuf != NULLCP) {
			free(fatalbuf);
			fatalbuf = NULLCP;
		}
		if ((adr = getnthrecip(&que,ix->RecipientId->parm)) == NULL) {
			PP_LOG(LLOG_EXCEPTIONS,
			       ("%s : failed to find recipient %d of msg '%s'",
				mychan->ch_name,ix->RecipientId->parm,
				this_msg));
			delivery_setstate(ix->RecipientId->parm, 
					  int_Qmgr_status_messageFailure,
					  "Unable to find specified recipient");
			continue;
		}
		buf[0] = '\0';
		key = get_adrstr(adr);
		if (local) {
			free (local);
			local = NULLCP;
		}
		if ((local = ad_getlocal(key, adr -> ad_type)) != NULLCP)
			info = tb_getprog(local, mychan->ch_table);

		if (info == NULLPROG)
			info = tb_getprog (key, mychan -> ch_table);
		
		if (info == NULLPROG) {
			PP_LOG(LLOG_EXCEPTIONS,
			       ("table lookup failed for '%s' in '%s'",
				key, mychan -> ch_table -> tb_name));
			(void) sprintf (buf,
					"Failed to find entry for '%s' in table '%s'",
					key, mychan -> ch_table -> tb_name);
			delivery_setstate(ix -> RecipientId -> parm,
				     int_Qmgr_status_messageFailure,
				     buf);
			continue;
		}

		expand_macros[6].expansion = adr -> ad_value;
		expand_macros[7].expansion = adr -> ad_r822adr;
		expand_macros[8].expansion = adr -> ad_r400adr;
		expand_macros[9].expansion = local;
		if (info -> username)
			expand_macros[10].expansion = info -> username;
		else {
			(void) sprintf (uidbuf, "%d", info -> uid);
			expand_macros[10].expansion = uidbuf;
		}
		(void) sprintf (gidbuf, "%d", info -> gid);
		expand_macros[11].expansion = gidbuf;

		
		switch (lchan_acheck (adr, mychan, 1, NULLVP)) {
		    default:
		    case NOTOK:
			break;
		    case OK:
			if (processMsg(this_msg, adr, info) == NOTOK) {
				if (fatal == TRUE) {
					/* DR it */
					set_1dr (&que, adr->ad_no,
						 DRR_TRANSFER_FAILURE,
						 DRD_UA_UNAVAILABLE,
						 fatalbuf);
					delivery_set (adr->ad_no,
						      (first_failDR == TRUE) ? int_Qmgr_status_negativeDR : int_Qmgr_status_failureSharedDR);
					first_failDR = FALSE;
				} else {
					PP_LOG(LLOG_EXCEPTIONS,
					       ("%s : failed to process msg '%s' for recip '%d' on channel '%s'",
						mychan->ch_name,this_msg, adr->ad_no, this_chan));
					delivery_setstate(adr->ad_no, 
							  int_Qmgr_status_messageFailure,
							  buf);
				}
			} else {
				PP_LOG(LLOG_NOTICE,
				       ("%s : processed '%s' for recipient %d",
					mychan->ch_name,this_msg,adr->ad_no));
				set_success(adr, &que);
			}
		}
		prog_free (info);
	}
	if (fatalbuf != NULLCP) {
		free(fatalbuf);
		fatalbuf = NULLCP;
	}

	if (rp_isbad(retval = wr_q2dr(&que, this_msg))) {
		PP_LOG(LLOG_EXCEPTIONS,
		       ("%s wr_q2dr failure '%d'",mychan->ch_name,retval));
		delivery_resetDRs(int_Qmgr_status_messageFailure);
	}

	rd_end();
	return deliverystate;
}

static void set_success(recip, que)
ADDR		*recip;
Q_struct	*que;
{
	if (recip->ad_usrreq == AD_USR_CONFIRM ||
	    recip->ad_mtarreq == AD_MTA_CONFIRM ||
	    recip->ad_mtarreq == AD_MTA_AUDIT_CONFIRM) {
		set_1dr(que, recip->ad_no,
			DRR_NO_REASON, -1, NULLCP);
		delivery_set(recip->ad_no, 
			     (first_successDR == TRUE) ?
			     int_Qmgr_status_positiveDR :
			     int_Qmgr_status_successSharedDR);
	} else {
		(void) wr_ad_status(recip, AD_STAT_DONE);
		delivery_set(recip->ad_no, int_Qmgr_status_success);
	}
}

/* \f

 */
static int toProcess (msg, recip)
/* returns true if should process msg for recip on mychan */
char	*msg;
ADDR 	*recip;
{
	LIST_RCHAN	*chan;
	if ((chan = recip->ad_outchan) == NULL) {
		PP_OPER(NULLCP,
			("%s : no outbound channel specified for recipient %d of msg '%s'",
			 mychan -> ch_name,
			 recip->ad_no,
			 msg));
		exit(1);
	}
			
	if (strcmp(chan->li_chan->ch_name, mychan->ch_name) == 0)
		return TRUE;
	else {
		PP_LOG(LLOG_EXCEPTIONS,
		       ("Channel mismatch for recip %d of msg '%s'",
			recip->ad_no,
			msg));
		return FALSE;
	}
}	

/* \f

 */
static int processMsg (msg,recip, info)
/* returns OK if managed to process msg for recip on mychan */
char	*msg;
ADDR 	*recip;
ProgInfo *info;
{
	char 	*origdir = NULL;
	int result = OK;

	if (qid2dir(msg, recip, TRUE, &origdir) != OK) {
		PP_LOG(LLOG_EXCEPTIONS,
		       ("%s no message directory for recip %d of message '%s'",
			mychan->ch_name,recip->ad_no, msg));
		(void) sprintf(buf,
			       "Unable to find source directory");
		result = NOTOK;
	}
	
	if ((result == OK) && (doProcess(origdir, info) != OK))
		result = NOTOK;

	if (origdir != NULL) free(origdir);
	return result;
}

/* \f

 */

static int sigpiped, sigalarmed;
static int alarmed(), piped();
static jmp_buf pipe_alrm_jmp;
static char env_home[LINESIZE], env_user[LINESIZE], env_shell[LINESIZE];
static char *env[] = {
	env_home, env_user, env_shell, NULLCP
	};

extern char *dupfpath ();
/* processes orig directory contents through mychan */
static int doProcess (orig, info)
char	*orig;	/* original directory */
ProgInfo *info;
{
	int	fd[2],
		fd_stderr[2],
		result = OK,
		pid,
		pgmresult;
	char	*expanded,
		*program = NULL,
		*margv[20];
	void	(*oldalarm)(), (*oldpipe)();
	VOLATILE int	killed = 0;
	struct stat statbuf;

	if ((expanded = expand_dyn (info->cmd_line, expand_macros)) == NULLCP) {
		PP_OPER (NULLCP,
			("%s : failed to expand '%s'", info->cmd_line));
		(void) sprintf(buf,
			       "Failed to expand '%s'",
			       info -> cmd_line);
		return NOTOK;
	}

	PP_NOTICE(("%s",expanded));
	if (sstr2arg(expanded, 20, margv, " \t") < 1) {
		PP_OPER(NULLCP, ("%s : no arguments in ch_info string",
			 mychan->ch_name));
		(void) sprintf (buf,
				"No arguments in ch_info string '%s'",
				expanded);
		free(expanded);
		return NOTOK;
	}

	program = dupfpath (chndfldir, margv[0]);
	
	if (stat(program, &statbuf) != OK) {
		PP_OPER(NULLCP, ("%s : missing program '%s'",
			 mychan -> ch_name, program));
		(void) sprintf (buf,
				"Missing program '%s",
				program);
		free(expanded);
		return NOTOK;
	}
	(void) sprintf (env_home, "HOME=%s", info -> home);
	(void) sprintf (env_user, "USER=%s", info -> username ?
			info -> username : "anon");
	(void) sprintf (env_shell, "SHELL=%s", info -> shell);

	if (pipe(fd) != 0) {
		PP_SLOG(LLOG_EXCEPTIONS, "pipe",
		       ("pipe failed"));
		(void) sprintf(buf,
			       "Pipe failed");
		free(expanded);
		return NOTOK;
	}
	if (pipe(fd_stderr) != 0) {
		PP_SLOG(LLOG_EXCEPTIONS, "pipe",
		       ("stderr pipe failed"));
		free(expanded);
		(void) sprintf (buf,
				"standard error pipe failed");
		(void) close (fd[0]);
		(void) close (fd[1]);
		return NOTOK;
	}
	if ((pid = tryfork()) == 0) {
		/* in child so redirect input and stderr */
		(void) dup2 (fd[0], 0);
		(void) dup2 (fd_stderr[1], 1);
		(void) dup2 (fd_stderr[1], 2);
		(void) close(fd[0]);
		(void) close(fd[1]);
		(void) close(fd_stderr[0]);
		(void) close(fd_stderr[1]);
		(void) setpgrp(0, getpid());

		if(chdir (info -> home) == NOTOK)
			chdir ("/tmp");

		if (info -> username)
			(void) initgroups (info -> username, info -> gid);
		if (setregid (info->gid, info->gid) == -1 ||
		    setreuid (info->uid, info->uid) == -1) {
			PP_SLOG (LLOG_EXCEPTIONS, "user",
				 ("Can't set uid/gid %d/%d",
				  info -> uid, info -> gid));
			_exit(1);
		}

		execve (program,margv, env);
		_exit (1);

	} else if (pid == -1) {
		PP_SLOG(LLOG_EXCEPTIONS, this_msg,
		       ("tryfork failed"));
		(void) sprintf(buf,
			       "Tryfork failed");
		(void) close(fd_stderr[1]);
		(void) close(fd[1]);
		(void) close(fd_stderr[0]);
		(void) close(fd[0]);
		result = NOTOK;
	} else {
		union wait 	w;
		
		(void) close(fd_stderr[1]);
		(void) close(fd[0]);
		
		oldalarm = signal(SIGALRM, alarmed);
		oldpipe = signal(SIGPIPE, piped);
		sigpiped = 0;
		sigalarmed = 0;
		
		if (info -> timeout_in_secs > 0) {
			alarm(info -> timeout_in_secs);
			PP_TRACE(("alarm set for %d secs",
				  info -> timeout_in_secs));
		}
		if (setjmp(pipe_alrm_jmp) != DONE) {
			send_to_child(orig, fd[1]);
			(void) close(fd[1]);
		} else {
			if (sigalarmed)
				PP_TRACE(("alarm went off"));
			if (sigpiped)
				PP_TRACE(("pipe went off"));
		}

		if (sigpiped) { /* pipe died - reset for timeout */
			sigpiped = 0;
			if (setjmp (pipe_alrm_jmp) == DONE)
				PP_TRACE(("Timeout"));
		}

		if (sigalarmed) { /* alarm went off */
			PP_NOTICE (("Process taking too long ... killing"));
			(void) sprintf(buf,
				       "Process took to long - killed");
			killed = 1;
			(void) killpg (pid, SIGTERM);
			sleep(2);
			(void) killpg (pid, SIGKILL);
		}
		do_child_logging (fd_stderr[0]);

		while ((pgmresult = wait(&w)) != pid && pgmresult != -1)
			PP_TRACE(("process %d returned", pgmresult));

		alarm(0);

		PP_TRACE(("pid %d term=%d retcode=%d core=%d killed=%d",
			  pid, w.w_termsig, w.w_retcode,
			  w.w_coredump, killed));

		(void) signal (SIGPIPE, oldpipe);
		(void) signal (SIGALRM, oldalarm);
		do_child_logging(fd_stderr[0]);
		(void) close(fd[1]);
		(void) close (fd_stderr[0]);
		
		if (pgmresult == pid && !killed && WIFEXITED(w) && rp_gbval(w.w_retcode) == RP_BNO)
			fatal = TRUE;

		if ((pgmresult == pid) && (w.w_retcode == 0) && (killed == 0))
			result = OK;
		else 
			result = NOTOK;
	}

	free(expanded);
	return result;
}

static int do_child_logging(fd)
int	fd;
{
	fd_set	rfds, ifds;
	char	buf[80];	/* smallish */
	int	n;
	struct timeval timer;
	char	*newfatal;
	timerclear(&timer);

	PP_TRACE (("suckuperrors (%d)", fd));
	FD_ZERO (&rfds);
	FD_SET (fd, &rfds);

#define the_universe_exists	1

	while (the_universe_exists) {
		ifds = rfds;

		if (select (fd + 1, &ifds, NULLFD, NULLFD, &timer) <= 0)
			break;

		PP_TRACE (("select fired"));
		if (FD_ISSET (fd, &ifds)) {
			if((n = read (fd, buf, sizeof buf - 1)) > 0) {
				buf[n] = 0;
				PP_LOG (LLOG_EXCEPTIONS,
					("Output from process '%s'", buf));
				if (fatalbuf == NULLCP)
					fatalbuf = strdup(buf);
				else {
					newfatal = malloc(strlen(fatalbuf) + strlen(buf) + 1);
					strcat (newfatal, fatalbuf);
					strcat (newfatal, buf);
					free(fatalbuf);
					fatalbuf = newfatal;
				}
			}
			else break;
		}
		else	break;
	}
}

static int alarmed()
{
	sigalarmed = 1;
	longjmp(pipe_alrm_jmp, DONE);
}

static int piped ()
{
	sigpiped = 1;
	longjmp(pipe_alrm_jmp, DONE);
}

/* \f

 */
static int write_to_child(file, fd_out)
char	*file;
int	fd_out;
{
	char	buf[BUFSIZ];
	int	fd_in,
		num;

	if ((fd_in = open(file, O_RDONLY, 0666)) == -1) {
		PP_SLOG(LLOG_EXCEPTIONS, file,
		       ("Can't open file"));
		return NOTOK;
	}

	while ((num = read(fd_in, buf, BUFSIZ)) > 0)
		if (write (fd_out, buf, num) == -1) {
			(void) close(fd_in);
			return NOTOK;
			}
	(void) close(fd_in);
	return OK;
}
		       
static ADDR *getnthrecip(que, num)
Q_struct	*que;
int		num;
{
	ADDR *ix = que->Raddress;
	int icount = 1;
	if (num == 0)
		return que->Oaddress;
	while ((ix != NULL) && (icount++ < num))
		ix = ix->ad_next;
	return ix;
}


static char	*get_adrstr(adr)
ADDR	*adr;
{
	char	*key;
	switch (adr->ad_type) {
	    case AD_X400_TYPE:
		key = adr->ad_r400adr;
		break;
	    case AD_822_TYPE:
		key = adr->ad_r822adr;
		break;
	    default:
		key = adr->ad_value;
		break;
	}
	return key;
}

static int is822hdr(file)
char	*file;
{
	char *ix = rindex(file, '/');

	if (ix == NULL
	    || (strncmp((ix+1),"hdr.822",strlen("hdr.822")) != 0))
		return FALSE;
	else
		return TRUE;
}

static int send_to_child(orig, fd)
char	*orig;
int	fd;
{
	char	file[FILNSIZE];
	void	(*old_sig) ();
		
	msg_rinit(orig);

	pipe_signaled = FALSE;

	/* set signal handler */
	old_sig = signal (SIGPIPE, sig_pipe);

	/* set long jump */
	setjmp(toplevel);

	while (pipe_signaled == FALSE
	       && msg_rfile(file) == RP_OK) {
		write_to_child(file, fd);
		if (is822hdr(file) == TRUE)
			write(fd, "\n", 1);
	}

	msg_rend();

	/* unset signal handler */
	signal (SIGPIPE, old_sig);
}