ref	-D_def_map_ast=1

hdr	spawn
lib	spawn,spawnve,spawnveg,spawnvex
lib	posix_spawnattr_setfchdir,posix_spawnattr_setsid,posix_spawnattr_setumask spawn.h

mem	inheritance.pgroup spawn.h

tst	lib_posix_spawn unistd.h stdlib.h spawn.h -Dfork=______fork note{ posix_spawn exists and it works and its worth using }end status{
	#include <sys/types.h>
	#include <sys/stat.h>
	#include <sys/wait.h>
	#include <spawn.h>
	#include <signal.h>
	#include <fcntl.h>
	#include <string.h>
	/* if it uses fork() why bother? */
	#undef fork
	pid_t fork _ARG_((void)) { NOTE("uses fork()"); return -1; }
	pid_t _fork _ARG_((void)) { NOTE("uses _fork()"); return -1; }
	pid_t __fork _ARG_((void)) { NOTE("uses __fork()"); return -1; }
	int
	main(argc, argv)
	int	argc;
	char**	argv;
	{
		char*			s;
		pid_t			pid;
		posix_spawnattr_t	attr;
		int			n;
		int			status;
		char*			cmd[4];
		char			tmp[1024];
		if (argv[2])
			_exit(signal(SIGHUP, SIG_DFL) != SIG_IGN);
		signal(SIGHUP, SIG_IGN);
		if (posix_spawnattr_init(&attr))
		{
			NOTE("posix_spawnattr_init() FAILED");
			_exit(0);
		}
		if (posix_spawnattr_setpgroup(&attr, 0))
		{
			NOTE("posix_spawnattr_setpgroup() FAILED");
			_exit(0);
		}
		if (posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETPGROUP))
		{
			NOTE("posix_spawnattr_setflags() FAILED");
			_exit(0);
		}
		/* first try an a.out and verify that SIGHUP is ignored */
		cmd[0] = argv[0];
		cmd[1] = argv[1];
		cmd[2] = "test";
		cmd[3] = 0;
		if (posix_spawn(&pid, cmd[0], 0, &attr, cmd, 0))
		{
			NOTE("posix_spawn() FAILED");
			_exit(0);
		}
		status = 1;
		if (wait(&status) < 0)
		{
			NOTE("wait() FAILED");
			_exit(0);
		}
		if (status != 0)
		{
			NOTE("SIGHUP ignored in parent not ignored in child");
			_exit(0);
		}
		/* must return exec-type errors or its useless to us *unless* there is no [v]fork() */
		n = strlen(cmd[0]);
		if (n >= (sizeof(tmp) - 3))
		{
			NOTE("test executable path too long");
			_exit(0);
		}
		strcpy(tmp, cmd[0]);
		tmp[n] = '.';
		tmp[n+1] = 's';
		tmp[n+2] = 'h';
		tmp[n+3] = 0;
		if ((n = open(tmp, O_CREAT|O_WRONLY, S_IRWXU|S_IRWXG|S_IRWXO)) < 0 ||
		    chmod(tmp, S_IRWXU|S_IRWXG|S_IRWXO) < 0 ||
		    write(n, "exit 99\n", 8) != 8 ||
		    close(n) < 0)
		{
			NOTE("test script create FAILED");
			_exit(0);
		}
		cmd[0] = tmp;
		n = 0; /* 0 means reject */
		pid = -1;
		if (posix_spawn(&pid, cmd[0], 0, &attr, cmd, 0))
		{
			n = 2;
			NOTE("ENOEXEC produces posix_spawn() error (BEST)");
		}
		else if (pid == -1)
			NOTE("ENOEXEC returns pid == -1");
		else if (wait(&status) != pid)
			NOTE("ENOEXEC produces no child process");
		else if (!WIFEXITED(status))
			NOTE("ENOEXEC produces signal exit");
		else
		{
			status = WEXITSTATUS(status);
			if (status == 127)
			{
				n = 1;
				NOTE("ENOEXEC produces exit status 127 (GOOD)");
			}
			else if (status == 99)
				NOTE("ENOEXEC invokes sh");
			else if (status == 0)
				NOTE("ENOEXEC reports no error");
			else
				NOTE("ENOEXEC produces non-zero exit status");
		}
		_exit(n);
 	}
}end

tst	lib_spawn_mode unistd.h stdlib.h note{ first spawn arg is mode and it works }end execute{
	#include <signal.h>
	#include <process.h>
	#ifndef P_NOWAIT
	#define P_NOWAIT _P_NOWAIT
	#endif
	int
	main(argc, argv)
	int	argc;
	char**	argv;
	{
		int	status;
		char*	cmd[4];
		if (argv[2])
			_exit(signal(SIGHUP, SIG_DFL) != SIG_IGN);
		signal(SIGHUP, SIG_IGN);
		cmd[0] = argv[0];
		cmd[1] = argv[1];
		cmd[2] = "test";
		cmd[3] = 0;
		if (spawnv(P_NOWAIT, cmd[0], cmd) < 0)
			_exit(1);
		status = 1;
		_exit(wait(&status) < 0 || status != 0);
	}
}end

tst	use_spawn_exec note{ interpreter exec as efficient as fork() }end run{
	exit 1
}end
