14887Schin /***********************************************************************
24887Schin * *
34887Schin * This software is part of the ast package *
4*12068SRoger.Faulkner@Oracle.COM * Copyright (c) 1985-2010 AT&T Intellectual Property *
54887Schin * and is licensed under the *
64887Schin * Common Public License, Version 1.0 *
78462SApril.Chin@Sun.COM * by AT&T Intellectual Property *
84887Schin * *
94887Schin * A copy of the License is available at *
104887Schin * http://www.opensource.org/licenses/cpl1.0.txt *
114887Schin * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
124887Schin * *
134887Schin * Information and Software Systems Research *
144887Schin * AT&T Research *
154887Schin * Florham Park NJ *
164887Schin * *
174887Schin * Glenn Fowler <gsf@research.att.com> *
184887Schin * David Korn <dgk@research.att.com> *
194887Schin * Phong Vo <kpv@research.att.com> *
204887Schin * *
214887Schin ***********************************************************************/
224887Schin #include "sfhdr.h"
234887Schin
244887Schin /* Create a coprocess.
254887Schin ** Written by Kiem-Phong Vo.
264887Schin */
274887Schin
284887Schin #if _PACKAGE_ast
294887Schin #include <proc.h>
304887Schin #else
314887Schin
324887Schin #define EXIT_NOTFOUND 127
334887Schin
344887Schin #define READ 0
354887Schin #define WRITE 1
364887Schin
374887Schin #ifndef CHAR_BIT
384887Schin #define CHAR_BIT 8
394887Schin #endif
404887Schin static char Meta[1<<CHAR_BIT], **Path;
414887Schin
424887Schin /* execute command directly if possible; else use the shell */
434887Schin #if __STD_C
execute(const char * argcmd)444887Schin static void execute(const char* argcmd)
454887Schin #else
464887Schin static void execute(argcmd)
474887Schin char* argcmd;
484887Schin #endif
494887Schin {
504887Schin reg char *s, *cmd, **argv, **p, *interp;
514887Schin reg int n;
524887Schin
534887Schin /* define interpreter */
544887Schin if(!(interp = getenv("SHELL")) || !interp[0])
554887Schin interp = "/bin/sh";
564887Schin
574887Schin if(strcmp(interp,"/bin/sh") != 0 && strcmp(interp,"/bin/ksh") != 0 )
584887Schin { if(access(interp,X_OK) == 0)
594887Schin goto do_interp;
604887Schin else interp = "/bin/sh";
614887Schin }
624887Schin
634887Schin /* if there is a meta character, let the shell do it */
644887Schin for(s = (char*)argcmd; *s; ++s)
654887Schin if(Meta[(uchar)s[0]])
664887Schin goto do_interp;
674887Schin
684887Schin /* try to construct argv */
694887Schin if(!(cmd = (char*)malloc(strlen(argcmd)+1)) )
704887Schin goto do_interp;
714887Schin strcpy(cmd,argcmd);
724887Schin if(!(argv = (char**)malloc(16*sizeof(char*))) )
734887Schin goto do_interp;
744887Schin for(n = 0, s = cmd;; )
754887Schin { while(isspace(s[0]))
764887Schin s += 1;
774887Schin if(s[0] == 0)
784887Schin break;
794887Schin
804887Schin /* new argument */
814887Schin argv[n++] = s;
824887Schin if((n%16) == 0 && !(argv = (char**)realloc(argv,(n+16)*sizeof(char*))) )
834887Schin goto do_interp;
844887Schin
854887Schin /* make this into a C string */
864887Schin while(s[0] && !isspace(s[0]))
874887Schin s += 1;
884887Schin if(!s[0])
894887Schin *s++ = 0;
904887Schin }
914887Schin if(n == 0)
924887Schin goto do_interp;
934887Schin argv[n] = NIL(char*);
944887Schin
954887Schin /* get the command name */
964887Schin cmd = argv[0];
974887Schin for(s = cmd+strlen(cmd)-1; s >= cmd; --s)
984887Schin if(*s == '/')
994887Schin break;
1004887Schin argv[0] = s+1;
1014887Schin
1024887Schin /* Non-standard pathnames as in nDFS should be handled by the shell */
1034887Schin for(s = cmd+strlen(cmd)-1; s >= cmd+2; --s)
1044887Schin if(s[0] == '.' && s[-1] == '.' && s[-2] == '.')
1054887Schin goto do_interp;
1064887Schin
1074887Schin if(cmd[0] == '/' ||
1084887Schin (cmd[0] == '.' && cmd[1] == '/') ||
1094887Schin (cmd[0] == '.' && cmd[1] == '.' && cmd[2] == '/') )
1104887Schin { if(access(cmd,X_OK) != 0)
1114887Schin goto do_interp;
1124887Schin else execv(cmd,argv);
1134887Schin }
1144887Schin else
1154887Schin { for(p = Path; *p; ++p)
1164887Schin { s = sfprints("%s/%s", *p, cmd);
1174887Schin if(access(s,X_OK) == 0)
1184887Schin execv(s,argv);
1194887Schin }
1204887Schin }
1214887Schin
1224887Schin /* if get here, let the interpreter do it */
1234887Schin do_interp:
1244887Schin for(s = interp+strlen(interp)-1; s >= interp; --s)
1254887Schin if(*s == '/')
1264887Schin break;
1274887Schin execl(interp, s+1, "-c", argcmd, NIL(char*));
1284887Schin _exit(EXIT_NOTFOUND);
1294887Schin }
1304887Schin
1314887Schin #endif /*_PACKAGE_ast*/
1324887Schin
1334887Schin #if __STD_C
sfpopen(Sfio_t * f,const char * command,const char * mode)1344887Schin Sfio_t* sfpopen(Sfio_t* f, const char* command, const char* mode)
1354887Schin #else
1364887Schin Sfio_t* sfpopen(f,command,mode)
1374887Schin Sfio_t* f;
1384887Schin char* command; /* command to execute */
1394887Schin char* mode; /* mode of the stream */
1404887Schin #endif
1414887Schin {
1424887Schin #if _PACKAGE_ast
1434887Schin reg Proc_t* proc;
1444887Schin reg int sflags;
1454887Schin reg long flags;
1464887Schin reg int pflags;
1474887Schin char* av[4];
1484887Schin
1494887Schin if (!command || !command[0] || !mode)
1504887Schin return 0;
1514887Schin sflags = _sftype(mode, NiL, NiL);
1524887Schin
1534887Schin if(f == (Sfio_t*)(-1))
1544887Schin { /* stdio compatibility mode */
1554887Schin f = NIL(Sfio_t*);
1564887Schin pflags = 1;
1574887Schin }
1584887Schin else pflags = 0;
1594887Schin
1604887Schin flags = 0;
1614887Schin if (sflags & SF_READ)
1624887Schin flags |= PROC_READ;
1634887Schin if (sflags & SF_WRITE)
1644887Schin flags |= PROC_WRITE;
1654887Schin av[0] = "sh";
1664887Schin av[1] = "-c";
1674887Schin av[2] = (char*)command;
1684887Schin av[3] = 0;
1694887Schin if (!(proc = procopen(0, av, 0, 0, flags)))
1704887Schin return 0;
1714887Schin if (!(f = sfnew(f, NIL(Void_t*), (size_t)SF_UNBOUND,
1724887Schin (sflags&SF_READ) ? proc->rfd : proc->wfd, sflags|((sflags&SF_RDWR)?0:SF_READ))) ||
1734887Schin _sfpopen(f, (sflags&SF_READ) ? proc->wfd : -1, proc->pid, pflags) < 0)
1744887Schin {
1754887Schin if (f) sfclose(f);
1764887Schin procclose(proc);
1774887Schin return 0;
1784887Schin }
1794887Schin procfree(proc);
1804887Schin return f;
1814887Schin #else
1824887Schin reg int pid, fd, pkeep, ckeep, sflags;
1834887Schin int stdio, parent[2], child[2];
1844887Schin Sfio_t sf;
1854887Schin
1864887Schin /* set shell meta characters */
1874887Schin if(Meta[0] == 0)
1884887Schin { reg char* s;
1894887Schin Meta[0] = 1;
1904887Schin for(s = "!@#$%&*(){}[]:;<>~`'|\"\\"; *s; ++s)
1914887Schin Meta[(uchar)s[0]] = 1;
1924887Schin }
1934887Schin if(!Path)
1944887Schin Path = _sfgetpath("PATH");
1954887Schin
1964887Schin /* sanity check */
1974887Schin if(!command || !command[0] || !mode)
1984887Schin return NIL(Sfio_t*);
1994887Schin sflags = _sftype(mode,NIL(int*),NIL(int*));
2004887Schin
2014887Schin /* make pipes */
2024887Schin parent[0] = parent[1] = child[0] = child[1] = -1;
2034887Schin if(sflags&SF_RDWR)
2044887Schin { if(syspipef(parent) < 0)
2054887Schin goto error;
2064887Schin if((sflags&SF_RDWR) == SF_RDWR && syspipef(child) < 0)
2074887Schin goto error;
2084887Schin }
2094887Schin
2104887Schin switch((pid = fork()) )
2114887Schin {
2124887Schin default : /* in parent process */
2134887Schin if(sflags&SF_READ)
2144887Schin { pkeep = READ; ckeep = WRITE; }
2154887Schin else { pkeep = WRITE; ckeep = READ; }
2164887Schin
2174887Schin if(f == (Sfio_t*)(-1))
2184887Schin { /* stdio compatibility mode */
2194887Schin f = NIL(Sfio_t*);
2204887Schin stdio = 1;
2214887Schin }
2224887Schin else stdio = 0;
2234887Schin
2244887Schin /* make the streams */
2254887Schin if(!(f = sfnew(f,NIL(Void_t*),(size_t)SF_UNBOUND,parent[pkeep],sflags|((sflags&SF_RDWR)?0:SF_READ))))
2264887Schin goto error;
2274887Schin if(sflags&SF_RDWR)
2284887Schin { CLOSE(parent[!pkeep]);
2294887Schin SETCLOEXEC(parent[pkeep]);
2304887Schin if((sflags&SF_RDWR) == SF_RDWR)
2314887Schin { CLOSE(child[!ckeep]);
2324887Schin SETCLOEXEC(child[ckeep]);
2334887Schin }
2344887Schin }
2354887Schin
2364887Schin /* save process info */
2374887Schin fd = (sflags&SF_RDWR) == SF_RDWR ? child[ckeep] : -1;
2384887Schin if(_sfpopen(f,fd,pid,stdio) < 0)
2394887Schin { (void)sfclose(f);
2404887Schin goto error;
2414887Schin }
2424887Schin
2434887Schin return f;
2444887Schin
2454887Schin case 0 : /* in child process */
2464887Schin /* determine what to keep */
2474887Schin if(sflags&SF_READ)
2484887Schin { pkeep = WRITE; ckeep = READ; }
2494887Schin else { pkeep = READ; ckeep = WRITE; }
2504887Schin
2514887Schin /* zap fd that we don't need */
2524887Schin if(sflags&SF_RDWR)
2534887Schin { CLOSE(parent[!pkeep]);
2544887Schin if((sflags&SF_RDWR) == SF_RDWR)
2554887Schin CLOSE(child[!ckeep]);
2564887Schin }
2574887Schin
2584887Schin /* use sfsetfd to make these descriptors the std-ones */
2594887Schin SFCLEAR(&sf,NIL(Vtmutex_t*));
2604887Schin
2614887Schin /* must be careful so not to close something useful */
2624887Schin if((sflags&SF_RDWR) == SF_RDWR && pkeep == child[ckeep])
2634887Schin if((child[ckeep] = sysdupf(pkeep)) < 0)
2644887Schin _exit(EXIT_NOTFOUND);
2654887Schin
2664887Schin if(sflags&SF_RDWR)
2674887Schin { if (parent[pkeep] != pkeep)
2684887Schin { sf.file = parent[pkeep];
2694887Schin CLOSE(pkeep);
2704887Schin if(sfsetfd(&sf,pkeep) != pkeep)
2714887Schin _exit(EXIT_NOTFOUND);
2724887Schin }
2734887Schin if((sflags&SF_RDWR) == SF_RDWR && child[ckeep] != ckeep)
2744887Schin { sf.file = child[ckeep];
2754887Schin CLOSE(ckeep);
2764887Schin if(sfsetfd(&sf,ckeep) != ckeep)
2774887Schin _exit(EXIT_NOTFOUND);
2784887Schin }
2794887Schin }
2804887Schin
2814887Schin execute(command);
2824887Schin return NIL(Sfio_t*);
2834887Schin
2844887Schin case -1 : /* error */
2854887Schin error:
2864887Schin if(parent[0] >= 0)
2874887Schin { CLOSE(parent[0]); CLOSE(parent[1]); }
2884887Schin if(child[0] >= 0)
2894887Schin { CLOSE(child[0]); CLOSE(child[1]); }
2904887Schin return NIL(Sfio_t*);
2914887Schin }
2924887Schin #endif /*_PACKAGE_ast*/
2934887Schin }
294