1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2012 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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
40 static char	Meta[1<<CHAR_BIT], **Path;
41 
42 /* execute command directly if possible; else use the shell */
43 #if __STD_C
execute(const char * argcmd)44 static void execute(const char* argcmd)
45 #else
46 static void execute(argcmd)
47 char*	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 */
123 do_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
sfpopen(Sfio_t * f,const char * command,const char * mode)134 Sfio_t*	sfpopen(Sfio_t* f, const char* command, const char* mode)
135 #else
136 Sfio_t*	sfpopen(f,command,mode)
137 Sfio_t*	f;
138 char*	command;	/* command to execute */
139 char*	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(int*), NIL(int*), NIL(int*));
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*),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