1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 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 40 static char Meta[1<<CHAR_BIT], **Path; 41 42 /* execute command directly if possible; else use the shell */ 43 #if __STD_C 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 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, 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