xref: /titanic_51/usr/src/lib/libast/common/sfio/sfpopen.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
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