1da2e3ebdSchin /***********************************************************************
2da2e3ebdSchin * *
3da2e3ebdSchin * This software is part of the ast package *
4*3e14f97fSRoger A. Faulkner * Copyright (c) 1985-2010 AT&T Intellectual Property *
5da2e3ebdSchin * and is licensed under the *
6da2e3ebdSchin * Common Public License, Version 1.0 *
77c2fbfb3SApril Chin * by AT&T Intellectual Property *
8da2e3ebdSchin * *
9da2e3ebdSchin * A copy of the License is available at *
10da2e3ebdSchin * http://www.opensource.org/licenses/cpl1.0.txt *
11da2e3ebdSchin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12da2e3ebdSchin * *
13da2e3ebdSchin * Information and Software Systems Research *
14da2e3ebdSchin * AT&T Research *
15da2e3ebdSchin * Florham Park NJ *
16da2e3ebdSchin * *
17da2e3ebdSchin * Glenn Fowler <gsf@research.att.com> *
18da2e3ebdSchin * David Korn <dgk@research.att.com> *
19da2e3ebdSchin * Phong Vo <kpv@research.att.com> *
20da2e3ebdSchin * *
21da2e3ebdSchin ***********************************************************************/
22da2e3ebdSchin #include "sfhdr.h"
23da2e3ebdSchin
24da2e3ebdSchin /* Create a coprocess.
25da2e3ebdSchin ** Written by Kiem-Phong Vo.
26da2e3ebdSchin */
27da2e3ebdSchin
28da2e3ebdSchin #if _PACKAGE_ast
29da2e3ebdSchin #include <proc.h>
30da2e3ebdSchin #else
31da2e3ebdSchin
32da2e3ebdSchin #define EXIT_NOTFOUND 127
33da2e3ebdSchin
34da2e3ebdSchin #define READ 0
35da2e3ebdSchin #define WRITE 1
36da2e3ebdSchin
37da2e3ebdSchin #ifndef CHAR_BIT
38da2e3ebdSchin #define CHAR_BIT 8
39da2e3ebdSchin #endif
40da2e3ebdSchin static char Meta[1<<CHAR_BIT], **Path;
41da2e3ebdSchin
42da2e3ebdSchin /* execute command directly if possible; else use the shell */
43da2e3ebdSchin #if __STD_C
execute(const char * argcmd)44da2e3ebdSchin static void execute(const char* argcmd)
45da2e3ebdSchin #else
46da2e3ebdSchin static void execute(argcmd)
47da2e3ebdSchin char* argcmd;
48da2e3ebdSchin #endif
49da2e3ebdSchin {
50da2e3ebdSchin reg char *s, *cmd, **argv, **p, *interp;
51da2e3ebdSchin reg int n;
52da2e3ebdSchin
53da2e3ebdSchin /* define interpreter */
54da2e3ebdSchin if(!(interp = getenv("SHELL")) || !interp[0])
55da2e3ebdSchin interp = "/bin/sh";
56da2e3ebdSchin
57da2e3ebdSchin if(strcmp(interp,"/bin/sh") != 0 && strcmp(interp,"/bin/ksh") != 0 )
58da2e3ebdSchin { if(access(interp,X_OK) == 0)
59da2e3ebdSchin goto do_interp;
60da2e3ebdSchin else interp = "/bin/sh";
61da2e3ebdSchin }
62da2e3ebdSchin
63da2e3ebdSchin /* if there is a meta character, let the shell do it */
64da2e3ebdSchin for(s = (char*)argcmd; *s; ++s)
65da2e3ebdSchin if(Meta[(uchar)s[0]])
66da2e3ebdSchin goto do_interp;
67da2e3ebdSchin
68da2e3ebdSchin /* try to construct argv */
69da2e3ebdSchin if(!(cmd = (char*)malloc(strlen(argcmd)+1)) )
70da2e3ebdSchin goto do_interp;
71da2e3ebdSchin strcpy(cmd,argcmd);
72da2e3ebdSchin if(!(argv = (char**)malloc(16*sizeof(char*))) )
73da2e3ebdSchin goto do_interp;
74da2e3ebdSchin for(n = 0, s = cmd;; )
75da2e3ebdSchin { while(isspace(s[0]))
76da2e3ebdSchin s += 1;
77da2e3ebdSchin if(s[0] == 0)
78da2e3ebdSchin break;
79da2e3ebdSchin
80da2e3ebdSchin /* new argument */
81da2e3ebdSchin argv[n++] = s;
82da2e3ebdSchin if((n%16) == 0 && !(argv = (char**)realloc(argv,(n+16)*sizeof(char*))) )
83da2e3ebdSchin goto do_interp;
84da2e3ebdSchin
85da2e3ebdSchin /* make this into a C string */
86da2e3ebdSchin while(s[0] && !isspace(s[0]))
87da2e3ebdSchin s += 1;
88da2e3ebdSchin if(!s[0])
89da2e3ebdSchin *s++ = 0;
90da2e3ebdSchin }
91da2e3ebdSchin if(n == 0)
92da2e3ebdSchin goto do_interp;
93da2e3ebdSchin argv[n] = NIL(char*);
94da2e3ebdSchin
95da2e3ebdSchin /* get the command name */
96da2e3ebdSchin cmd = argv[0];
97da2e3ebdSchin for(s = cmd+strlen(cmd)-1; s >= cmd; --s)
98da2e3ebdSchin if(*s == '/')
99da2e3ebdSchin break;
100da2e3ebdSchin argv[0] = s+1;
101da2e3ebdSchin
102da2e3ebdSchin /* Non-standard pathnames as in nDFS should be handled by the shell */
103da2e3ebdSchin for(s = cmd+strlen(cmd)-1; s >= cmd+2; --s)
104da2e3ebdSchin if(s[0] == '.' && s[-1] == '.' && s[-2] == '.')
105da2e3ebdSchin goto do_interp;
106da2e3ebdSchin
107da2e3ebdSchin if(cmd[0] == '/' ||
108da2e3ebdSchin (cmd[0] == '.' && cmd[1] == '/') ||
109da2e3ebdSchin (cmd[0] == '.' && cmd[1] == '.' && cmd[2] == '/') )
110da2e3ebdSchin { if(access(cmd,X_OK) != 0)
111da2e3ebdSchin goto do_interp;
112da2e3ebdSchin else execv(cmd,argv);
113da2e3ebdSchin }
114da2e3ebdSchin else
115da2e3ebdSchin { for(p = Path; *p; ++p)
116da2e3ebdSchin { s = sfprints("%s/%s", *p, cmd);
117da2e3ebdSchin if(access(s,X_OK) == 0)
118da2e3ebdSchin execv(s,argv);
119da2e3ebdSchin }
120da2e3ebdSchin }
121da2e3ebdSchin
122da2e3ebdSchin /* if get here, let the interpreter do it */
123da2e3ebdSchin do_interp:
124da2e3ebdSchin for(s = interp+strlen(interp)-1; s >= interp; --s)
125da2e3ebdSchin if(*s == '/')
126da2e3ebdSchin break;
127da2e3ebdSchin execl(interp, s+1, "-c", argcmd, NIL(char*));
128da2e3ebdSchin _exit(EXIT_NOTFOUND);
129da2e3ebdSchin }
130da2e3ebdSchin
131da2e3ebdSchin #endif /*_PACKAGE_ast*/
132da2e3ebdSchin
133da2e3ebdSchin #if __STD_C
sfpopen(Sfio_t * f,const char * command,const char * mode)134da2e3ebdSchin Sfio_t* sfpopen(Sfio_t* f, const char* command, const char* mode)
135da2e3ebdSchin #else
136da2e3ebdSchin Sfio_t* sfpopen(f,command,mode)
137da2e3ebdSchin Sfio_t* f;
138da2e3ebdSchin char* command; /* command to execute */
139da2e3ebdSchin char* mode; /* mode of the stream */
140da2e3ebdSchin #endif
141da2e3ebdSchin {
142da2e3ebdSchin #if _PACKAGE_ast
143da2e3ebdSchin reg Proc_t* proc;
144da2e3ebdSchin reg int sflags;
145da2e3ebdSchin reg long flags;
146da2e3ebdSchin reg int pflags;
147da2e3ebdSchin char* av[4];
148da2e3ebdSchin
149da2e3ebdSchin if (!command || !command[0] || !mode)
150da2e3ebdSchin return 0;
151da2e3ebdSchin sflags = _sftype(mode, NiL, NiL);
152da2e3ebdSchin
153da2e3ebdSchin if(f == (Sfio_t*)(-1))
154da2e3ebdSchin { /* stdio compatibility mode */
155da2e3ebdSchin f = NIL(Sfio_t*);
156da2e3ebdSchin pflags = 1;
157da2e3ebdSchin }
158da2e3ebdSchin else pflags = 0;
159da2e3ebdSchin
160da2e3ebdSchin flags = 0;
161da2e3ebdSchin if (sflags & SF_READ)
162da2e3ebdSchin flags |= PROC_READ;
163da2e3ebdSchin if (sflags & SF_WRITE)
164da2e3ebdSchin flags |= PROC_WRITE;
165da2e3ebdSchin av[0] = "sh";
166da2e3ebdSchin av[1] = "-c";
167da2e3ebdSchin av[2] = (char*)command;
168da2e3ebdSchin av[3] = 0;
169da2e3ebdSchin if (!(proc = procopen(0, av, 0, 0, flags)))
170da2e3ebdSchin return 0;
171da2e3ebdSchin if (!(f = sfnew(f, NIL(Void_t*), (size_t)SF_UNBOUND,
172da2e3ebdSchin (sflags&SF_READ) ? proc->rfd : proc->wfd, sflags|((sflags&SF_RDWR)?0:SF_READ))) ||
173da2e3ebdSchin _sfpopen(f, (sflags&SF_READ) ? proc->wfd : -1, proc->pid, pflags) < 0)
174da2e3ebdSchin {
175da2e3ebdSchin if (f) sfclose(f);
176da2e3ebdSchin procclose(proc);
177da2e3ebdSchin return 0;
178da2e3ebdSchin }
179da2e3ebdSchin procfree(proc);
180da2e3ebdSchin return f;
181da2e3ebdSchin #else
182da2e3ebdSchin reg int pid, fd, pkeep, ckeep, sflags;
183da2e3ebdSchin int stdio, parent[2], child[2];
184da2e3ebdSchin Sfio_t sf;
185da2e3ebdSchin
186da2e3ebdSchin /* set shell meta characters */
187da2e3ebdSchin if(Meta[0] == 0)
188da2e3ebdSchin { reg char* s;
189da2e3ebdSchin Meta[0] = 1;
190da2e3ebdSchin for(s = "!@#$%&*(){}[]:;<>~`'|\"\\"; *s; ++s)
191da2e3ebdSchin Meta[(uchar)s[0]] = 1;
192da2e3ebdSchin }
193da2e3ebdSchin if(!Path)
194da2e3ebdSchin Path = _sfgetpath("PATH");
195da2e3ebdSchin
196da2e3ebdSchin /* sanity check */
197da2e3ebdSchin if(!command || !command[0] || !mode)
198da2e3ebdSchin return NIL(Sfio_t*);
199da2e3ebdSchin sflags = _sftype(mode,NIL(int*),NIL(int*));
200da2e3ebdSchin
201da2e3ebdSchin /* make pipes */
202da2e3ebdSchin parent[0] = parent[1] = child[0] = child[1] = -1;
203da2e3ebdSchin if(sflags&SF_RDWR)
204da2e3ebdSchin { if(syspipef(parent) < 0)
205da2e3ebdSchin goto error;
206da2e3ebdSchin if((sflags&SF_RDWR) == SF_RDWR && syspipef(child) < 0)
207da2e3ebdSchin goto error;
208da2e3ebdSchin }
209da2e3ebdSchin
210da2e3ebdSchin switch((pid = fork()) )
211da2e3ebdSchin {
212da2e3ebdSchin default : /* in parent process */
213da2e3ebdSchin if(sflags&SF_READ)
214da2e3ebdSchin { pkeep = READ; ckeep = WRITE; }
215da2e3ebdSchin else { pkeep = WRITE; ckeep = READ; }
216da2e3ebdSchin
217da2e3ebdSchin if(f == (Sfio_t*)(-1))
218da2e3ebdSchin { /* stdio compatibility mode */
219da2e3ebdSchin f = NIL(Sfio_t*);
220da2e3ebdSchin stdio = 1;
221da2e3ebdSchin }
222da2e3ebdSchin else stdio = 0;
223da2e3ebdSchin
224da2e3ebdSchin /* make the streams */
225da2e3ebdSchin if(!(f = sfnew(f,NIL(Void_t*),(size_t)SF_UNBOUND,parent[pkeep],sflags|((sflags&SF_RDWR)?0:SF_READ))))
226da2e3ebdSchin goto error;
227da2e3ebdSchin if(sflags&SF_RDWR)
228da2e3ebdSchin { CLOSE(parent[!pkeep]);
229da2e3ebdSchin SETCLOEXEC(parent[pkeep]);
230da2e3ebdSchin if((sflags&SF_RDWR) == SF_RDWR)
231da2e3ebdSchin { CLOSE(child[!ckeep]);
232da2e3ebdSchin SETCLOEXEC(child[ckeep]);
233da2e3ebdSchin }
234da2e3ebdSchin }
235da2e3ebdSchin
236da2e3ebdSchin /* save process info */
237da2e3ebdSchin fd = (sflags&SF_RDWR) == SF_RDWR ? child[ckeep] : -1;
238da2e3ebdSchin if(_sfpopen(f,fd,pid,stdio) < 0)
239da2e3ebdSchin { (void)sfclose(f);
240da2e3ebdSchin goto error;
241da2e3ebdSchin }
242da2e3ebdSchin
243da2e3ebdSchin return f;
244da2e3ebdSchin
245da2e3ebdSchin case 0 : /* in child process */
246da2e3ebdSchin /* determine what to keep */
247da2e3ebdSchin if(sflags&SF_READ)
248da2e3ebdSchin { pkeep = WRITE; ckeep = READ; }
249da2e3ebdSchin else { pkeep = READ; ckeep = WRITE; }
250da2e3ebdSchin
251da2e3ebdSchin /* zap fd that we don't need */
252da2e3ebdSchin if(sflags&SF_RDWR)
253da2e3ebdSchin { CLOSE(parent[!pkeep]);
254da2e3ebdSchin if((sflags&SF_RDWR) == SF_RDWR)
255da2e3ebdSchin CLOSE(child[!ckeep]);
256da2e3ebdSchin }
257da2e3ebdSchin
258da2e3ebdSchin /* use sfsetfd to make these descriptors the std-ones */
259da2e3ebdSchin SFCLEAR(&sf,NIL(Vtmutex_t*));
260da2e3ebdSchin
261da2e3ebdSchin /* must be careful so not to close something useful */
262da2e3ebdSchin if((sflags&SF_RDWR) == SF_RDWR && pkeep == child[ckeep])
263da2e3ebdSchin if((child[ckeep] = sysdupf(pkeep)) < 0)
264da2e3ebdSchin _exit(EXIT_NOTFOUND);
265da2e3ebdSchin
266da2e3ebdSchin if(sflags&SF_RDWR)
267da2e3ebdSchin { if (parent[pkeep] != pkeep)
268da2e3ebdSchin { sf.file = parent[pkeep];
269da2e3ebdSchin CLOSE(pkeep);
270da2e3ebdSchin if(sfsetfd(&sf,pkeep) != pkeep)
271da2e3ebdSchin _exit(EXIT_NOTFOUND);
272da2e3ebdSchin }
273da2e3ebdSchin if((sflags&SF_RDWR) == SF_RDWR && child[ckeep] != ckeep)
274da2e3ebdSchin { sf.file = child[ckeep];
275da2e3ebdSchin CLOSE(ckeep);
276da2e3ebdSchin if(sfsetfd(&sf,ckeep) != ckeep)
277da2e3ebdSchin _exit(EXIT_NOTFOUND);
278da2e3ebdSchin }
279da2e3ebdSchin }
280da2e3ebdSchin
281da2e3ebdSchin execute(command);
282da2e3ebdSchin return NIL(Sfio_t*);
283da2e3ebdSchin
284da2e3ebdSchin case -1 : /* error */
285da2e3ebdSchin error:
286da2e3ebdSchin if(parent[0] >= 0)
287da2e3ebdSchin { CLOSE(parent[0]); CLOSE(parent[1]); }
288da2e3ebdSchin if(child[0] >= 0)
289da2e3ebdSchin { CLOSE(child[0]); CLOSE(child[1]); }
290da2e3ebdSchin return NIL(Sfio_t*);
291da2e3ebdSchin }
292da2e3ebdSchin #endif /*_PACKAGE_ast*/
293da2e3ebdSchin }
294