|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 17199 (0x432f)
Types: TextFile
Names: »shell.c«
└─⟦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«
/* 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);
}