diff --git a/qse/include/qse/cmn/pio.h b/qse/include/qse/cmn/pio.h index 45b4e10b..67ad791e 100644 --- a/qse/include/qse/cmn/pio.h +++ b/qse/include/qse/cmn/pio.h @@ -43,6 +43,12 @@ enum qse_pio_open_flag_t QSE_PIO_SHELL = (1 << 11) }; +enum qse_pio_wait_flag_t +{ + QSE_PIO_NOWAIT = (1 << 0), + QSE_PIO_IGNINTR = (1 << 1) +}; + enum qse_pio_hid_t { QSE_PIO_IN = 0, @@ -55,11 +61,14 @@ typedef enum qse_pio_hid_t qse_pio_hid_t; #ifdef _WIN32 /* => typedef PVOID HANDLE; */ typedef void* qse_pio_hnd_t; - typedef int qse_pio_pid_t; /* TODO */ + typedef void* qse_pio_pid_t; +# define QSE_PIO_HND_NIL ((qse_pio_hnd_t)QSE_NULL) +# define QSE_PIO_PID_NIL ((qse_pio_pid_t)QSE_NULL) #else typedef int qse_pio_hnd_t; typedef int qse_pio_pid_t; # define QSE_PIO_HND_NIL ((qse_pio_hnd_t)-1) +# define QSE_PIO_PID_NIL ((qse_pio_hnd_t)-1) #endif typedef struct qse_pio_t qse_pio_t; @@ -73,6 +82,7 @@ struct qse_pio_t #define QSE_PIO_MMGR(pio) ((pio)->mmgr) #define QSE_PIO_HANDLE(pio,hid) ((pio)->handle[hid]) +#define QSE_PIO_CHILD(pio) ((pio)->child) #ifdef __cplusplus extern "C" { @@ -80,7 +90,7 @@ extern "C" { /****f* qse.cmn.pio/qse_pio_open * NAME - * qse_pio_open - open a pipe to a child process + * qse_pio_open - open pipes to a child process * * DESCRIPTION * QSE_PIO_SHELL drives the function to execute the command via /bin/sh. @@ -98,7 +108,7 @@ qse_pio_t* qse_pio_open ( /****f* qse.cmn.pio/qse_pio_close * NAME - * qse_pio_close - close a pipe + * qse_pio_close - close pipes to a child process * * SYNOPSIS */ @@ -107,24 +117,27 @@ void qse_pio_close ( ); /******/ +/****f* qse.cmn/pio/qse_pio_init + * NAME + * qse_pio_init - initialize pipes to a child process + * + * SYNOPSIS + */ qse_pio_t* qse_pio_init ( qse_pio_t* pio, qse_mmgr_t* mmgr, const qse_char_t* path, int flags ); +/******/ -void qse_pio_fini ( - qse_pio_t* pio -); - -/****f* qse.cmn.pio/qse_pio_wait +/****f* qse.cmn/pio/qse_pio_fini * NAME - * qse_pio_wait - wait for a child process + * qse_pio_fini - finalize pipes to a child process * * SYNOPSIS */ -int qse_pio_wait ( +void qse_pio_fini ( qse_pio_t* pio ); /******/ @@ -141,6 +154,17 @@ qse_pio_hnd_t qse_pio_gethandle ( ); /******/ +/****f* qse.cmn.pio/qse_pio_getchild + * NAME + * qse_pio_getchild - get the PID of a child process + * + * SYNOPSIS + */ +qse_pio_pid_t qse_pio_getchild ( + qse_pio_t* pio +); +/******/ + /****f* qse.cmn.pio/qse_pio_read * NAME * qse_pio_read - read data @@ -185,6 +209,18 @@ void qse_pio_end ( ); /******/ +/****f* qse.cmn.pio/qse_pio_wait + * NAME + * qse_pio_wait - wait for a child process + * + * SYNOPSIS + */ +int qse_pio_wait ( + qse_pio_t* pio, + int flags +); +/******/ + #ifdef __cplusplus } #endif diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index ff8453e5..f8a31e7b 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -27,8 +27,11 @@ # include "syscall.h" # include # include +# include #endif +#define CHILD_EXIT_CODE 128 + qse_pio_t* qse_pio_open ( qse_mmgr_t* mmgr, qse_size_t ext, const qse_char_t* path, int flags) @@ -86,7 +89,9 @@ qse_pio_t* qse_pio_init ( #ifdef _WIN32 /* TODO: XXXXXXXXXXXXXXXXX */ +http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx #else + if (flags & QSE_PIO_WRITEIN) { if (QSE_PIPE(&handle[0]) == -1) goto oops; @@ -298,7 +303,7 @@ qse_pio_t* qse_pio_init ( } child_oops: - QSE_EXIT(127); + QSE_EXIT (CHILD_EXIT_CODE); } /* parent */ @@ -352,44 +357,10 @@ oops: void qse_pio_fini (qse_pio_t* pio) { -#ifdef _WIN32 - CloseHandle (pio->handle); -#else - int i, status; - qse_pio_end (pio, QSE_PIO_IN); qse_pio_end (pio, QSE_PIO_OUT); qse_pio_end (pio, QSE_PIO_ERR); - - while (QSE_WAITPID (pio->child, &status, 0) == -1) - { - if (errno != EINTR) break; - } -#endif -} - -int qse_pio_wait (qse_pio_t* pio) -{ - int status; - -#if 0 - if (opt & QSE_PIO_NOWAIT) - { - opt |= WNOHANG; - } - - while (1) - { - n = QSE_WAITPID (pio->child, &status, opt); - if (n == 0) break; - /* TODO: .... */ - } - - - output => return code... - output => termination cause... -#endif - return -1; + qse_pio_wait (pio, QSE_PIO_IGNINTR); } qse_pio_hnd_t qse_pio_gethandle (qse_pio_t* pio, qse_pio_hid_t hid) @@ -397,6 +368,11 @@ qse_pio_hnd_t qse_pio_gethandle (qse_pio_t* pio, qse_pio_hid_t hid) return pio->handle[hid]; } +qse_pio_pid_t qse_pio_getchild (qse_pio_t* pio) +{ + return pio->child; +} + qse_ssize_t qse_pio_read ( qse_pio_t* pio, void* buf, qse_size_t size, qse_pio_hid_t hid) { @@ -445,3 +421,87 @@ void qse_pio_end (qse_pio_t* pio, qse_pio_hid_t hid) pio->handle[hid] = QSE_PIO_HND_NIL; } } + +/* +return -1 on error +return -2 on no change +return retcode on normal exit +return 255+signal on kill. +*/ + +int qse_pio_wait (qse_pio_t* pio, int flags) +{ +#ifdef _WIN32 + DWORD ec; + + if (pio->child == QSE_PIO_PID_NIL) return -1; + + WaitForSingleObject (pio->child, -1); + if (GetExitCodeProcess (pio->child, &ec) == -1) + /* close handle here to emulate waitpid() as much as possible. */ + CloseHandle (pio->child); + pio->child = QSE_PIO_PID_NIL; + +#else + int status; + int opt = 0; + int ret = -1; + + if (pio->child == QSE_PIO_PID_NIL) return -1; + + if (flags & QSE_PIO_NOWAIT) opt |= WNOHANG; + + while (1) + { + int n = QSE_WAITPID (pio->child, &status, opt); + +qse_printf (QSE_T("n=%d,pio->child=%d,errno=%d,ECHILD=%d\n"), n, pio->child, errno, ECHILD); + if (n == -1) + { + if (errno == ECHILD) + { + /* most likely, the process has already been waitpid()ed on. */ + /* TODO: what should we do... ? */ + /* ??? TREAT AS NORMAL??? => cannot know exit code => TREAT AS ERROR but reset pio->child */ + + pio->child = QSE_PIO_PID_NIL; + ret = -1; /* OR ECHILD / QSE_PIO_ECHILD */ + break; + } + + if (errno != EINTR || !(flags & QSE_PIO_IGNINTR)) break; + } + + if (n == 0) + { + /* when WNOHANG is not specified, 0 can't be returned */ + QSE_ASSERT (flags & QSE_PIO_NOWAIT); + + ret = -2; + /* 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 + WTERMSIG (status); + } + + /* not interested in WIFSTOPPED & WIFCONTINUED */ + + pio->child = QSE_PIO_PID_NIL; + break; + } + } + + return ret; +#endif +} diff --git a/qse/test/cmn/pio.c b/qse/test/cmn/pio.c index 3d5f2170..2f792128 100644 --- a/qse/test/cmn/pio.c +++ b/qse/test/cmn/pio.c @@ -53,6 +53,7 @@ static int pio1 (const qse_char_t* cmd, int oflags, qse_pio_hid_t rhid) return 0; } + static int test1 (void) { return pio1 (QSE_T("ls -laF"), QSE_PIO_READOUT|QSE_PIO_WRITEIN|QSE_PIO_SHELL, QSE_PIO_OUT); @@ -65,8 +66,32 @@ static int test2 (void) static int test3 (void) { - //return pio1 (QSE_T("/bin/ls -laF"), QSE_PIO_READERR|QSE_PIO_OUTTOERR|QSE_PIO_WRITEIN, QSE_PIO_ERR); - return pio1 (QSE_T("\"/bin/ls\" -laF"), QSE_PIO_READERR|QSE_PIO_OUTTOERR|QSE_PIO_WRITEIN, QSE_PIO_ERR); + return pio1 (QSE_T("/bin/ls -laF"), QSE_PIO_READERR|QSE_PIO_OUTTOERR|QSE_PIO_WRITEIN, QSE_PIO_ERR); +} + +static int test4 (void) +{ + qse_pio_t* pio; + + pio = qse_pio_open ( + QSE_NULL, + 0, + "ls -laF", + QSE_PIO_READOUT|QSE_PIO_READERR|QSE_PIO_WRITEIN + ); + if (pio == QSE_NULL) + { + qse_printf (QSE_T("cannot open program through pipe\n")); + return -1; + } + + { + int status; + sleep (5); + qse_printf (QSE_T("waitpid...%d\n"), (int)waitpid (-1, &status, 0)); + } + + qse_pio_close (pio); } int main () @@ -80,6 +105,7 @@ int main () R (test1); R (test2); R (test3); + R (test4); return 0; }