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