xref: /onnv-gate/usr/src/lib/libast/common/sfio/sfpopen.c (revision 12068:08a39a083754)
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