hawk/lib/pio.c

2381 lines
58 KiB
C
Raw Normal View History

2019-12-13 04:29:58 +00:00
/*
Copyright (c) 2006-2020 Chung, Hyung-Hwan. All rights reserved.
2019-12-13 04:29:58 +00:00
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <hawk-pio.h>
#include "hawk-prv.h"
#if defined(_WIN32)
# include <windows.h>
# include <tchar.h>
#elif defined(__OS2__)
# define INCL_DOSQUEUES
# define INCL_DOSPROCESS
# define INCL_DOSERRORS
# include <os2.h>
#elif defined(__DOS__)
# include <io.h>
# include <errno.h>
#else
# if !defined(_GNU_SOURCE)
# define _GNU_SOURCE
# endif
2019-12-13 04:29:58 +00:00
# include "syscall.h"
# if defined(HAVE_SPAWN_H)
# include <spawn.h>
# endif
# if defined(HAVE_CRT_EXTERNS_H)
# include <crt_externs.h> /* MacOSX/darwin. _NSGetEnviron() */
# endif
#endif
2019-12-20 14:55:10 +00:00
static hawk_ooi_t pio_input (hawk_tio_t* tio, hawk_tio_cmd_t cmd, void* buf, hawk_oow_t size);
static hawk_ooi_t pio_output (hawk_tio_t* tio, hawk_tio_cmd_t cmd, void* buf, hawk_oow_t size);
2019-12-13 04:29:58 +00:00
2019-12-18 05:15:03 +00:00
#if !defined(_WIN32) && !defined(__OS2__) && !defined(__DOS__)
2019-12-13 04:29:58 +00:00
static int get_highest_fd (hawk_pio_t* pio)
{
#if defined(HAVE_GETRLIMIT)
struct rlimit rlim;
#endif
int fd = -1;
HAWK_DIR* d;
#if defined(F_MAXFD)
fd = HAWK_FCNTL(0, F_MAXFD, 0);
if (fd >= 0) return fd;
#endif
/* will getting the highest file descriptor be faster than
2024-05-02 13:47:30 +00:00
* attempting to close any files descriptors less than the
* system limit? */
2019-12-13 04:29:58 +00:00
d = HAWK_OPENDIR(HAWK_BT("/proc/self/fd"));
if (!d)
{
hawk_bch_t buf[64];
2019-12-18 05:15:03 +00:00
hawk_gem_fmttobcstr (pio->gem, buf, HAWK_COUNTOF(buf), HAWK_BT("/proc/%d/fd"), HAWK_GETPID());
2019-12-13 04:29:58 +00:00
d = HAWK_OPENDIR (buf);
if (!d) d = HAWK_OPENDIR(HAWK_BT("/dev/fd")); /* Darwin, FreeBSD */
}
if (d)
{
int maxfd = -1;
hawk_dirent_t* de;
while ((de = HAWK_READDIR(d)))
{
hawk_int_t l;
const hawk_bch_t* endptr;
if (de->d_name[0] == HAWK_BT('.')) continue;
l = hawk_bchars_to_int(de->d_name, hawk_count_bcstr(de->d_name), HAWK_OOCHARS_TO_INT_MAKE_OPTION(0, 0, 10), &endptr, HAWK_NULL);
if (*endptr == '\0')
2019-12-13 04:29:58 +00:00
{
fd = (int)l;
if ((hawk_intptr_t)fd == l && fd != HAWK_DIRFD(d))
{
if (fd > maxfd) maxfd = fd;
}
}
}
HAWK_CLOSEDIR (d);
return maxfd;
}
/* TODO: should i also use getdtablesize() if available? */
#if defined(HAVE_GETRLIMIT)
if (HAWK_GETRLIMIT(RLIMIT_NOFILE, &rlim) <= -1 ||
2024-05-02 13:47:30 +00:00
rlim.rlim_max == RLIM_INFINITY)
2019-12-13 04:29:58 +00:00
{
#if defined(HAVE_SYSCONF)
fd = sysconf(_SC_OPEN_MAX);
#endif
}
else fd = rlim.rlim_max;
#elif defined(HAVE_SYSCONF)
fd = sysconf(_SC_OPEN_MAX);
#endif
if (fd <= -1) fd = 1024; /* fallback */
2024-05-02 13:47:30 +00:00
/* F_MAXFD is the highest fd. but RLIMIT_NOFILE and
* _SC_OPEN_MAX returnes the maximum number of file
2019-12-13 04:29:58 +00:00
* descriptors. make adjustment */
2024-05-02 13:47:30 +00:00
if (fd > 0) fd--;
2019-12-13 04:29:58 +00:00
return fd;
}
static int close_open_fds_using_proc (hawk_pio_t* pio, int* excepts, hawk_oow_t count)
{
HAWK_DIR* d;
/* will getting the highest file descriptor be faster than
2024-05-02 13:47:30 +00:00
* attempting to close any files descriptors less than the
* system limit? */
2019-12-13 04:29:58 +00:00
d = HAWK_OPENDIR(HAWK_BT("/proc/self/fd"));
if (!d)
{
hawk_bch_t buf[64];
2019-12-18 05:15:03 +00:00
hawk_gem_fmttobcstr (pio->gem, buf, HAWK_COUNTOF(buf), HAWK_BT("/proc/%d/fd"), HAWK_GETPID());
2019-12-13 04:29:58 +00:00
d = HAWK_OPENDIR(buf);
#if !defined(_SCO_DS)
/* on SCO OpenServer, a range of file descriptors starting from 0 are
2024-05-02 13:47:30 +00:00
* listed under /dev/fd regardless of opening state. And some high
2019-12-13 04:29:58 +00:00
* numbered descriptors are not listed. not reliable */
if (!d) d = HAWK_OPENDIR(HAWK_BT("/dev/fd")); /* Darwin, FreeBSD */
#endif
}
if (d)
{
hawk_dirent_t* de;
while ((de = HAWK_READDIR(d)))
{
hawk_int_t l;
const hawk_bch_t* endptr;
if (de->d_name[0] == HAWK_BT('.')) continue;
l = hawk_bchars_to_int(de->d_name, hawk_count_bcstr(de->d_name), HAWK_OOCHARS_TO_INT_MAKE_OPTION(0, 0, 10), &endptr, HAWK_NULL);
if (*endptr == '\0')
2019-12-13 04:29:58 +00:00
{
int fd = (int)l;
if ((hawk_intptr_t)fd == l && fd != HAWK_DIRFD(d) && fd > 2)
{
hawk_oow_t i;
for (i = 0; i < count; i++)
{
if (fd == excepts[i]) goto skip_close;
}
HAWK_CLOSE (fd);
skip_close:
;
}
}
}
HAWK_CLOSEDIR (d);
return 0;
}
return -1;
}
2019-12-18 05:15:03 +00:00
#endif
2019-12-13 04:29:58 +00:00
2019-12-18 05:15:03 +00:00
hawk_pio_t* hawk_pio_open (hawk_gem_t* gem, hawk_oow_t xtnsize, const hawk_ooch_t* cmd, int flags)
2019-12-13 04:29:58 +00:00
{
hawk_pio_t* pio;
2019-12-18 05:15:03 +00:00
pio = (hawk_pio_t*)hawk_gem_allocmem(gem, HAWK_SIZEOF(hawk_pio_t) + xtnsize);
2019-12-13 04:29:58 +00:00
if (pio)
{
2019-12-18 05:15:03 +00:00
if (hawk_pio_init(pio, gem, cmd, flags) <= -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_freemem (gem, pio);
2019-12-13 04:29:58 +00:00
pio = HAWK_NULL;
}
else HAWK_MEMSET (pio + 1, 0, xtnsize);
}
return pio;
}
void hawk_pio_close (hawk_pio_t* pio)
{
hawk_pio_fini (pio);
2019-12-18 05:15:03 +00:00
hawk_gem_freemem (pio->gem, pio);
2019-12-13 04:29:58 +00:00
}
#if !defined(_WIN32) && !defined(__OS2__) && !defined(__DOS__)
struct param_t
{
hawk_bch_t* mcmd;
hawk_bch_t* fixed_argv[4];
hawk_bch_t** argv;
#if defined(HAWK_OOCH_IS_BCH)
/* nothing extra */
#else
hawk_bch_t fixed_mbuf[64];
#endif
};
typedef struct param_t param_t;
static void free_param (hawk_pio_t* pio, param_t* param)
{
2024-05-02 13:47:30 +00:00
if (param->argv && param->argv != param->fixed_argv)
2019-12-18 05:15:03 +00:00
hawk_gem_freemem (pio->gem, param->argv);
if (param->mcmd) hawk_gem_freemem (pio->gem, param->mcmd);
2019-12-13 04:29:58 +00:00
}
static int make_param (hawk_pio_t* pio, const hawk_ooch_t* cmd, int flags, param_t* param)
{
#if defined(HAWK_OOCH_IS_BCH)
hawk_bch_t* mcmd = HAWK_NULL;
#else
hawk_bch_t* mcmd = HAWK_NULL;
hawk_ooch_t* wcmd = HAWK_NULL;
#endif
int fcnt = 0;
HAWK_MEMSET (param, 0, HAWK_SIZEOF(*param));
#if defined(HAWK_OOCH_IS_BCH)
if (flags & HAWK_PIO_SHELL) mcmd = (hawk_ooch_t*)cmd;
else
{
2019-12-18 08:16:34 +00:00
mcmd = hawk_gem_dupoocstr(pio->gem, cmd, HAWK_NULL);
if (mcmd == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
fcnt = hawk_split_oocstr(mcmd, HAWK_T(""), HAWK_T('\"'), HAWK_T('\"'), HAWK_T('\\'));
if (fcnt <= 0)
2019-12-13 04:29:58 +00:00
{
/* no field or an error */
2019-12-18 08:16:34 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2024-05-02 13:47:30 +00:00
goto oops;
2019-12-13 04:29:58 +00:00
}
}
#else
2024-05-02 13:47:30 +00:00
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
/* the cmd is flagged to be of hawk_bch_t
2019-12-13 04:29:58 +00:00
* while the default character type is hawk_uch_t. */
if (flags & HAWK_PIO_SHELL) mcmd = (hawk_bch_t*)cmd;
else
{
2019-12-18 05:15:03 +00:00
mcmd = hawk_gem_dupbcstr(pio->gem, (const hawk_bch_t*)cmd, HAWK_NULL);
if (mcmd == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
fcnt = hawk_split_bcstr(mcmd, "", '\"', '\"', '\\');
if (fcnt <= 0)
2019-12-13 04:29:58 +00:00
{
/* no field or an error */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2024-05-02 13:47:30 +00:00
goto oops;
2019-12-13 04:29:58 +00:00
}
}
}
else
{
hawk_oow_t n, mn, wl;
if (flags & HAWK_PIO_SHELL)
{
2019-12-18 05:15:03 +00:00
/* use the cmgr of the gem, not pio->cmgr */
if (hawk_conv_ucstr_to_bcstr_with_cmgr(cmd, &wl, HAWK_NULL, &mn, pio->gem->cmgr) <= -1)
2019-12-13 04:29:58 +00:00
{
/* cmd has illegal sequence */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
}
else
{
2019-12-18 05:15:03 +00:00
wcmd = hawk_gem_dupoocstr(pio->gem, cmd, HAWK_NULL);
if (wcmd == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
fcnt = hawk_split_oocstr(wcmd, HAWK_T(""), HAWK_T('\"'), HAWK_T('\"'), HAWK_T('\\'));
2019-12-13 04:29:58 +00:00
if (fcnt <= 0)
{
/* no field or an error */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
2019-12-13 04:29:58 +00:00
/* calculate the length of the string after splitting */
for (wl = 0, n = fcnt; n > 0; )
{
if (wcmd[wl++] == HAWK_T('\0')) n--;
}
2024-05-02 13:47:30 +00:00
if (hawk_conv_uchars_to_bchars_with_cmgr(wcmd, &wl, HAWK_NULL, &mn, pio->gem->cmgr) <= -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
}
/* prepare to reserve 1 more slot for the terminating '\0'
* by incrementing mn by 1. */
mn = mn + 1;
if (mn <= HAWK_COUNTOF(param->fixed_mbuf))
{
mcmd = param->fixed_mbuf;
mn = HAWK_COUNTOF(param->fixed_mbuf);
}
else
{
2019-12-18 05:15:03 +00:00
mcmd = hawk_gem_allocmem(pio->gem, mn * HAWK_SIZEOF(*mcmd));
if (mcmd == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
}
if (flags & HAWK_PIO_SHELL)
{
2019-12-21 16:59:00 +00:00
/*HAWK_ASSERT (wcmd == HAWK_NULL);*/
2024-05-02 13:47:30 +00:00
/* hawk_wcstombs() should succeed as
2019-12-13 04:29:58 +00:00
* it was successful above */
2019-12-18 05:15:03 +00:00
hawk_conv_ucstr_to_bcstr_with_cmgr (cmd, &wl, mcmd, &mn, pio->gem->cmgr);
2019-12-13 04:29:58 +00:00
/* hawk_wcstombs() null-terminate mcmd */
}
else
{
2019-12-21 16:59:00 +00:00
HAWK_ASSERT (wcmd != HAWK_NULL);
2024-05-02 13:47:30 +00:00
/* hawk_wcsntombsn() should succeed as
2019-12-13 04:29:58 +00:00
* it was was successful above */
2019-12-18 05:15:03 +00:00
hawk_conv_uchars_to_bchars_with_cmgr (wcmd, &wl, mcmd, &mn, pio->gem->cmgr);
2019-12-13 04:29:58 +00:00
/* hawk_wcsntombsn() doesn't null-terminate mcmd */
2019-12-18 05:15:03 +00:00
mcmd[mn] = '\0';
2019-12-13 04:29:58 +00:00
2019-12-18 05:15:03 +00:00
hawk_gem_freemem (pio->gem, wcmd);
2019-12-13 04:29:58 +00:00
wcmd = HAWK_NULL;
}
}
#endif
if (flags & HAWK_PIO_SHELL)
{
param->argv = param->fixed_argv;
param->argv[0] = HAWK_BT("/bin/sh");
param->argv[1] = HAWK_BT("-c");
param->argv[2] = mcmd;
param->argv[3] = HAWK_NULL;
}
else
{
int i;
hawk_bch_t** argv;
hawk_bch_t* mcmdptr;
if (fcnt < HAWK_COUNTOF(param->fixed_argv))
{
param->argv = param->fixed_argv;
}
else
{
2019-12-18 05:15:03 +00:00
param->argv = hawk_gem_allocmem(pio->gem, (fcnt + 1) * HAWK_SIZEOF(argv[0]));
if (param->argv == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
}
mcmdptr = mcmd;
for (i = 0; i < fcnt; i++)
{
param->argv[i] = mcmdptr;
while (*mcmdptr != HAWK_BT('\0')) mcmdptr++;
mcmdptr++;
}
param->argv[i] = HAWK_NULL;
}
#if defined(HAWK_OOCH_IS_BCH)
if (mcmd && mcmd != (hawk_bch_t*)cmd) param->mcmd = mcmd;
#else
2024-05-02 13:47:30 +00:00
if (mcmd && mcmd != (hawk_bch_t*)cmd &&
2019-12-13 04:29:58 +00:00
mcmd != param->fixed_mbuf) param->mcmd = mcmd;
#endif
return 0;
oops:
#if defined(HAWK_OOCH_IS_BCH)
2019-12-18 05:15:03 +00:00
if (mcmd && mcmd != cmd) hawk_gem_freemem (pio->gem, mcmd);
2019-12-13 04:29:58 +00:00
#else
2024-05-02 13:47:30 +00:00
if (mcmd && mcmd != (hawk_bch_t*)cmd &&
2019-12-18 05:15:03 +00:00
mcmd != param->fixed_mbuf) hawk_gem_freemem (pio->gem, mcmd);
if (wcmd) hawk_gem_freemem (pio->gem, wcmd);
2019-12-13 04:29:58 +00:00
#endif
return -1;
}
static int assert_executable (hawk_pio_t* pio, const hawk_bch_t* path)
{
hawk_lstat_t st;
2024-05-02 13:47:30 +00:00
if (HAWK_ACCESS(path, X_OK) <= -1)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return -1;
}
/*if (HAWK_LSTAT(path, &st) <= -1)*/
if (HAWK_STAT(path, &st) <= -1)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return -1;
}
2024-05-02 13:47:30 +00:00
if (!S_ISREG(st.st_mode))
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EACCES);
2019-12-13 04:29:58 +00:00
return -1;
}
return 0;
}
static HAWK_INLINE int is_fd_valid (int fd)
{
return HAWK_FCNTL (fd, F_GETFD, 0) != -1 || errno != EBADF;
}
static HAWK_INLINE int is_fd_valid_and_nocloexec (int fd)
{
int flags = HAWK_FCNTL (fd, F_GETFD, 0);
if (flags == -1)
{
if (errno == EBADF) return 0; /* invalid. return false */
return -1; /* unknown. true but negative to indicate unknown */
}
return !(flags & FD_CLOEXEC)? 1: 0;
}
static hawk_pio_pid_t standard_fork_and_exec (hawk_pio_t* pio, int pipes[], param_t* param)
{
hawk_pio_pid_t pid;
#if defined(HAVE_CRT_EXTERNS_H)
# define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
pid = HAWK_FORK();
2024-05-02 13:47:30 +00:00
if (pid <= -1)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return -1;
}
if (pid == 0)
{
/* child */
hawk_pio_hnd_t devnull = -1;
if (!(pio->flags & HAWK_PIO_NOCLOEXEC))
{
if (close_open_fds_using_proc(pio, pipes, 6) <= -1)
{
int fd = get_highest_fd(pio);
2024-05-02 13:47:30 +00:00
/* close all other unknown open handles except
2019-12-13 04:29:58 +00:00
* stdin/out/err and the pipes. */
while (fd > 2)
{
if (fd != pipes[0] && fd != pipes[1] &&
fd != pipes[2] && fd != pipes[3] &&
2024-05-02 13:47:30 +00:00
fd != pipes[4] && fd != pipes[5])
2019-12-13 04:29:58 +00:00
{
HAWK_CLOSE (fd);
}
fd--;
}
}
}
if (pio->flags & HAWK_PIO_WRITEIN)
{
/* child should read */
HAWK_CLOSE (pipes[1]);
pipes[1] = HAWK_PIO_HND_NIL;
if (HAWK_DUP2 (pipes[0], 0) <= -1) goto child_oops;
HAWK_CLOSE (pipes[0]);
pipes[0] = HAWK_PIO_HND_NIL;
}
if (pio->flags & HAWK_PIO_READOUT)
{
/* child should write */
HAWK_CLOSE (pipes[2]);
pipes[2] = HAWK_PIO_HND_NIL;
if (HAWK_DUP2 (pipes[3], 1) <= -1) goto child_oops;
if (pio->flags & HAWK_PIO_ERRTOOUT)
{
if (HAWK_DUP2 (pipes[3], 2) <= -1) goto child_oops;
}
2024-05-02 13:47:30 +00:00
HAWK_CLOSE (pipes[3]);
2019-12-13 04:29:58 +00:00
pipes[3] = HAWK_PIO_HND_NIL;
}
if (pio->flags & HAWK_PIO_READERR)
{
/* child should write */
2024-05-02 13:47:30 +00:00
HAWK_CLOSE (pipes[4]);
2019-12-13 04:29:58 +00:00
pipes[4] = HAWK_PIO_HND_NIL;
if (HAWK_DUP2 (pipes[5], 2) <= -1) goto child_oops;
if (pio->flags & HAWK_PIO_OUTTOERR)
{
if (HAWK_DUP2 (pipes[5], 1) <= -1) goto child_oops;
}
HAWK_CLOSE (pipes[5]);
pipes[5] = HAWK_PIO_HND_NIL;
}
2024-05-02 13:47:30 +00:00
if ((pio->flags & HAWK_PIO_INTONUL) ||
2019-12-13 04:29:58 +00:00
(pio->flags & HAWK_PIO_OUTTONUL) ||
(pio->flags & HAWK_PIO_ERRTONUL))
{
#if defined(O_LARGEFILE)
devnull = HAWK_OPEN (HAWK_BT("/dev/null"), O_RDWR|O_LARGEFILE, 0);
#else
devnull = HAWK_OPEN (HAWK_BT("/dev/null"), O_RDWR, 0);
#endif
if (devnull <= -1) goto child_oops;
}
if ((pio->flags & HAWK_PIO_INTONUL) &&
HAWK_DUP2(devnull,0) <= -1) goto child_oops;
if ((pio->flags & HAWK_PIO_OUTTONUL) &&
HAWK_DUP2(devnull,1) <= -1) goto child_oops;
if ((pio->flags & HAWK_PIO_ERRTONUL) &&
HAWK_DUP2(devnull,2) <= -1) goto child_oops;
2024-05-02 13:47:30 +00:00
if ((pio->flags & HAWK_PIO_INTONUL) ||
2019-12-13 04:29:58 +00:00
(pio->flags & HAWK_PIO_OUTTONUL) ||
2024-05-02 13:47:30 +00:00
(pio->flags & HAWK_PIO_ERRTONUL))
2019-12-13 04:29:58 +00:00
{
HAWK_CLOSE (devnull);
devnull = -1;
}
if (pio->flags & HAWK_PIO_DROPIN) HAWK_CLOSE(0);
if (pio->flags & HAWK_PIO_DROPOUT) HAWK_CLOSE(1);
if (pio->flags & HAWK_PIO_DROPERR) HAWK_CLOSE(2);
if (pio->flags & HAWK_PIO_FNCCMD)
{
/* -----------------------------------------------
* the function pointer to execute has been given.
* -----------------------------------------------*/
hawk_pio_fnc_t* fnc = (hawk_pio_fnc_t*)param;
int retx;
retx = fnc->ptr(fnc->ctx);
if (devnull >= 0) HAWK_CLOSE (devnull);
HAWK_EXIT (retx);
}
else
{
HAWK_EXECVE (param->argv[0], param->argv, environ);
/* if exec fails, free 'param' parameter which is an inherited pointer */
2024-05-02 13:47:30 +00:00
free_param (pio, param);
2019-12-13 04:29:58 +00:00
}
child_oops:
if (devnull >= 0) HAWK_CLOSE (devnull);
HAWK_EXIT (128);
}
return pid;
}
#endif
static int set_pipe_nonblock (hawk_pio_t* pio, hawk_pio_hnd_t fd, int enabled)
{
#if defined(O_NONBLOCK)
int flag = HAWK_FCNTL (fd, F_GETFL, 0);
2024-05-05 05:13:46 +00:00
if (flag >= 0) flag = HAWK_FCNTL(fd, F_SETFL, (enabled? (flag | O_NONBLOCK): (flag & ~O_NONBLOCK)));
if (flag <= -1) hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return flag;
#else
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOIMPL);
2019-12-13 04:29:58 +00:00
return -1;
#endif
}
2019-12-18 05:15:03 +00:00
int hawk_pio_init (hawk_pio_t* pio, hawk_gem_t* gem, const hawk_ooch_t* cmd, int flags)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
hawk_pio_hnd_t handle[6] /*=
{
HAWK_PIO_HND_NIL,
2019-12-13 04:29:58 +00:00
HAWK_PIO_HND_NIL,
HAWK_PIO_HND_NIL,
HAWK_PIO_HND_NIL,
HAWK_PIO_HND_NIL,
HAWK_PIO_HND_NIL
}*/;
2024-05-02 13:47:30 +00:00
hawk_tio_t* tio[3] /*=
{
HAWK_NULL,
HAWK_NULL,
HAWK_NULL
2019-12-13 04:29:58 +00:00
}*/;
int i, minidx = -1, maxidx = -1;
#if defined(_WIN32)
2024-05-02 13:47:30 +00:00
SECURITY_ATTRIBUTES secattr;
2019-12-13 04:29:58 +00:00
PROCESS_INFORMATION procinfo;
STARTUPINFO startup;
HANDLE windevnul = INVALID_HANDLE_VALUE;
BOOL apiret;
hawk_ooch_t* dupcmd;
int create_retried;
#elif defined(__OS2__)
APIRET rc;
ULONG pipe_size = 4096;
UCHAR load_error[CCHMAXPATH];
RESULTCODES child_rc;
HFILE old_in = HAWK_PIO_HND_NIL;
HFILE old_out = HAWK_PIO_HND_NIL;
HFILE old_err = HAWK_PIO_HND_NIL;
HFILE std_in = 0, std_out = 1, std_err = 2;
hawk_bch_t* cmd_line = HAWK_NULL;
hawk_bch_t* cmd_file;
HFILE os2devnul = (HFILE)-1;
#elif defined(__DOS__)
/* DOS not multi-processed. can't support pio */
2024-05-02 13:47:30 +00:00
#elif defined(HAVE_POSIX_SPAWN) && !(defined(HAWK_SYSCALL0) && defined(SYS_vfork))
2019-12-13 04:29:58 +00:00
posix_spawn_file_actions_t fa;
int fa_inited = 0;
int pserr;
posix_spawnattr_t psattr;
hawk_pio_pid_t pid;
param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
#elif defined(HAWK_SYSCALL0) && defined(SYS_vfork)
hawk_pio_pid_t pid;
param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
int highest_fd;
int dummy;
#else
hawk_pio_pid_t pid;
param_t param;
#if defined(HAVE_CRT_EXTERNS_H)
#define environ (*(_NSGetEnviron()))
#else
extern char** environ;
#endif
#endif
HAWK_MEMSET (pio, 0, HAWK_SIZEOF(*pio));
2019-12-18 05:15:03 +00:00
pio->gem = gem;
2019-12-13 04:29:58 +00:00
pio->flags = flags;
handle[0] = HAWK_PIO_HND_NIL;
handle[1] = HAWK_PIO_HND_NIL;
handle[2] = HAWK_PIO_HND_NIL;
handle[3] = HAWK_PIO_HND_NIL;
handle[4] = HAWK_PIO_HND_NIL;
handle[5] = HAWK_PIO_HND_NIL;
tio[0] = HAWK_NULL;
tio[1] = HAWK_NULL;
tio[2] = HAWK_NULL;
#if defined(_WIN32)
/* http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx */
secattr.nLength = HAWK_SIZEOF(secattr);
secattr.bInheritHandle = TRUE;
secattr.lpSecurityDescriptor = HAWK_NULL;
if (flags & HAWK_PIO_WRITEIN)
{
/* child reads, parent writes */
2024-05-02 13:47:30 +00:00
if (CreatePipe(&handle[0], &handle[1], &secattr, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
goto oops;
}
/* don't inherit write handle */
2024-05-02 13:47:30 +00:00
if (SetHandleInformation(handle[1], HANDLE_FLAG_INHERIT, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
DWORD e = GetLastError();
if (e != ERROR_CALL_NOT_IMPLEMENTED)
{
/* SetHandleInformation() is not implemented on win9x.
* so let's care only if it is implemented */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(e));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
minidx = 0; maxidx = 1;
}
if (flags & HAWK_PIO_READOUT)
{
/* child writes, parent reads */
2024-05-02 13:47:30 +00:00
if (CreatePipe(&handle[2], &handle[3], &secattr, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
goto oops;
}
/* don't inherit read handle */
2024-05-02 13:47:30 +00:00
if (SetHandleInformation(handle[2], HANDLE_FLAG_INHERIT, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
DWORD e = GetLastError();
if (e != ERROR_CALL_NOT_IMPLEMENTED)
{
/* SetHandleInformation() is not implemented on win9x.
* so let's care only if it is implemented */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(e));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
if (minidx == -1) minidx = 2;
maxidx = 3;
}
if (flags & HAWK_PIO_READERR)
{
/* child writes, parent reads */
2019-12-18 05:15:03 +00:00
if (CreatePipe(&handle[4], &handle[5], &secattr, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
goto oops;
}
/* don't inherit read handle */
2019-12-18 05:15:03 +00:00
if (SetHandleInformation(handle[4], HANDLE_FLAG_INHERIT, 0) == FALSE)
2019-12-13 04:29:58 +00:00
{
DWORD e = GetLastError();
if (e != ERROR_CALL_NOT_IMPLEMENTED)
{
/* SetHandleInformation() is not implemented on win9x.
* so let's care only if it is implemented */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(e));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
if (minidx == -1) minidx = 4;
maxidx = 5;
}
2024-05-02 13:47:30 +00:00
if (maxidx == -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
if ((flags & HAWK_PIO_INTONUL) ||
2019-12-13 04:29:58 +00:00
(flags & HAWK_PIO_OUTTONUL) ||
(flags & HAWK_PIO_ERRTONUL))
{
windevnul = CreateFile(
HAWK_T("NUL"), GENERIC_READ | GENERIC_WRITE,
2024-05-02 13:47:30 +00:00
FILE_SHARE_READ | FILE_SHARE_WRITE,
2019-12-13 04:29:58 +00:00
&secattr, OPEN_EXISTING, 0, NULL
);
2024-05-02 13:47:30 +00:00
if (windevnul == INVALID_HANDLE_VALUE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
HAWK_MEMSET (&procinfo, 0, HAWK_SIZEOF(procinfo));
HAWK_MEMSET (&startup, 0, HAWK_SIZEOF(startup));
startup.cb = HAWK_SIZEOF(startup);
/*
startup.hStdInput = INVALID_HANDLE_VALUE;
startup.hStdOutput = INVALID_HANDLE_VALUE;
startup.hStdOutput = INVALID_HANDLE_VALUE;
*/
2019-12-18 05:15:03 +00:00
startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
startup.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
startup.hStdOutput = GetStdHandle(STD_ERROR_HANDLE);
2019-12-13 04:29:58 +00:00
if (startup.hStdInput == INVALID_HANDLE_VALUE ||
startup.hStdOutput == INVALID_HANDLE_VALUE ||
2024-05-02 13:47:30 +00:00
startup.hStdError == INVALID_HANDLE_VALUE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
if (flags & HAWK_PIO_WRITEIN)
2019-12-13 04:29:58 +00:00
{
startup.hStdInput = handle[0];
}
if (flags & HAWK_PIO_READOUT)
{
startup.hStdOutput = handle[3];
if (flags & HAWK_PIO_ERRTOOUT) startup.hStdError = handle[3];
}
if (flags & HAWK_PIO_READERR)
{
startup.hStdError = handle[5];
if (flags & HAWK_PIO_OUTTOERR) startup.hStdOutput = handle[5];
}
if (flags & HAWK_PIO_INTONUL) startup.hStdInput = windevnul;
if (flags & HAWK_PIO_OUTTONUL) startup.hStdOutput = windevnul;
if (flags & HAWK_PIO_ERRTONUL) startup.hStdError = windevnul;
if (flags & HAWK_PIO_DROPIN) startup.hStdInput = INVALID_HANDLE_VALUE;
if (flags & HAWK_PIO_DROPOUT) startup.hStdOutput = INVALID_HANDLE_VALUE;
if (flags & HAWK_PIO_DROPERR) startup.hStdError = INVALID_HANDLE_VALUE;
startup.dwFlags |= STARTF_USESTDHANDLES;
/* there is nothing to do for HAWK_PIO_SHELL as CreateProcess
* takes the entire command line */
create_retried = 0;
2019-12-18 05:15:03 +00:00
create_process:
2024-05-02 13:47:30 +00:00
if (flags & HAWK_PIO_SHELL)
2019-12-13 04:29:58 +00:00
{
static const hawk_ooch_t* cmdname[] =
{
HAWK_T("cmd.exe /c "),
HAWK_T("command.com /c ")
};
static const hawk_bch_t* mcmdname[] =
{
HAWK_BT("cmd.exe /c "),
HAWK_BT("command.com /c ")
};
#if defined(HAWK_OOCH_IS_UCH)
2019-12-18 08:16:34 +00:00
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
const hawk_bch_t* x[3];
x[0] = mcmdname[create_retried];
x[1] = (const hawk_bch_t*)cmd;
x[2] = HAWK_NULL;
2019-12-18 08:16:34 +00:00
dupcmd = hawk_gem_dupbcstrarrtoucstr(pio->gem, x, HAWK_NULL, 0);
2019-12-13 04:29:58 +00:00
}
else
#endif
2019-12-18 08:16:34 +00:00
{
const hawk_ooch_t* x[3];
x[0] = cmdname[create_retried];
x[1] = cmd;
x[2] = HAWK_NULL;
dupcmd = hawk_gem_dupoocstrarr(pio->gem, x, HAWK_NULL);
2019-12-13 04:29:58 +00:00
}
}
2024-05-02 13:47:30 +00:00
else
2019-12-13 04:29:58 +00:00
{
#if defined(HAWK_OOCH_IS_UCH)
2019-12-18 08:16:34 +00:00
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
2019-12-18 08:16:34 +00:00
dupcmd = hawk_gem_dupbtoucstr(pio->gem, (const hawk_bch_t*)cmd, HAWK_NULL, 0);
2019-12-13 04:29:58 +00:00
}
else
{
#endif
/* CreateProcess requires command buffer to be read-write. */
2019-12-18 05:15:03 +00:00
dupcmd = hawk_gem_dupoocstr(pio->gem, cmd, HAWK_NULL);
2019-12-13 04:29:58 +00:00
#if defined(HAWK_OOCH_IS_UCH)
}
#endif
}
2024-05-05 05:13:46 +00:00
if (HAWK_UNLIKELY(!dupcmd)) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-05 05:13:46 +00:00
apiret = CreateProcess(
2019-12-13 04:29:58 +00:00
HAWK_NULL, /* LPCTSTR lpApplicationName */
dupcmd, /* LPTSTR lpCommandLine */
HAWK_NULL, /* LPSECURITY_ATTRIBUTES lpProcessAttributes */
HAWK_NULL, /* LPSECURITY_ATTRIBUTES lpThreadAttributes */
TRUE, /* BOOL bInheritHandles */
#if defined(HAWK_OOCH_IS_BCH)
0, /* DWORD dwCreationFlags */
#else
CREATE_UNICODE_ENVIRONMENT, /* DWORD dwCreationFlags */
#endif
HAWK_NULL, /* LPVOID lpEnvironment */
HAWK_NULL, /* LPCTSTR lpCurrentDirectory */
&startup, /* LPSTARTUPINFO lpStartupInfo */
&procinfo /* LPPROCESS_INFORMATION lpProcessInformation */
);
2024-05-02 13:47:30 +00:00
hawk_gem_freemem (pio->gem, dupcmd);
if (apiret == FALSE)
2019-12-13 04:29:58 +00:00
{
DWORD e = GetLastError();
2024-05-02 13:47:30 +00:00
if (create_retried == 0 && (flags & HAWK_PIO_SHELL) &&
2019-12-13 04:29:58 +00:00
e == ERROR_FILE_NOT_FOUND)
{
2024-05-02 13:47:30 +00:00
/* if it failed to exeucte cmd.exe,
2019-12-13 04:29:58 +00:00
* attempt to execute command.com.
* this is provision for old windows platforms */
create_retried = 1;
goto create_process;
}
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(e));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (windevnul != INVALID_HANDLE_VALUE)
{
2024-05-02 13:47:30 +00:00
CloseHandle (windevnul);
2019-12-13 04:29:58 +00:00
windevnul = INVALID_HANDLE_VALUE;
}
if (flags & HAWK_PIO_WRITEIN)
{
CloseHandle (handle[0]);
handle[0] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READOUT)
{
CloseHandle (handle[3]);
handle[3] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READERR)
{
CloseHandle (handle[5]);
handle[5] = HAWK_PIO_HND_NIL;
}
CloseHandle (procinfo.hThread);
pio->child = procinfo.hProcess;
#elif defined(__OS2__)
#define DOS_DUP_HANDLE(x,y) HAWK_BLOCK ( \
rc = DosDupHandle(x,y); \
if (rc != NO_ERROR) \
{ \
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc)); \
2019-12-13 04:29:58 +00:00
goto oops; \
} \
)
if (flags & HAWK_PIO_WRITEIN)
{
2020-01-10 15:30:57 +00:00
/* child reads, parent writes */
2024-05-05 05:13:46 +00:00
rc = DosCreatePipe(&handle[0], &handle[1], pipe_size);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
/* the parent writes to handle[1] and the child reads from
2019-12-13 04:29:58 +00:00
* handle[0] inherited. set the flag not to inherit handle[1]. */
2024-05-05 05:13:46 +00:00
rc = DosSetFHState(handle[1], OPEN_FLAGS_NOINHERIT);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
/* Need to do somthing like this to set the flag instead?
ULONG state;
2019-12-13 04:29:58 +00:00
DosQueryFHState (handle[1], &state);
DosSetFHState (handle[1], state | OPEN_FLAGS_NOINHERIT); */
minidx = 0; maxidx = 1;
}
if (flags & HAWK_PIO_READOUT)
{
/* child writes, parent reads */
2024-05-05 05:13:46 +00:00
rc = DosCreatePipe(&handle[2], &handle[3], pipe_size);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
/* the parent reads from handle[2] and the child writes to
2019-12-13 04:29:58 +00:00
* handle[3] inherited. set the flag not to inherit handle[2] */
2019-12-18 05:15:03 +00:00
rc = DosSetFHState(handle[2], OPEN_FLAGS_NOINHERIT);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (minidx == -1) minidx = 2;
maxidx = 3;
}
if (flags & HAWK_PIO_READERR)
{
/* child writes, parent reads */
2019-12-18 05:15:03 +00:00
rc = DosCreatePipe(&handle[4], &handle[5], pipe_size);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
/* the parent reads from handle[4] and the child writes to
2019-12-13 04:29:58 +00:00
* handle[5] inherited. set the flag not to inherit handle[4] */
2024-05-05 05:13:46 +00:00
rc = DosSetFHState(handle[4], OPEN_FLAGS_NOINHERIT);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (minidx == -1) minidx = 4;
maxidx = 5;
}
2024-05-02 13:47:30 +00:00
if (maxidx == -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 08:16:34 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
if ((flags & HAWK_PIO_INTONUL) ||
2019-12-13 04:29:58 +00:00
(flags & HAWK_PIO_OUTTONUL) ||
(flags & HAWK_PIO_ERRTONUL))
{
ULONG action_taken;
/*
LONGLONG zero;
zero.ulLo = 0;
zero.ulHi = 0;
*/
/* TODO: selective between DosOpenL and DosOpen */
2020-01-10 15:30:57 +00:00
rc = DosOpen/*DosOpenL*/(
2019-12-13 04:29:58 +00:00
HAWK_BT("NUL"),
&os2devnul,
&action_taken,
0, /*zero,*/
2020-01-10 15:30:57 +00:00
FILE_NORMAL,
2019-12-13 04:29:58 +00:00
OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW,
OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE,
0L
);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
/* duplicate the current stdin/out/err to old_in/out/err as a new handle */
2024-05-02 13:47:30 +00:00
2020-01-10 15:30:57 +00:00
rc = DosDupHandle(std_in, &old_in);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
2020-01-10 15:30:57 +00:00
rc = DosDupHandle(std_out, &old_out);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
DosClose (old_in); old_in = HAWK_PIO_HND_NIL;
goto oops;
}
2020-01-10 15:30:57 +00:00
rc = DosDupHandle(std_err, &old_err);
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
DosClose (old_out); old_out = HAWK_PIO_HND_NIL;
DosClose (old_in); old_in = HAWK_PIO_HND_NIL;
goto oops;
}
2024-05-02 13:47:30 +00:00
/* we must not let our own stdin/out/err duplicated
2019-12-13 04:29:58 +00:00
* into old_in/out/err be inherited */
DosSetFHState (old_in, OPEN_FLAGS_NOINHERIT);
2024-05-02 13:47:30 +00:00
DosSetFHState (old_out, OPEN_FLAGS_NOINHERIT);
2019-12-13 04:29:58 +00:00
DosSetFHState (old_err, OPEN_FLAGS_NOINHERIT);
if (flags & HAWK_PIO_WRITEIN)
{
/* the child reads from handle[0] inherited and expects it to
* be stdin(0). so we duplicate handle[0] to stdin */
DOS_DUP_HANDLE (handle[0], &std_in);
/* the parent writes to handle[1] but does not read from handle[0].
* so we close it */
DosClose (handle[0]); handle[0] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READOUT)
{
/* the child writes to handle[3] inherited and expects it to
* be stdout(1). so we duplicate handle[3] to stdout. */
DOS_DUP_HANDLE (handle[3], &std_out);
if (flags & HAWK_PIO_ERRTOOUT) DOS_DUP_HANDLE (handle[3], &std_err);
/* the parent reads from handle[2] but does not write to handle[3].
* so we close it */
DosClose (handle[3]); handle[3] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READERR)
{
DOS_DUP_HANDLE (handle[5], &std_err);
if (flags & HAWK_PIO_OUTTOERR) DOS_DUP_HANDLE (handle[5], &std_out);
DosClose (handle[5]); handle[5] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_INTONUL) DOS_DUP_HANDLE (os2devnul, &std_in);
if (flags & HAWK_PIO_OUTTONUL) DOS_DUP_HANDLE (os2devnul, &std_out);
if (flags & HAWK_PIO_ERRTONUL) DOS_DUP_HANDLE (os2devnul, &std_err);
if (os2devnul != HAWK_PIO_HND_NIL)
{
/* close NUL early as we've duplicated it already */
2024-05-02 13:47:30 +00:00
DosClose (os2devnul);
2019-12-13 04:29:58 +00:00
os2devnul = HAWK_PIO_HND_NIL;
}
2024-05-02 13:47:30 +00:00
2019-12-13 04:29:58 +00:00
/* at this moment, stdin/out/err are already redirected to pipes
2024-05-02 13:47:30 +00:00
* if proper flags have been set. we close them selectively if
2019-12-13 04:29:58 +00:00
* dropping is requested */
if (flags & HAWK_PIO_DROPIN) DosClose (std_in);
if (flags & HAWK_PIO_DROPOUT) DosClose (std_out);
if (flags & HAWK_PIO_DROPERR) DosClose (std_err);
2024-05-02 13:47:30 +00:00
if (flags & HAWK_PIO_SHELL)
2019-12-13 04:29:58 +00:00
{
hawk_oow_t n, mn;
#if defined(HAWK_OOCH_IS_BCH)
2019-12-18 08:16:34 +00:00
mn = hawk_count_oocstr(cmd);
2019-12-13 04:29:58 +00:00
#else
2019-12-18 08:16:34 +00:00
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
2019-12-18 08:16:34 +00:00
mn = hawk_count_bcstr((const hawk_bch_t*)cmd);
2019-12-13 04:29:58 +00:00
}
else
{
2019-12-18 08:16:34 +00:00
if (hawk_gem_convutobcstr(pio->gem, cmd, &n, HAWK_NULL, &mn) <= -1) goto oops; /* illegal sequence found */
2019-12-13 04:29:58 +00:00
}
#endif
2019-12-18 08:16:34 +00:00
cmd_line = hawk_gem_allocmem(pio->gem, ((11+mn+1+1) * HAWK_SIZEOF(*cmd_line)));
if (cmd_line == HAWK_NULL) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
hawk_copy_bcstr_unlimited (cmd_line, "cmd.exe"); /* cmd.exe\0/c */
2019-12-18 08:16:34 +00:00
hawk_copy_bcstr_unlimited (&cmd_line[8], "/c ");
2019-12-13 04:29:58 +00:00
#if defined(HAWK_OOCH_IS_BCH)
2019-12-18 08:16:34 +00:00
hawk_copy_bcstr_unlimited (&cmd_line[11], cmd);
2019-12-13 04:29:58 +00:00
#else
2019-12-18 08:16:34 +00:00
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
2019-12-18 08:16:34 +00:00
hawk_copy_bcstr_unlimited (&cmd_line[11], (const hawk_bch_t*)cmd);
2019-12-13 04:29:58 +00:00
}
else
{
mn = mn + 1; /* update the buffer size */
2019-12-18 08:16:34 +00:00
hawk_gem_convutobcstr (pio->gem, cmd, &n, &cmd_line[11], &mn);
2019-12-13 04:29:58 +00:00
}
#endif
2024-05-02 13:47:30 +00:00
cmd_line[11+mn+1] = '\0'; /* additional \0 after \0 */
2019-12-18 08:16:34 +00:00
cmd_file = "cmd.exe";
2019-12-13 04:29:58 +00:00
}
else
{
hawk_bch_t* mptr;
hawk_oow_t mn;
#if defined(HAWK_OOCH_IS_BCH)
2019-12-18 08:16:34 +00:00
const hawk_ooch_t* strarr[3];
strarr[0] = cmd;
strarr[1] = HAWK_T(" ");
strarr[2] = HAWK_NULL;
2019-12-18 05:15:03 +00:00
mn = hawk_count_oocstr(cmd);
2019-12-18 08:16:34 +00:00
cmd_line = hawk_dupucstrarr(pio->gem, strarr HAWK_NULL);
2024-05-05 05:13:46 +00:00
if (HAWK_UNLIKELY(!cmd_line)) goto oops;
2019-12-18 08:16:34 +00:00
#else
if (flags & HAWK_PIO_BCSTRCMD)
2019-12-13 04:29:58 +00:00
{
2019-12-18 08:16:34 +00:00
const hawk_bch_t* mbsarr[3];
mbsarr[0] = (const hawk_bch_t*)cmd;
mbsarr[1] = " ";
mbsarr[2] = HAWK_NULL;
2019-12-18 05:15:03 +00:00
mn = hawk_count_bcstr((const hawk_bch_t*)cmd);
2019-12-18 08:16:34 +00:00
cmd_line = hawk_dupbcstrarr(pio->gem, mbsarr, HAWK_NULL);
2024-05-05 05:13:46 +00:00
if (HAWK_UNLIKELY(!cmd_line)) goto oops;
2019-12-13 04:29:58 +00:00
}
else
{
2019-12-18 05:15:03 +00:00
cmd_line = hawk_gem_duputobcstr(gem, cmd, &mn);
2024-05-05 05:13:46 +00:00
if (HAWK_UNLIKELY(!cmd_line)) goto oops; /* illegal sequence in cmd or out of memory */
2019-12-13 04:29:58 +00:00
}
#endif
/* TODO: enhance this part by:
* supporting file names containing whitespaces.
* detecting the end of the file name better.
* doing better parsing of the command line.
*/
2024-05-02 13:47:30 +00:00
/* NOTE: you must separate the command name and the parameters
2019-12-13 04:29:58 +00:00
* with a space. "pstat.exe /c" is ok while "pstat.exe/c"
* is not. */
2019-12-18 05:15:03 +00:00
mptr = hawk_mbspbrk(cmd_line, HAWK_BT(" \t"));
if (mptr) *mptr = '\0';
cmd_line[mn+1] = '\0'; /* the second '\0' at the end */
2019-12-13 04:29:58 +00:00
cmd_file = cmd_line;
}
/* execute the command line */
2024-05-05 05:13:46 +00:00
rc = DosExecPgm(
2019-12-13 04:29:58 +00:00
&load_error,
2024-05-02 13:47:30 +00:00
HAWK_SIZEOF(load_error),
2019-12-13 04:29:58 +00:00
EXEC_ASYNCRESULT,
cmd_line,
HAWK_NULL,
&child_rc,
cmd_file
);
2019-12-18 05:15:03 +00:00
hawk_gem_freemem (pio->gem, cmd_line);
2019-12-13 04:29:58 +00:00
cmd_line = HAWK_NULL;
/* Once execution is completed regardless of success or failure,
* Restore stdin/out/err using handles duplicated into old_in/out/err */
DosDupHandle (old_in, &std_in); /* I can't do much if this fails */
DosClose (old_in); old_in = HAWK_PIO_HND_NIL;
DosDupHandle (old_out, &std_out);
DosClose (old_out); old_out = HAWK_PIO_HND_NIL;
DosDupHandle (old_err, &std_err);
DosClose (old_err); old_err = HAWK_PIO_HND_NIL;
2024-05-02 13:47:30 +00:00
if (rc != NO_ERROR)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
goto oops;
}
pio->child = child_rc.codeTerminate;
#elif defined(__DOS__)
/* DOS not multi-processed. can't support pio */
2019-12-18 08:16:34 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOIMPL);
2019-12-13 04:29:58 +00:00
return -1;
#else
if (flags & HAWK_PIO_WRITEIN)
{
2024-05-02 13:47:30 +00:00
if (HAWK_PIPE(&handle[0]) <= -1)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
goto oops;
}
minidx = 0; maxidx = 1;
}
if (flags & HAWK_PIO_READOUT)
{
2024-05-02 13:47:30 +00:00
if (HAWK_PIPE(&handle[2]) <= -1)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (minidx == -1) minidx = 2;
maxidx = 3;
}
if (flags & HAWK_PIO_READERR)
{
2024-05-02 13:47:30 +00:00
if (HAWK_PIPE(&handle[4]) <= -1)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (minidx == -1) minidx = 4;
maxidx = 5;
}
2024-05-02 13:47:30 +00:00
if (maxidx == -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
goto oops;
}
if (pio->flags & HAWK_PIO_FNCCMD)
{
/* i know i'm abusing typecasting here.
* cmd is supposed to be hawk_pio_fnc_t*, anyway */
pid = standard_fork_and_exec(pio, handle, (param_t*)cmd);
if (pid <= -1) goto oops;
pio->child = pid;
}
else
{
#if defined(HAVE_POSIX_SPAWN) && !(defined(HAWK_SYSCALL0) && defined(SYS_vfork))
2024-05-02 13:47:30 +00:00
if ((pserr = posix_spawn_file_actions_init(&fa)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
fa_inited = 1;
if (flags & HAWK_PIO_WRITEIN)
{
/* child should read */
2024-05-02 13:47:30 +00:00
if ((pserr = posix_spawn_file_actions_addclose(&fa, handle[1])) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((pserr = posix_spawn_file_actions_adddup2(&fa, handle[0], 0)) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-02 13:47:30 +00:00
if ((pserr = posix_spawn_file_actions_addclose(&fa, handle[0])) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
if (flags & HAWK_PIO_READOUT)
{
/* child should write */
if ((pserr = posix_spawn_file_actions_addclose(&fa, handle[2])) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((pserr = posix_spawn_file_actions_adddup2(&fa, handle[3], 1)) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_ERRTOOUT) &&
(pserr = posix_spawn_file_actions_adddup2 (&fa, handle[3], 2)) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((pserr = posix_spawn_file_actions_addclose(&fa, handle[3])) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
if (flags & HAWK_PIO_READERR)
{
/* child should write */
if ((pserr = posix_spawn_file_actions_addclose (&fa, handle[4])) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 2)) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_OUTTOERR) &&
(pserr = posix_spawn_file_actions_adddup2 (&fa, handle[5], 1)) != 0)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
2024-05-05 05:13:46 +00:00
if ((pserr = posix_spawn_file_actions_addclose(&fa, handle[5])) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
{
int oflags = O_RDWR;
#if defined(O_LARGEFILE)
oflags |= O_LARGEFILE;
#endif
if ((flags & HAWK_PIO_INTONUL) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addopen(&fa, 0, HAWK_BT("/dev/null"), oflags, 0)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_OUTTONUL) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addopen(&fa, 1, HAWK_BT("/dev/null"), oflags, 0)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_ERRTONUL) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addopen(&fa, 2, HAWK_BT("/dev/null"), oflags, 0)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
/* there remains the chance of race condition that
* 0, 1, 2 can be closed between addclose() and posix_spawn().
* so checking the file descriptors with is_fd_valid() is
* just on the best-effort basis.
*/
if ((flags & HAWK_PIO_DROPIN) && is_fd_valid(0) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addclose(&fa, 0)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_DROPOUT) && is_fd_valid(1) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addclose(&fa, 1)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if ((flags & HAWK_PIO_DROPERR) && is_fd_valid(2) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addclose(&fa, 2)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
if (!(flags & HAWK_PIO_NOCLOEXEC))
{
int fd = get_highest_fd(pio);
while (fd > 2)
{
if (fd != handle[0] && fd != handle[1] &&
fd != handle[2] && fd != handle[3] &&
2024-05-02 13:47:30 +00:00
fd != handle[4] && fd != handle[5])
2019-12-13 04:29:58 +00:00
{
/* closing attempt on a best-effort basis.
* posix_spawn() fails if the file descriptor added
* with addclose() is closed before posix_spawn().
* addclose() if no FD_CLOEXEC is set or it's unknown. */
2024-05-02 13:47:30 +00:00
if (is_fd_valid_and_nocloexec(fd) &&
2024-05-05 05:13:46 +00:00
(pserr = posix_spawn_file_actions_addclose(&fa, fd)) != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
}
fd--;
}
}
2024-05-05 05:13:46 +00:00
if (make_param(pio, cmd, flags, &param) <= -1) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
/* check if the command(the command requested or /bin/sh) is
2019-12-13 04:29:58 +00:00
* exectuable to return an error without trying to execute it
* though this check alone isn't sufficient */
2024-05-05 05:13:46 +00:00
if (assert_executable(pio, param.argv[0]) <= -1)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
free_param (pio, &param);
2019-12-13 04:29:58 +00:00
goto oops;
}
posix_spawnattr_init (&psattr);
#if defined(__linux)
#if !defined(POSIX_SPAWN_USEVFORK)
# define POSIX_SPAWN_USEVFORK 0x40
#endif
posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK);
#endif
pserr = posix_spawn(&pid, param.argv[0], &fa, &psattr, param.argv, environ);
#if defined(__linux)
posix_spawnattr_destroy (&psattr);
#endif
2024-05-02 13:47:30 +00:00
free_param (pio, &param);
if (fa_inited)
2019-12-13 04:29:58 +00:00
{
posix_spawn_file_actions_destroy (&fa);
fa_inited = 0;
}
2024-05-02 13:47:30 +00:00
if (pserr != 0)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(pserr));
2019-12-13 04:29:58 +00:00
goto oops;
}
pio->child = pid;
#elif defined(HAWK_SYSCALL0) && defined(SYS_vfork)
2024-05-05 05:13:46 +00:00
if (make_param(pio, cmd, flags, &param) <= -1) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
/* check if the command(the command requested or /bin/sh) is
2019-12-13 04:29:58 +00:00
* exectuable to return an error without trying to execute it
* though this check alone isn't sufficient */
2019-12-18 05:15:03 +00:00
if (assert_executable(pio, param.argv[0]) <= -1)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
free_param (pio, &param);
2019-12-13 04:29:58 +00:00
goto oops;
}
/* prepare some data before vforking for vfork limitation.
2024-05-02 13:47:30 +00:00
* the child in vfork should not make function calls or
2019-12-13 04:29:58 +00:00
* change data shared with the parent. */
if (!(flags & HAWK_PIO_NOCLOEXEC)) highest_fd = get_highest_fd (pio);
HAWK_SYSCALL0 (pid, SYS_vfork);
2024-05-02 13:47:30 +00:00
if (pid <= -1)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINVAL);
2019-12-13 04:29:58 +00:00
free_param (pio, &param);
goto oops;
}
if (pid == 0)
{
/* the child after vfork should not make function calls.
* since the system call like close() are also normal
* functions, i have to use assembly macros to make
* system calls. */
hawk_pio_hnd_t devnull = -1;
if (!(flags & HAWK_PIO_NOCLOEXEC))
{
/* cannot call close_open_fds_using_proc() in the vfork() context */
int fd = highest_fd;
2024-05-02 13:47:30 +00:00
/* close all other unknown open handles except
2019-12-13 04:29:58 +00:00
* stdin/out/err and the pipes. */
while (fd > 2)
{
if (fd != handle[0] && fd != handle[1] &&
fd != handle[2] && fd != handle[3] &&
2024-05-02 13:47:30 +00:00
fd != handle[4] && fd != handle[5])
2019-12-13 04:29:58 +00:00
{
HAWK_SYSCALL1 (dummy, SYS_close, fd);
}
fd--;
}
}
if (flags & HAWK_PIO_WRITEIN)
{
/* child should read */
HAWK_SYSCALL1 (dummy, SYS_close, handle[1]);
HAWK_SYSCALL2 (dummy, SYS_dup2, handle[0], 0);
if (dummy <= -1) goto child_oops;
HAWK_SYSCALL1 (dummy, SYS_close, handle[0]);
}
if (flags & HAWK_PIO_READOUT)
{
/* child should write */
HAWK_SYSCALL1 (dummy, SYS_close, handle[2]);
HAWK_SYSCALL2 (dummy, SYS_dup2, handle[3], 1);
if (dummy <= -1) goto child_oops;
if (flags & HAWK_PIO_ERRTOOUT)
{
HAWK_SYSCALL2 (dummy, SYS_dup2, handle[3], 2);
if (dummy <= -1) goto child_oops;
}
HAWK_SYSCALL1 (dummy, SYS_close, handle[3]);
}
if (flags & HAWK_PIO_READERR)
{
/* child should write */
HAWK_SYSCALL1 (dummy, SYS_close, handle[4]);
HAWK_SYSCALL2 (dummy, SYS_dup2, handle[5], 2);
if (dummy <= -1) goto child_oops;
if (flags & HAWK_PIO_OUTTOERR)
{
HAWK_SYSCALL2 (dummy, SYS_dup2, handle[5], 1);
if (dummy <= -1) goto child_oops;
}
HAWK_SYSCALL1 (dummy, SYS_close, handle[5]);
}
if (flags & (HAWK_PIO_INTONUL | HAWK_PIO_OUTTONUL | HAWK_PIO_ERRTONUL))
{
#if defined(O_LARGEFILE)
HAWK_SYSCALL3 (devnull, SYS_open, HAWK_BT("/dev/null"), O_RDWR|O_LARGEFILE, 0);
#else
HAWK_SYSCALL3 (devnull, SYS_open, HAWK_BT("/dev/null"), O_RDWR, 0);
#endif
if (devnull <= -1) goto child_oops;
}
if (flags & HAWK_PIO_INTONUL)
{
HAWK_SYSCALL2 (dummy, SYS_dup2, devnull, 0);
if (dummy <= -1) goto child_oops;
}
if (flags & HAWK_PIO_OUTTONUL)
{
HAWK_SYSCALL2 (dummy, SYS_dup2, devnull, 1);
if (dummy <= -1) goto child_oops;
}
if (flags & HAWK_PIO_ERRTONUL)
{
HAWK_SYSCALL2 (dummy, SYS_dup2, devnull, 2);
if (dummy <= -1) goto child_oops;
}
if (flags & (HAWK_PIO_INTONUL | HAWK_PIO_OUTTONUL | HAWK_PIO_ERRTONUL))
{
HAWK_SYSCALL1 (dummy, SYS_close, devnull);
devnull = -1;
}
if (flags & HAWK_PIO_DROPIN) HAWK_SYSCALL1 (dummy, SYS_close, 0);
if (flags & HAWK_PIO_DROPOUT) HAWK_SYSCALL1 (dummy, SYS_close, 1);
if (flags & HAWK_PIO_DROPERR) HAWK_SYSCALL1 (dummy, SYS_close, 2);
HAWK_SYSCALL3 (dummy, SYS_execve, param.argv[0], param.argv, environ);
/*free_param (pio, &param); don't free this in the vfork version */
child_oops:
if (devnull >= 0) HAWK_SYSCALL1 (dummy, SYS_close, devnull);
HAWK_SYSCALL1 (dummy, SYS_exit, 128);
}
/* parent */
free_param (pio, &param);
pio->child = pid;
#else
2024-05-05 05:13:46 +00:00
if (make_param(pio, cmd, flags, &param) <= -1) goto oops;
2019-12-13 04:29:58 +00:00
2024-05-02 13:47:30 +00:00
/* check if the command(the command requested or /bin/sh) is
2019-12-13 04:29:58 +00:00
* exectuable to return an error without trying to execute it
* though this check alone isn't sufficient */
2024-05-05 05:13:46 +00:00
if (assert_executable(pio, param.argv[0]) <= -1)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
free_param (pio, &param);
2019-12-13 04:29:58 +00:00
goto oops;
}
pid = standard_fork_and_exec(pio, handle, &param);
if (pid <= -1)
{
free_param (pio, &param);
goto oops;
}
/* parent */
free_param (pio, &param);
pio->child = pid;
#endif
}
if (flags & HAWK_PIO_WRITEIN)
{
2024-05-02 13:47:30 +00:00
/*
2019-12-13 04:29:58 +00:00
* 012345
* rw----
2024-05-02 13:47:30 +00:00
* X
2019-12-13 04:29:58 +00:00
* WRITE => 1
*/
HAWK_CLOSE (handle[0]);
handle[0] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READOUT)
{
2024-05-02 13:47:30 +00:00
/*
2019-12-13 04:29:58 +00:00
* 012345
* --rw--
* X
* READ => 2
*/
HAWK_CLOSE (handle[3]);
handle[3] = HAWK_PIO_HND_NIL;
}
if (flags & HAWK_PIO_READERR)
{
2024-05-02 13:47:30 +00:00
/*
2019-12-13 04:29:58 +00:00
* 012345
* ----rw
2024-05-02 13:47:30 +00:00
* X
2019-12-13 04:29:58 +00:00
* READ => 4
*/
HAWK_CLOSE (handle[5]);
handle[5] = HAWK_PIO_HND_NIL;
}
#endif
if (((flags & HAWK_PIO_INNOBLOCK) && set_pipe_nonblock(pio, handle[1], 1) <= -1) ||
((flags & HAWK_PIO_OUTNOBLOCK) && set_pipe_nonblock(pio, handle[2], 1) <= -1) ||
((flags & HAWK_PIO_ERRNOBLOCK) && set_pipe_nonblock(pio, handle[4], 1) <= -1))
{
goto oops;
}
/* store back references */
pio->pin[HAWK_PIO_IN].self = pio;
pio->pin[HAWK_PIO_OUT].self = pio;
pio->pin[HAWK_PIO_ERR].self = pio;
/* store actual pipe handles */
pio->pin[HAWK_PIO_IN].handle = handle[1];
pio->pin[HAWK_PIO_OUT].handle = handle[2];
pio->pin[HAWK_PIO_ERR].handle = handle[4];
if (flags & HAWK_PIO_TEXT)
{
int topt = 0;
if (flags & HAWK_PIO_IGNOREECERR) topt |= HAWK_TIO_IGNOREECERR;
if (flags & HAWK_PIO_NOAUTOFLUSH) topt |= HAWK_TIO_NOAUTOFLUSH;
2019-12-21 16:59:00 +00:00
HAWK_ASSERT (HAWK_COUNTOF(tio) == HAWK_COUNTOF(pio->pin));
2019-12-13 04:29:58 +00:00
for (i = 0; i < HAWK_COUNTOF(tio); i++)
{
int r;
2019-12-18 05:15:03 +00:00
tio[i] = hawk_tio_open(pio->gem, HAWK_SIZEOF(&pio->pin[i]), topt);
if (HAWK_UNLIKELY(!tio[i])) goto oops;
2019-12-18 05:15:03 +00:00
2019-12-13 04:29:58 +00:00
*(hawk_pio_pin_t**)hawk_tio_getxtn(tio[i]) = &pio->pin[i];
r = (i == HAWK_PIO_IN)?
2019-12-18 05:15:03 +00:00
hawk_tio_attachout(tio[i], pio_output, HAWK_NULL, 4096):
hawk_tio_attachin(tio[i], pio_input, HAWK_NULL, 4096);
if (r <= -1) goto oops;
2019-12-13 04:29:58 +00:00
pio->pin[i].tio = tio[i];
}
}
return 0;
oops:
#if defined(_WIN32)
if (windevnul != INVALID_HANDLE_VALUE) CloseHandle (windevnul);
#elif defined(__OS2__)
2019-12-18 05:15:03 +00:00
if (cmd_line) hawk_gem_freemem (pio->gem, cmd_line);
2019-12-13 04:29:58 +00:00
if (old_in != HAWK_PIO_HND_NIL)
{
DosDupHandle (old_in, &std_in);
2024-05-02 13:47:30 +00:00
DosClose (old_in);
2019-12-13 04:29:58 +00:00
}
if (old_out != HAWK_PIO_HND_NIL)
{
DosDupHandle (old_out, &std_out);
DosClose (old_out);
}
if (old_err != HAWK_PIO_HND_NIL)
{
DosDupHandle (old_err, &std_err);
DosClose (old_err);
}
if (os2devnul != HAWK_PIO_HND_NIL) DosClose (os2devnul);
#endif
2024-05-02 13:47:30 +00:00
for (i = 0; i < HAWK_COUNTOF(tio); i++)
2019-12-13 04:29:58 +00:00
{
if (tio[i]) hawk_tio_close (tio[i]);
}
#if defined(_WIN32)
for (i = minidx; i < maxidx; i++) CloseHandle (handle[i]);
#elif defined(__OS2__)
2024-05-02 13:47:30 +00:00
for (i = minidx; i < maxidx; i++)
2019-12-13 04:29:58 +00:00
{
if (handle[i] != HAWK_PIO_HND_NIL) DosClose (handle[i]);
}
#elif defined(__DOS__)
/* DOS not multi-processed. can't support pio */
#elif defined(HAVE_POSIX_SPAWN) && !(defined(HAWK_SYSCALL0) && defined(SYS_vfork))
2024-05-02 13:47:30 +00:00
if (fa_inited)
2019-12-13 04:29:58 +00:00
{
posix_spawn_file_actions_destroy (&fa);
fa_inited = 0;
}
2024-05-02 13:47:30 +00:00
for (i = minidx; i < maxidx; i++)
2019-12-13 04:29:58 +00:00
{
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]);
}
#elif defined(HAWK_SYSCALL0) && defined(SYS_vfork)
2024-05-02 13:47:30 +00:00
for (i = minidx; i < maxidx; i++)
2019-12-13 04:29:58 +00:00
{
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]);
}
#else
2024-05-02 13:47:30 +00:00
for (i = minidx; i < maxidx; i++)
2019-12-13 04:29:58 +00:00
{
if (handle[i] != HAWK_PIO_HND_NIL) HAWK_CLOSE (handle[i]);
}
#endif
return -1;
}
void hawk_pio_fini (hawk_pio_t* pio)
{
hawk_pio_end (pio, HAWK_PIO_ERR);
hawk_pio_end (pio, HAWK_PIO_OUT);
hawk_pio_end (pio, HAWK_PIO_IN);
/* when closing, enable blocking and retrying */
pio->flags &= ~HAWK_PIO_WAITNOBLOCK;
pio->flags &= ~HAWK_PIO_WAITNORETRY;
hawk_pio_wait (pio);
}
hawk_cmgr_t* hawk_pio_getcmgr (hawk_pio_t* pio, hawk_pio_hid_t hid)
{
2019-12-18 05:15:03 +00:00
return pio->pin[hid].tio? hawk_tio_getcmgr(pio->pin[hid].tio): HAWK_NULL;
2019-12-13 04:29:58 +00:00
}
void hawk_pio_setcmgr (hawk_pio_t* pio, hawk_pio_hid_t hid, hawk_cmgr_t* cmgr)
{
if (pio->pin[hid].tio) hawk_tio_setcmgr (pio->pin[hid].tio, cmgr);
}
hawk_pio_hnd_t hawk_pio_gethnd (const hawk_pio_t* pio, hawk_pio_hid_t hid)
{
return pio->pin[hid].handle;
}
hawk_pio_pid_t hawk_pio_getchild (const hawk_pio_t* pio)
{
return pio->child;
}
static hawk_ooi_t pio_read (hawk_pio_t* pio, void* buf, hawk_oow_t size, hawk_pio_hnd_t hnd)
{
#if defined(_WIN32)
DWORD count;
#elif defined(__OS2__)
ULONG count;
APIRET rc;
#elif defined(__DOS__)
int n;
#else
hawk_ooi_t n;
#endif
2024-05-02 13:47:30 +00:00
if (hnd == HAWK_PIO_HND_NIL)
2019-12-13 04:29:58 +00:00
{
/* the stream is already closed */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOHND);
2019-12-13 04:29:58 +00:00
return (hawk_ooi_t)-1;
}
#if defined(_WIN32)
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(DWORD)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(DWORD);
2024-05-02 13:47:30 +00:00
if (ReadFile(hnd, buf, (DWORD)size, &count, HAWK_NULL) == FALSE)
2019-12-13 04:29:58 +00:00
{
/* ReadFile receives ERROR_BROKEN_PIPE when the write end
* is closed in the child process */
if (GetLastError() == ERROR_BROKEN_PIPE) return 0;
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
return -1;
}
return (hawk_ooi_t)count;
#elif defined(__OS2__)
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(ULONG)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(ULONG);
2024-05-05 05:13:46 +00:00
rc = DosRead(hnd, buf, (ULONG)size, &count);
2019-12-13 04:29:58 +00:00
if (rc != NO_ERROR)
{
if (rc == ERROR_BROKEN_PIPE) return 0; /* TODO: check this */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
return -1;
}
return (hawk_ooi_t)count;
#elif defined(__DOS__)
/* TODO: verify this */
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(unsigned int)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(unsigned int);
2024-05-05 05:13:46 +00:00
n = read(hnd, buf, size);
if (n <= -1) hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return n;
#else
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(size_t)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(size_t);
reread:
n = HAWK_READ(hnd, buf, size);
2024-05-02 13:47:30 +00:00
if (n <= -1)
2019-12-13 04:29:58 +00:00
{
if (errno == EINTR)
{
2024-05-02 13:47:30 +00:00
if (pio->flags & HAWK_PIO_READNORETRY)
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINTR);
2019-12-13 04:29:58 +00:00
else goto reread;
}
else
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
}
}
return n;
#endif
}
hawk_ooi_t hawk_pio_read (hawk_pio_t* pio, hawk_pio_hid_t hid, void* buf, hawk_oow_t size)
{
2019-12-18 05:15:03 +00:00
return (pio->pin[hid].tio == HAWK_NULL)?
pio_read(pio, buf, size, pio->pin[hid].handle):
hawk_tio_read(pio->pin[hid].tio, buf, size);
2019-12-13 04:29:58 +00:00
}
2020-01-14 09:10:47 +00:00
hawk_ooi_t hawk_pio_readbytes (hawk_pio_t* pio, hawk_pio_hid_t hid, void* buf, hawk_oow_t size)
{
return (pio->pin[hid].tio == HAWK_NULL)?
pio_read(pio, buf, size, pio->pin[hid].handle):
hawk_tio_readbchars(pio->pin[hid].tio, buf, size);
}
2019-12-13 04:29:58 +00:00
static hawk_ooi_t pio_write (hawk_pio_t* pio, const void* data, hawk_oow_t size, hawk_pio_hnd_t hnd)
{
#if defined(_WIN32)
DWORD count;
#elif defined(__OS2__)
ULONG count;
APIRET rc;
#elif defined(__DOS__)
int n;
#else
hawk_ooi_t n;
#endif
2024-05-02 13:47:30 +00:00
if (hnd == HAWK_PIO_HND_NIL)
2019-12-13 04:29:58 +00:00
{
/* the stream is already closed */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOHND);
2019-12-13 04:29:58 +00:00
return (hawk_ooi_t)-1;
}
#if defined(_WIN32)
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(DWORD)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(DWORD);
2024-05-05 05:13:46 +00:00
if (WriteFile(hnd, data, (DWORD)size, &count, HAWK_NULL) == FALSE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
return -1;
}
return (hawk_ooi_t)count;
#elif defined(__OS2__)
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(ULONG)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(ULONG);
2024-05-05 05:13:46 +00:00
rc = DosWrite(hnd, (PVOID)data, (ULONG)size, &count);
2019-12-13 04:29:58 +00:00
if (rc != NO_ERROR)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
return -1;
}
return (hawk_ooi_t)count;
#elif defined(__DOS__)
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(unsigned int)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(unsigned int);
2024-05-05 05:13:46 +00:00
n = write(hnd, data, size);
if (n <= -1) hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return n;
#else
if (size > (HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(size_t)))
size = HAWK_TYPE_MAX(hawk_ooi_t) & HAWK_TYPE_MAX(size_t);
rewrite:
2020-01-14 09:10:47 +00:00
n = HAWK_WRITE(hnd, data, size);
2024-05-02 13:47:30 +00:00
if (n <= -1)
2019-12-13 04:29:58 +00:00
{
if (errno == EINTR)
{
if (pio->flags & HAWK_PIO_WRITENORETRY)
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_EINTR);
2019-12-13 04:29:58 +00:00
else goto rewrite;
}
else
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
}
}
return n;
#endif
}
hawk_ooi_t hawk_pio_write (hawk_pio_t* pio, hawk_pio_hid_t hid, const void* data, hawk_oow_t size)
{
2019-12-18 05:15:03 +00:00
return (pio->pin[hid].tio == HAWK_NULL)?
pio_write(pio, data, size, pio->pin[hid].handle):
hawk_tio_write(pio->pin[hid].tio, data, size);
2019-12-13 04:29:58 +00:00
}
hawk_ooi_t hawk_pio_writebytes (hawk_pio_t* pio, hawk_pio_hid_t hid, const void* data, hawk_oow_t size)
{
2019-12-18 05:15:03 +00:00
return (pio->pin[hid].tio == HAWK_NULL)?
pio_write(pio, data, size, pio->pin[hid].handle):
hawk_tio_writebchars(pio->pin[hid].tio, data, size);
2019-12-13 04:29:58 +00:00
}
hawk_ooi_t hawk_pio_flush (hawk_pio_t* pio, hawk_pio_hid_t hid)
{
if (pio->pin[hid].tio == HAWK_NULL) return 0;
2019-12-18 05:15:03 +00:00
return hawk_tio_flush(pio->pin[hid].tio);
2019-12-13 04:29:58 +00:00
}
void hawk_pio_drain (hawk_pio_t* pio, hawk_pio_hid_t hid)
{
if (pio->pin[hid].tio) hawk_tio_drain (pio->pin[hid].tio);
}
void hawk_pio_end (hawk_pio_t* pio, hawk_pio_hid_t hid)
{
if (pio->pin[hid].tio)
{
hawk_tio_close (pio->pin[hid].tio);
pio->pin[hid].tio = HAWK_NULL;
}
if (pio->pin[hid].handle != HAWK_PIO_HND_NIL)
{
#if defined(_WIN32)
CloseHandle (pio->pin[hid].handle);
#elif defined(__OS2__)
DosClose (pio->pin[hid].handle);
#elif defined(__DOS__)
close (pio->pin[hid].handle);
#else
HAWK_CLOSE (pio->pin[hid].handle);
#endif
pio->pin[hid].handle = HAWK_PIO_HND_NIL;
}
}
int hawk_pio_wait (hawk_pio_t* pio)
{
#if defined(_WIN32)
DWORD ecode, w;
2024-05-02 13:47:30 +00:00
if (pio->child == HAWK_PIO_PID_NIL)
2019-12-18 05:15:03 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ECHILD);
2019-12-13 04:29:58 +00:00
return -1;
}
2024-05-05 05:13:46 +00:00
w = WaitForSingleObject(pio->child,
2019-12-13 04:29:58 +00:00
((pio->flags & HAWK_PIO_WAITNOBLOCK)? 0: INFINITE)
);
if (w == WAIT_TIMEOUT)
{
/* the child process is still alive */
return 255 + 1;
}
if (w != WAIT_OBJECT_0)
{
/* WAIT_FAILED, WAIT_ABANDONED */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
return -1;
}
2019-12-21 16:59:00 +00:00
HAWK_ASSERT (w == WAIT_OBJECT_0);
2024-05-02 13:47:30 +00:00
if (GetExitCodeProcess(pio->child, &ecode) == FALSE)
2019-12-13 04:29:58 +00:00
{
2024-05-02 13:47:30 +00:00
/* close the handle anyway to prevent further
2019-12-13 04:29:58 +00:00
* errors when this function is called again */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2024-05-02 13:47:30 +00:00
CloseHandle (pio->child);
2019-12-13 04:29:58 +00:00
pio->child = HAWK_PIO_PID_NIL;
return -1;
}
/* close handle here to emulate waitpid() as much as possible. */
2024-05-02 13:47:30 +00:00
CloseHandle (pio->child);
2019-12-13 04:29:58 +00:00
pio->child = HAWK_PIO_PID_NIL;
if (ecode == STILL_ACTIVE)
{
/* this should not happen as the control reaches here
* only when WaitforSingleObject() is successful.
* if it happends, close the handle and return an error */
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ESYSERR);
2019-12-13 04:29:58 +00:00
return -1;
}
return ecode;
#elif defined(__OS2__)
APIRET rc;
RESULTCODES child_rc;
PID ppid;
2024-05-02 13:47:30 +00:00
if (pio->child == HAWK_PIO_PID_NIL)
2019-12-18 05:15:03 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ECHILD);
2019-12-13 04:29:58 +00:00
return -1;
}
2024-05-05 05:13:46 +00:00
rc = DosWaitChild(
2019-12-13 04:29:58 +00:00
DCWA_PROCESSTREE,
((pio->flags & HAWK_PIO_WAITNOBLOCK)? DCWW_NOWAIT: DCWW_WAIT),
&child_rc,
&ppid,
pio->child
);
if (rc == ERROR_CHILD_NOT_COMPLETE)
{
/* the child process is still alive */
return 255 + 1;
}
if (rc != NO_ERROR)
{
/* WAIT_FAILED, WAIT_ABANDONED */
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
return -1;
}
/* close handle here to emulate waitpid() as much as possible. */
/*DosClose (pio->child);*/
pio->child = HAWK_PIO_PID_NIL;
2024-05-02 13:47:30 +00:00
return (child_rc.codeTerminate == TC_EXIT)?
2019-12-13 04:29:58 +00:00
child_rc.codeResult: (255 + 1 + child_rc.codeTerminate);
#elif defined(__DOS__)
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOIMPL);
2019-12-13 04:29:58 +00:00
return -1;
#else
int opt = 0;
int ret = -1;
2024-05-02 13:47:30 +00:00
if (pio->child == HAWK_PIO_PID_NIL)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ECHILD);
2019-12-13 04:29:58 +00:00
return -1;
}
if (pio->flags & HAWK_PIO_WAITNOBLOCK) opt |= WNOHANG;
while (1)
{
int status, n;
2024-05-05 05:13:46 +00:00
n = HAWK_WAITPID(pio->child, &status, opt);
2019-12-13 04:29:58 +00:00
if (n <= -1)
{
if (errno == ECHILD)
{
2024-05-02 13:47:30 +00:00
/* most likely, the process has already been
2019-12-13 04:29:58 +00:00
* waitpid()ed on. */
pio->child = HAWK_PIO_PID_NIL;
}
else if (errno == EINTR)
{
2019-12-18 05:15:03 +00:00
if (!(pio->flags & HAWK_PIO_WAITNORETRY)) continue;
2019-12-13 04:29:58 +00:00
}
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
break;
}
2024-05-02 13:47:30 +00:00
if (n == 0)
2019-12-13 04:29:58 +00:00
{
/* when WNOHANG is not specified, 0 can't be returned */
2019-12-21 16:59:00 +00:00
/*HAWK_ASSERT (pio->flags & HAWK_PIO_WAITNOBLOCK);*/
2019-12-13 04:29:58 +00:00
ret = 255 + 1;
/* the child process is still alive */
break;
}
if (n == pio->child)
{
if (WIFEXITED(status))
{
/* the child process ended normally */
ret = WEXITSTATUS(status);
}
else if (WIFSIGNALED(status))
{
/* the child process was killed by a signal */
ret = 255 + 1 + WTERMSIG (status);
}
else
{
/* not interested in WIFSTOPPED & WIFCONTINUED.
* in fact, this else-block should not be reached
* as WIFEXITED or WIFSIGNALED must be true.
* anyhow, just set the return value to 0. */
ret = 0;
}
pio->child = HAWK_PIO_PID_NIL;
break;
}
}
return ret;
#endif
}
int hawk_pio_kill (hawk_pio_t* pio)
{
#if defined(_WIN32)
DWORD n;
#elif defined(__OS2__)
APIRET rc;
#elif defined(__DOS__)
/* TODO: implement this */
#else
int n;
#endif
2024-05-02 13:47:30 +00:00
if (pio->child == HAWK_PIO_PID_NIL)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ECHILD);
2019-12-13 04:29:58 +00:00
return -1;
}
#if defined(_WIN32)
/* 9 was chosen below to treat TerminateProcess as kill -KILL. */
n = TerminateProcess(pio->child, 255 + 1 + 9);
2024-05-02 13:47:30 +00:00
if (n == FALSE)
2019-12-13 04:29:58 +00:00
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(GetLastError()));
2019-12-13 04:29:58 +00:00
return -1;
}
return 0;
#elif defined(__OS2__)
/*TODO: must use DKP_PROCESS? */
rc = DosKillProcess(pio->child, DKP_PROCESSTREE);
if (rc != NO_ERROR)
{
hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(rc));
2019-12-13 04:29:58 +00:00
return -1;
}
2024-05-02 13:47:30 +00:00
return 0;
2019-12-13 04:29:58 +00:00
#elif defined(__DOS__)
2019-12-18 05:15:03 +00:00
hawk_gem_seterrnum (pio->gem, HAWK_NULL, HAWK_ENOIMPL);
2019-12-13 04:29:58 +00:00
return -1;
#else
2019-12-18 05:15:03 +00:00
n = HAWK_KILL(pio->child, SIGKILL);
if (n <= -1) hawk_gem_seterrnum (pio->gem, HAWK_NULL, hawk_syserr_to_errnum(errno));
2019-12-13 04:29:58 +00:00
return n;
#endif
}
static hawk_ooi_t pio_input (hawk_tio_t* tio, hawk_tio_cmd_t cmd, void* buf, hawk_oow_t size)
{
2024-05-02 13:47:30 +00:00
if (cmd == HAWK_TIO_DATA)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_pio_pin_t* pin = *(hawk_pio_pin_t**)hawk_tio_getxtn(tio);
2019-12-21 16:59:00 +00:00
HAWK_ASSERT (pin != HAWK_NULL);
HAWK_ASSERT (pin->self != HAWK_NULL);
2019-12-13 04:29:58 +00:00
return pio_read(pin->self, buf, size, pin->handle);
}
/* take no actions for OPEN and CLOSE as they are handled
* by pio */
return 0;
}
static hawk_ooi_t pio_output (hawk_tio_t* tio, hawk_tio_cmd_t cmd, void* buf, hawk_oow_t size)
{
2024-05-02 13:47:30 +00:00
if (cmd == HAWK_TIO_DATA)
2019-12-13 04:29:58 +00:00
{
2019-12-18 05:15:03 +00:00
hawk_pio_pin_t* pin = *(hawk_pio_pin_t**)hawk_tio_getxtn(tio);
2019-12-21 16:59:00 +00:00
HAWK_ASSERT (pin != HAWK_NULL);
HAWK_ASSERT (pin->self != HAWK_NULL);
2019-12-13 04:29:58 +00:00
return pio_write(pin->self, buf, size, pin->handle);
}
/* take no actions for OPEN and CLOSE as they are handled
* by pio */
return 0;
}