[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: IUP, popen & processes
- From: Peter Odding <peter@...>
- Date: Wed, 11 Aug 2010 10:04:51 +0200
I actually think that simple non-blocking I/O would suffice with a
proper popen.
I have no problem with the cost of starting (and maintaining) a new
process to do I/O.
The real problem is that you can't make the pipe from popen non-blocking
on Win32.
I might look into a module that would allow non-blocking pipes.... and a
silent background process
(ie no "dos box") on Win32...
I recently needed the ability for io.popen() to not show the command
prompt window (dos box) on Windows. I found code on the lua-users wiki
which did this for Lua 4 and got it to work with the current Windows SDK
(with some caveats), please see the attached file.
- Peter Odding
PS. This requires recompiling Lua (which wasn't a problem for me
fortunately) with the following changes to luaconf.h:
/*
@@ lua_popen spawns a new process connected to the current one through
@* the file streams.
** CHANGE it if you have a way to implement it in your system.
*/
#if defined(LUA_USE_POPEN)
#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m))
#define lua_pclose(L,file) ((void)L, (pclose(file) != -1))
#elif defined(LUA_WIN)
/* Modification to support hidden console windows based on
* http://lua-users.org/wiki/PipesOnWindows. */
#include <stdio.h> /* we need FILE */
FILE* pt_popen(const char *, const char *);
int pt_pclose(FILE *);
#define lua_popen(L,c,m) ((void)L, pt_popen(c,m))
#define lua_pclose(L,file) ((void)L, (pt_pclose(file) != -1))
/* End of modification (but see "popen.c"). */
#else
#define lua_popen(L,c,m) ((void)((void)c, m), \
luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0)
#define lua_pclose(L,file) ((void)((void)L, file), 0)
#endif
/* I found the following code at http://home.mweb.co.za/sd/sdonovan/popen.zip
* via http://lua-users.org/wiki/PipesOnWindows. Apparently it was written for
* an earlier version of Lua and it needed some changes to pt_pipe() but apart
* from only supporting binary mode pipes this works fine with Lua 5.1.4 :-).
* The changes to pt_pipe() are as follows:
*
* - STARTF_USESTDHANDLES -> STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW
* - si.wShowWindow = SW_HIDE
* - DETACHED_PROCESS -> CREATE_NEW_CONSOLE
*
* I've also cleaned up the code a little. For future reference the following
* article in the Microsoft Knowledge Base is really useful:
* http://support.microsoft.com/kb/190351
*
* See also the accompanying changes in the file luaconf.h.
* - Peter Odding <peter@peterodding.com>
*
* Original file header follows:
*
* popen.c
* RunSilent() is by Steven Szelei,
* and pt_popen()/pt_pclose() is by Kurt Keller
* Modified and comments translated by Steve Donovan
*
* ... <outdated integration instructions snipped by Peter> ...
*/
#include <windows.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <io.h>
/* TODO: Use a dynamically resized buffer for this! */
#define CMDLINE_BUFSIZE 4096
char *buildcmdline(const char *cmdline, char *buffer)
{
/* Launch the external command using the default command interpreter. */
const char *interpreter = getenv("COMSPEC");
if (!interpreter) interpreter = "CMD.EXE";
strcpy(buffer, interpreter);
/* CMD /C carries out the specified command line, then terminates. */
strcat(buffer, " /C ");
strcat(buffer, cmdline);
return buffer;
}
DWORD RunSilent(const char* cmdline)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
char buffer[CMDLINE_BUFSIZE];
char *interpreter;
ULONG returncode;
/* Initialize the required structures. */
ZeroMemory(&pi, sizeof(pi));
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
/* Build the command line. */
buildcmdline(cmdline, buffer);
if (!CreateProcess(
NULL, /* Name of module to be executed. */
buffer, /* Command line to be executed. */
NULL, /* Process security attributes. */
NULL, /* Primary thread security attributes. */
FALSE, /* Handles are inherited. */
CREATE_NEW_CONSOLE, /* Creation flags. */
NULL, /* Use parent process environment. */
NULL, /* Use parent process working directory. */
&si, /* Startup information structure. */
&pi)) /* Process information structure. */
/* TODO This doesn't really make sense does it?! */
return GetLastError();
/* Wait for the external command to finish. */
WaitForSingleObject(pi.hProcess, INFINITE);
/* Get the return code of the external command. */
if (!GetExitCodeProcess(pi.hProcess, &returncode))
/* TODO If this fails we may need to raise(GetLastError())?! */
returncode = 0;
/* Cleanup parent references to child process. */
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
return returncode;
}
/*------------------------------------------------------------------------------
Globals for the Routines pt_popen() / pt_pclose()
------------------------------------------------------------------------------*/
static HANDLE my_pipein[2], my_pipeout[2], my_pipeerr[2];
static char my_popenmode = ' ';
static int
my_pipe(HANDLE *readwrite)
{
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa); /* Length in bytes. */
sa.bInheritHandle = 1; /* Child must inherit these handles. */
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&readwrite[0], &readwrite[1], &sa, 1 << 13))
{
errno = -1; /* EMFILE; que? */
return -1;
}
return 0;
}
/* Replacement for popen() under Windows. Note that if the command line
* contains the string 2>&1 we connect standard error to standard output. */
FILE * pt_popen(const char *cmdline, const char *mode)
{
PROCESS_INFORMATION pi;
STARTUPINFO si;
char buffer[CMDLINE_BUFSIZE];
FILE *fptr = (FILE *)0;
char *err2out;
int redirect_error = 0;
/* Build the command line. */
buildcmdline(cmdline, buffer);
/* Initialize the global pipe handle structures. */
my_pipein[0] = INVALID_HANDLE_VALUE;
my_pipein[1] = INVALID_HANDLE_VALUE;
my_pipeout[0] = INVALID_HANDLE_VALUE;
my_pipeout[1] = INVALID_HANDLE_VALUE;
my_pipeerr[0] = INVALID_HANDLE_VALUE;
my_pipeerr[1] = INVALID_HANDLE_VALUE;
/* Validate mode argument. */
if (!(mode && (mode[0] == 'r' || mode[0] == 'w')))
goto finito;
my_popenmode = mode[0];
/* Shall we redirect standard error to standard output? */
if ((err2out = strstr("2>&1", buffer)) != NULL) {
/* This option doesn't apply to Windows shells, so we clear it out! */
strncpy(err2out, " ", 4);
redirect_error = 1;
}
/* Create the pipes. */
if (my_pipe(my_pipein) == -1 || my_pipe(my_pipeout) == -1)
goto finito;
if (!redirect_error && my_pipe(my_pipeerr) == -1)
goto finito;
/* Now create the child process. */
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
/* XXX Peter Odding: I've changed the following two lines. */
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdInput = my_pipein[0];
si.hStdOutput = my_pipeout[1];
si.hStdError = redirect_error ? my_pipeout[1] : my_pipeerr[1];
if (!CreateProcess(
NULL, /* Name of module to be executed. */
(LPTSTR)buffer, /* Command line to be executed. */
NULL, /* Process security attributes. */
NULL, /* Primary thread security attributes. */
TRUE, /* Handles are inherited. */
CREATE_NEW_CONSOLE, /* Creation flags: without window (?). */
NULL, /* Use parent process environment. */
NULL, /* Use parent process working directory. */
&si, /* Startup information structure. */
&pi)) /* Process information structure. */
goto finito;
/* These handles listen to the child process. */
CloseHandle(my_pipein[0]);
my_pipein[0] = INVALID_HANDLE_VALUE;
CloseHandle(my_pipeout[1]);
my_pipeout[1] = INVALID_HANDLE_VALUE;
CloseHandle(my_pipeerr[1]);
my_pipeerr[1] = INVALID_HANDLE_VALUE;
if (my_popenmode == 'r')
fptr = _fdopen(_open_osfhandle((long)my_pipeout[0], _O_BINARY), "r");
else
fptr = _fdopen(_open_osfhandle((long)my_pipein[1], _O_BINARY), "w");
finito:
if (!fptr) {
if (my_pipein[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipein[0]);
if (my_pipein[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipein[1]);
if (my_pipeout[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipeout[0]);
if (my_pipeout[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipeout[1]);
if (my_pipeerr[0] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipeerr[0]);
if (my_pipeerr[1] != INVALID_HANDLE_VALUE)
CloseHandle(my_pipeerr[1]);
}
return fptr;
}
/* Replacement for pclose() under Windows. */
int pt_pclose(FILE *handle)
{
if (handle) {
(void)fclose(handle);
CloseHandle(my_pipeerr[0]);
if (my_popenmode == 'r')
CloseHandle(my_pipein[1]);
else
CloseHandle(my_pipeout[0]);
return 0;
}
return -1;
}
/* vim: set ts=2 sw=2 et : */
- References:
- IUP, popen & processes, Bob Hibberdine
- RE: IUP, popen & processes, Antonio Scuri
- RE: IUP, popen & processes, Bob Hibberdine
- RE: IUP, popen & processes, Antonio Scuri
- Re: IUP, popen & processes, steve donovan
- RE: IUP, popen & processes, Antonio Scuri
- RE: IUP, popen & processes, Bob Hibberdine