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