1 /************************************************************************
2 * Routines related to setting up pipes and filters *
3 * *
4 * Copyright (c) 1990-1999, S.R. van den Berg, The Netherlands *
5 * Copyright (c) 1998-2001, Philip Guenther, The United States *
6 * of America *
7 * #include "../README" *
8 ************************************************************************/
9 #ifdef RCS
10 static /*const*/char rcsid[]=
11 "$Id: pipes.c,v 1.73 2001/08/27 08:43:59 guenther Exp $";
12 #endif
13 #include "procmail.h"
14 #include "robust.h"
15 #include "shell.h"
16 #include "misc.h"
17 #include "memblk.h"
18 #include "pipes.h"
19 #include "common.h"
20 #include "mcommon.h"
21 #include "foldinfo.h"
22 #include "mailfold.h"
23 #include "goodies.h"
24 #include "variables.h"
25
26 static const char comma[]=",";
27 pid_t pidchild;
28 volatile time_t alrmtime;
29 volatile int toutflag;
30 static char*lastexec,*backblock;
31 static long backlen; /* length of backblock, filter recovery block */
32 static pid_t pidfilt;
33 static int pbackfd[2]; /* the emergency backpipe :-) */
34 int pipw;
35
36 #define PRDO poutfd[0]
37 #define PWRO poutfd[1]
38 #define PRDI pinfd[0]
39 #define PWRI pinfd[1]
40 #define PRDB pbackfd[0]
41 #define PWRB pbackfd[1]
42
inittmout(progname)43 void inittmout(progname)const char*const progname;
44 { lastexec=cstr(lastexec,progname);toutflag=0;
45 alrmtime=timeoutv?time((time_t*)0)+(unsigned)timeoutv:0;
46 alarm((unsigned)timeoutv);
47 }
48
ftimeout(void)49 void ftimeout P((void))
50 { alarm(0);alrmtime=0;toutflag=1;nlog("Timeout, "); /* careful, killing */
51 elog(pidchild>0&&!kill(pidchild,SIGTERM)?"terminating":"was waiting for");
52 logqnl(lastexec);signal(SIGALRM,(void(*)())ftimeout);
53 }
54
resettmout(void)55 void resettmout P((void))
56 { if(alrmtime) /* any need to reset timeout? */
57 alarm((unsigned)(alrmtime=0)); /* reset timeout */
58 }
59
stermchild(void)60 static void stermchild P((void))
61 { static const char rescdata[]="Rescue of unfiltered data ";
62 if(pidfilt>0) /* don't kill what is not ours, we might be root */
63 kill(pidfilt,SIGTERM);
64 rawnonl=1; /* give back the raw contents */
65 if(PWRB<0); /* previously unset? */
66 else if(dump(PWRB,ft_PIPE,backblock,backlen)) /* pump data back via */
67 nlog(rescdata),elog("failed\n"); /* the backpipe */
68 else if(verbose||pwait!=4) /* are we not looking the other way? */
69 nlog(rescdata),elog("succeeded\n");
70 exit(lexitcode);
71 }
72
childsetup(void)73 static void childsetup P((void))
74 { lexitcode=EX_UNAVAILABLE;qsignal(SIGTERM,stermchild);
75 qsignal(SIGINT,stermchild);qsignal(SIGHUP,stermchild);
76 qsignal(SIGQUIT,stermchild);shutdesc();
77 }
78
getstdin(pip)79 static void getstdin(pip)const int pip;
80 { rclose(STDIN);rdup(pip);rclose(pip);
81 }
82
callnewprog(newname)83 static void callnewprog(newname)const char*const newname;
84 {
85 #ifdef RESTRICT_EXEC
86 if(erestrict&&uid>=RESTRICT_EXEC)
87 { syslog(LOG_ERR,slogstr,"Attempt to execute",newname);
88 nlog("No permission to execute");logqnl(newname);
89 return;
90 }
91 #endif
92 if(sh) /* should we start a shell? */
93 { const char*newargv[4];
94 yell(executing,newname);newargv[3]=0;newargv[2]=newname;
95 newargv[1]=shellflags;*newargv=tgetenv(shell);shexec(newargv);
96 }
97 ;{ register const char*p;int argc;
98 argc=1;p=newname; /* If no shell, chop up the arguments ourselves */
99 if(verbose)
100 { nlog(executing);elog(oquote);
101 goto no_1st_comma;
102 }
103 do /* show chopped up command line */
104 { if(verbose)
105 { elog(comma);
106 no_1st_comma:
107 elog(p);
108 }
109 while(*p++);
110 if(verbose&&p-1==All_args&&crestarg) /* any "$@" found? */
111 { const char*const*walkargs=restargv;
112 goto No_1st_comma;
113 do
114 { elog(comma);
115 No_1st_comma: elog(*walkargs); /* expand it */
116 }
117 while(*++walkargs);
118 }
119 if(p-1==All_args)
120 argc+=crestarg-1; /* and account for it */
121 }
122 while(argc++,p!=Tmnate);
123 if(verbose)
124 elog(cquote); /* allocate argv array */
125 ;{ const char**newargv;
126 newargv=malloc(argc*sizeof*newargv);p=newname;argc=0;
127 do
128 { newargv[argc++]=p;
129 while(*p++);
130 if(p-1==All_args&&crestarg)
131 { const char*const*walkargs=restargv; /* expand "$@" */
132 argc--;
133 while(newargv[argc]= *walkargs++)
134 argc++;
135 }
136 }
137 while(p!=Tmnate);
138 newargv[argc]=0;shexec(newargv);
139 }
140 }
141 }
142
pipthrough(line,source,len)143 int pipthrough(line,source,len)char*line,*source;const long len;
144 { int pinfd[2],poutfd[2];char*eq;
145 if(Stdout)
146 { *(eq=strchr(Stdout,'\0')-1)='\0'; /* chop the '=' */
147 if(!(backblock=getenv(Stdout))) /* no current value? */
148 PRDB=PWRB= -1;
149 else
150 { backlen=strlen(backblock);
151 goto pip;
152 }
153 }
154 else
155 pip: rpipe(pbackfd);
156 rpipe(pinfd); /* main pipes setup */
157 if(!(pidchild=sfork())) /* create a sending procmail */
158 { if(Stdout&&backblock)
159 backlen=strlen(backblock);
160 else
161 backblock=source,backlen=len;
162 childsetup();rclose(PRDI);rclose(PRDB);
163 rpipe(poutfd);rclose(STDOUT);
164 if(!(pidfilt=sfork())) /* create the filter */
165 { rclose(PWRB);rclose(PWRO);rdup(PWRI);rclose(PWRI);getstdin(PRDO);
166 callnewprog(line);
167 }
168 rclose(PWRI);rclose(PRDO);
169 if(forkerr(pidfilt,line))
170 rclose(PWRO),stermchild();
171 if(dump(PWRO,ft_PIPE,source,len)&&!ignwerr) /* send in the text */
172 writeerr(line),lexitcode=EX_IOERR,stermchild(); /* to be filtered */
173 ;{ int excode; /* optionally check the exitcode of the filter */
174 if(pwait&&(excode=waitfor(pidfilt))!=EXIT_SUCCESS)
175 { pidfilt=0;
176 if(pwait&2) /* do we put it on report? */
177 { pwait=4; /* no, we'll look the other way */
178 if(verbose)
179 goto perr;
180 }
181 else
182 perr: progerr(line,excode,pwait==4); /* I'm going to tell my mommy! */
183 stermchild();
184 }
185 }
186 rclose(PWRB);exit(EXIT_SUCCESS); /* allow parent to proceed */
187 }
188 rclose(PWRB);rclose(PWRI);getstdin(PRDI);
189 if(forkerr(pidchild,procmailn))
190 return -1;
191 if(Stdout)
192 { char*name;memblk temp; /* ick. This is inefficient XXX */
193 *eq='=';name=Stdout;Stdout=0;primeStdout(name);free(name);
194 makeblock(&temp,Stdfilled);
195 tmemmove(temp.p,Stdout,Stdfilled);
196 readdyn(&temp,&Stdfilled,Stdfilled+backlen+1);
197 Stdout=realloc(Stdout,Stdfilled+1);
198 tmemmove(Stdout,temp.p,Stdfilled+1);
199 freeblock(&temp);
200 retStdout(Stdout,pwait&&pipw,!backblock);
201 return pipw;
202 }
203 return 0; /* we stay behind to read back the filtered text */
204 }
205
pipin(line,source,len,asgnlastf)206 long pipin(line,source,len,asgnlastf)char*const line;char*source;long len;
207 int asgnlastf;
208 { int poutfd[2];
209 #if 0 /* not supported (yet?) */
210 if(!sh) /* shortcut builtin commands */
211 { const char*t1,*t2,*t3;
212 static const char pbuiltin[]="Builtin";
213 t1=strchr(line,'\0')+1;
214 if(!strcmp(test,line))
215 { if(t1!=Tmnate)
216 { t2=strchr(t1,'\0')+1;
217 if(t2!=Tmnate)
218 { t3=strchr(t2,'\0')+1;
219 if(t3!=Tmnate&&!strcmp(t2,"=")&&strchr(t3,'\0')==Tmnate-1)
220 { int excode;
221 if(verbose)
222 { nlog(pbuiltin);elog(oquote);elog(test);elog(comma),
223 if(!ignwerr)
224 writeerr(line);
225 else
226 len=0;
227 if(pwait&&(excode=strcmp(t1,t3)?1:EXIT_SUCCESS)!=EXIT_SUCCESS)
228 { if(!(pwait&2)||verbose) /* do we put it on report? */
229 progerr(line,excode,pwait&2);
230 len=1;
231 }
232 goto builtin;
233 }
234 }
235 }
236 }
237 }
238 #endif
239 rpipe(poutfd);
240 if(!(pidchild=sfork())) /* spawn program */
241 rclose(PWRO),shutdesc(),getstdin(PRDO),callnewprog(line);
242 rclose(PRDO);
243 if(forkerr(pidchild,line))
244 { rclose(PWRO);
245 return -1; /* dump mail in the pipe */
246 }
247 if((len=dump(PWRO,ft_PIPE,source,len))&&(!ignwerr||(len=0)))
248 writeerr(line); /* pipe was shut in our face, get mad */
249 ;{ int excode; /* optionally check the exitcode */
250 if(pwait&&(excode=waitfor(pidchild))!=EXIT_SUCCESS)
251 { if(!(pwait&2)||verbose) /* do we put it on report? */
252 progerr(line,excode,pwait&2);
253 len=1;
254 }
255 }
256 pidchild=0;
257 builtin:
258 if(!sh)
259 concatenate(line);
260 if(asgnlastf)
261 setlastfolder(line);
262 return len;
263 }
264
read_read(p,left,data)265 static char*read_read(p,left,data)char*p;long left;void*data;
266 { long got;
267 do
268 if(0>=(got=rread(STDIN,p,left))) /* read mail */
269 return p;
270 while(p+=got,left-=got); /* change listed buffer size */
271 return 0;
272 }
273
read_cleanup(mb,filledp,origfilled,data)274 static int read_cleanup(mb,filledp,origfilled,data)memblk*mb;
275 long*filledp,origfilled;void*data;
276 { long oldfilled= *(long*)data;
277 if(pidchild>0)
278 { if(PRDB>=0)
279 { getstdin(PRDB); /* filter ready, get backpipe */
280 if(1==rread(STDIN,buf,1)) /* backup pipe closed? */
281 { resizeblock(mb,oldfilled,0);
282 mb->p[origfilled]= *buf;
283 *filledp=origfilled+1;
284 PRDB= -1;pwait=2; /* break loop, definitely reap */
285 return 1; /* filter goofed, rescue data */
286 }
287 }
288 if(pwait)
289 pipw=waitfor(pidchild); /* reap your child in any case */
290 }
291 pidchild=0; /* child must be gone by now */
292 return 0;
293 }
294
readdyn(mb,filled,oldfilled)295 char*readdyn(mb,filled,oldfilled)memblk*const mb;long*const filled,oldfilled;
296 { return read2blk(mb,filled,&read_read,&read_cleanup,&oldfilled);
297 }
298
fromprog(name,dest,max)299 char*fromprog(name,dest,max)char*name;char*const dest;size_t max;
300 { int pinfd[2],poutfd[2];int i;char*p;
301 concon('\n');rpipe(pinfd);inittmout(name);
302 if(!(pidchild=sfork())) /* create a sending procmail */
303 { Stdout=name;childsetup();rclose(PRDI);rpipe(poutfd);rclose(STDOUT);
304 if(!(pidfilt=sfork())) /* spawn program/filter */
305 rclose(PWRO),rdup(PWRI),rclose(PWRI),getstdin(PRDO),callnewprog(name);
306 rclose(PWRI);rclose(PRDO);
307 if(forkerr(pidfilt,name))
308 rclose(PWRO),stermchild();
309 dump(PWRO,ft_PIPE,themail.p,filled);waitfor(pidfilt);exit(lexitcode);
310 }
311 rclose(PWRI);p=dest;
312 if(!forkerr(pidchild,name))
313 { name=tstrdup(name);
314 while(0<(i=rread(PRDI,p,(int)max))&&(p+=i,max-=i)); /* read its lips */
315 if(0<rread(PRDI,p,1))
316 { nlog("Excessive output quenched from");logqnl(name);
317 setoverflow();
318 }
319 else
320 while(--p>=dest&&*p=='\n'); /* trailing newlines should be discarded */
321 rclose(PRDI);free(name);
322 p++;waitfor(pidchild);
323 }
324 else
325 rclose(PRDI);
326 resettmout();
327 pidchild=0;*p='\0';
328 return p;
329 }
330
exectrap(tp)331 void exectrap(tp)const char*const tp;
332 { int forceret;
333 forceret=setexitcode(*tp); /* whether TRAP is set matters */
334 if(*tp)
335 { int poutfd[2];
336 rawnonl=0; /* force a trailing newline */
337 metaparse(tp);concon('\n'); /* expand $TRAP */
338 rpipe(poutfd);inittmout(buf);
339 if(!(pidchild=sfork())) /* connect stdout to stderr before exec */
340 { rclose(PWRO);getstdin(PRDO);rclose(STDOUT);rdup(STDERR);
341 callnewprog(buf); /* trap your heart out */
342 }
343 rclose(PRDO); /* neat & clean */
344 if(!forkerr(pidchild,buf))
345 { int newret;
346 dump(PWRO,ft_PIPE,themail.p,filled); /* try and shove down the mail */
347 if((newret=waitfor(pidchild))!=EXIT_SUCCESS&&forceret==-2)
348 retval=newret; /* supersede the return value */
349 pidchild=0;
350 }
351 else
352 rclose(PWRO);
353 resettmout();
354 }
355 }
356