From fe4c2d2c93cd000ad108e7b045ea7f553b09f0ce Mon Sep 17 00:00:00 2001 From: J. Ali Harlow Date: Tue, 18 Aug 2009 14:11:42 +0100 Subject: [PATCH] Add new function to spawn child --- configure.ac | 11 +++ whelk/Makefile.am | 5 +- whelk/_whelk.h | 24 +++++++ whelk/spawn.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++ whelk/string.c | 37 +++++++++++ whelk/wait.c | 47 ++++++++++++++ whelk/whelk.c | 1 + 7 files changed, 298 insertions(+), 2 deletions(-) create mode 100644 whelk/spawn.c create mode 100644 whelk/string.c create mode 100644 whelk/wait.c diff --git a/configure.ac b/configure.ac index 1f09a84..feb7fa0 100644 --- a/configure.ac +++ b/configure.ac @@ -22,6 +22,17 @@ case $host_os in esac AM_CONDITIONAL(WHELK_MINGW,[test -n "$host_mingw"]) +# libtool versioning - this applies to all libraries in this package +# +# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details +# +LT_CURRENT=1 +LT_REVISION=0 +LT_AGE=1 +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) + ################################################## # Checks for programs. ################################################## diff --git a/whelk/Makefile.am b/whelk/Makefile.am index c643597..301a63e 100644 --- a/whelk/Makefile.am +++ b/whelk/Makefile.am @@ -6,11 +6,12 @@ INCLUDES=-I$(top_srcdir) lib_LTLIBRARIES=libwhelk.la -libwhelk_la_SOURCES=whelk.c whelk.h _whelk.h +libwhelk_la_SOURCES=whelk.c whelk.h _whelk.h string.c if WHELK_MINGW libwhelk_la_SOURCES+=unicode.c get_folder_path.c registry.c reg_keys.c \ - shelllink.c + shelllink.c spawn.c wait.c endif +libwhelk_la_LDFLAGS=-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) pkginclude_HEADERS=whelk.h diff --git a/whelk/_whelk.h b/whelk/_whelk.h index 9d0e503..5d018d7 100644 --- a/whelk/_whelk.h +++ b/whelk/_whelk.h @@ -1,5 +1,6 @@ #include #include +#include /* For ssize_t */ #ifdef __WIN32__ #include #endif @@ -12,11 +13,29 @@ lua_settable(L,-3); \ } while(0) +struct whelk_string { + size_t len,alloc; + char *buffer; +}; + +#ifdef __WIN32__ +struct whelk_wait { + int no_objects; + HANDLE *objects; +}; +#endif + int whelk_perror(lua_State *L,const char *s); int whelk_get_folder_path(lua_State *L); void whelk_open_get_folder_path(lua_State *L); void whelk_open_reg_keys(lua_State *L); int whelk_create_short_cut(lua_State *L); +int whelk_spawn(lua_State *L); + +char *whelk_string_prealloc(struct whelk_string *string,size_t len); +void whelk_string_seek(struct whelk_string *string,ssize_t offset); +char *whelk_string_finalize(struct whelk_string *string); +void whelk_string_free(struct whelk_string *string); #ifdef __WIN32__ char *whelk_utf16_to_utf8(const WCHAR *ucs2,int len); @@ -29,4 +48,9 @@ HRESULT whelk_reg_get_value(HKEY key,const char *subkey,const char *value, HRESULT whelk_reg_set_value(HKEY key,const char *subkey,const char *value, DWORD type,const void *data,int nb); HRESULT whelk_reg_delete_key(HKEY key,const char *subkey); + +int whelk_wait_add_object(struct whelk_wait *wait,HANDLE object); +void whelk_wait_remove_object(struct whelk_wait *wait,HANDLE object); +HANDLE whelk_wait_poll(struct whelk_wait *wait); +void whelk_wait_free(struct whelk_wait *wait); #endif diff --git a/whelk/spawn.c b/whelk/spawn.c new file mode 100644 index 0000000..ee30c6b --- /dev/null +++ b/whelk/spawn.c @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include "_whelk.h" + +static void whelk_close_pipe(HANDLE pipe[2]) +{ + CloseHandle(pipe[0]); + CloseHandle(pipe[1]); +} + +/* Create a pipe with one end inheritable */ + +static BOOL whelk_child_pipe(HANDLE pipe[2],BOOL to_child) +{ + SECURITY_ATTRIBUTES sa={0}; + HANDLE h; + sa.nLength=sizeof(sa); + sa.bInheritHandle=TRUE; + if (!CreatePipe(pipe+0,pipe+1,&sa,0)) + return FALSE; + if (!DuplicateHandle(GetCurrentProcess(),pipe[to_child?0:1], + GetCurrentProcess(),&h,0,TRUE,DUPLICATE_SAME_ACCESS)) + { + whelk_close_pipe(pipe); + return FALSE; + } + CloseHandle(pipe[to_child?0:1]); + pipe[to_child?0:1]=h; + return TRUE; +} + +/* + * spawn(application_name,command-line,working-directory,standard-input) + * + * Returns two strings (the data written to standard output and standard error + * respectively) and the exit code. + */ + +int whelk_spawn(lua_State *L) +{ + STARTUPINFOW si={0}; + PROCESS_INFORMATION pi={0}; + WCHAR *application_name=whelk_utf8_to_utf16(luaL_checkstring(L,1),-1); + WCHAR *command_line=whelk_utf8_to_utf16(luaL_checkstring(L,2),-1); + WCHAR *working_directory=whelk_utf8_to_utf16(luaL_checkstring(L,3),-1); + size_t stdin_len; + const char *standard_input=luaL_checklstring(L,4,&stdin_len); + struct whelk_string standard_output={0}; + struct whelk_string standard_error={0}; + char *buffer; + HANDLE stdin_pipe[2],stdout_pipe[2],stderr_pipe[2],h; + struct whelk_wait wait={0}; + DWORD nb; + int exitcode; + if (!whelk_child_pipe(stdin_pipe,TRUE)) + { + free(application_name); + free(command_line); + free(working_directory); + errno=EMFILE; + return whelk_perror(L,NULL); + } + if (!whelk_child_pipe(stdout_pipe,FALSE)) + { + free(application_name); + free(command_line); + free(working_directory); + whelk_close_pipe(stdin_pipe); + errno=EMFILE; + return whelk_perror(L,NULL); + } + if (!whelk_child_pipe(stderr_pipe,FALSE)) + { + free(application_name); + free(command_line); + free(working_directory); + whelk_close_pipe(stdin_pipe); + whelk_close_pipe(stdout_pipe); + errno=EMFILE; + return whelk_perror(L,NULL); + } + si.cb=sizeof(si); + si.dwFlags=STARTF_USESTDHANDLES; + si.hStdInput=stdin_pipe[0]; + si.hStdOutput=stdout_pipe[1]; + si.hStdError=stderr_pipe[1]; + if (!CreateProcessW(application_name,command_line,NULL,NULL,TRUE, + CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS,NULL,working_directory, + &si,&pi)) + { + free(application_name); + free(command_line); + free(working_directory); + whelk_close_pipe(stdin_pipe); + whelk_close_pipe(stdout_pipe); + whelk_close_pipe(stderr_pipe); + errno=EACCES; + return whelk_perror(L,NULL); + } + free(application_name); + free(command_line); + free(working_directory); + CloseHandle(stdout_pipe[1]); + CloseHandle(stderr_pipe[1]); + whelk_wait_add_object(&wait,stdout_pipe[0]); + whelk_wait_add_object(&wait,stderr_pipe[0]); + whelk_wait_add_object(&wait,pi.hProcess); + if (standard_input) + whelk_wait_add_object(&wait,stdin_pipe[1]); + else + { + whelk_close_pipe(stdin_pipe); + stdin_pipe[1]=INVALID_HANDLE_VALUE; + } + for(;;) + { + h=whelk_wait_poll(&wait); + if (h==INVALID_HANDLE_VALUE) + break; + else if (h==stdout_pipe[0]) + { + buffer=whelk_string_prealloc(&standard_output,1024); + if (ReadFile(stdout_pipe[0],buffer,1024,&nb,NULL)) + whelk_string_seek(&standard_output,nb); + else + whelk_wait_remove_object(&wait,stdout_pipe[0]); + } + else if (h==stderr_pipe[0]) + { + buffer=whelk_string_prealloc(&standard_error,1024); + if (ReadFile(stderr_pipe[0],buffer,1024,&nb,NULL)) + whelk_string_seek(&standard_error,nb); + else + whelk_wait_remove_object(&wait,stderr_pipe[0]); + } + else if (h==pi.hProcess) + break; + else if (h==stdin_pipe[1]) + { + nb=max(stdin_len,512); + WriteFile(stdin_pipe[1],standard_input,nb,NULL,NULL); + standard_input+=nb; + stdin_len-=nb; + if (!stdin_len) + { + whelk_wait_remove_object(&wait,stdin_pipe[1]); + whelk_close_pipe(stdin_pipe); + standard_input=NULL; + } + } + } + whelk_wait_free(&wait); + if (standard_input) + whelk_close_pipe(stdin_pipe); + if (GetExitCodeProcess(pi.hProcess,&nb)) + exitcode=(int)nb; + else + exitcode=-1; + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + CloseHandle(stdout_pipe[0]); + CloseHandle(stderr_pipe[0]); + whelk_string_finalize(&standard_output); + whelk_string_finalize(&standard_error); + lua_pushlstring(L,standard_output.buffer,standard_output.len); + lua_pushlstring(L,standard_error.buffer,standard_error.len); + whelk_string_free(&standard_output); + whelk_string_free(&standard_error); + lua_pushnumber(L,exitcode); + return 3; +} diff --git a/whelk/string.c b/whelk/string.c new file mode 100644 index 0000000..6b33ea0 --- /dev/null +++ b/whelk/string.c @@ -0,0 +1,37 @@ +#include +#include "_whelk.h" + +char *whelk_string_prealloc(struct whelk_string *string,size_t len) +{ + char *new; + if (string->alloclen+len) + { + new=realloc(string->buffer,string->len+len); + if (!new) + return NULL; + string->buffer=new; + string->alloc=string->len+len; + } + return string->buffer+string->len; +} + +void whelk_string_seek(struct whelk_string *string,ssize_t offset) +{ + if (offset>=0 || string->len>=(size_t)-offset) + string->len+=offset; + else + string->len=0; +} + +char *whelk_string_finalize(struct whelk_string *string) +{ + whelk_string_prealloc(string,1); + string->buffer[string->len]='\0'; + return string->buffer; +} + +void whelk_string_free(struct whelk_string *string) +{ + free(string->buffer); + memset(string,0,sizeof(*string)); +} diff --git a/whelk/wait.c b/whelk/wait.c new file mode 100644 index 0000000..ed0f4b9 --- /dev/null +++ b/whelk/wait.c @@ -0,0 +1,47 @@ +#include +#include +#include "_whelk.h" + +int whelk_wait_add_object(struct whelk_wait *wait,HANDLE object) +{ + HANDLE *new; + new=realloc(wait->objects,wait->no_objects+1); + if (!new) + return -1; + wait->objects=new; + wait->objects[wait->no_objects++]=object; + return 0; +} + +void whelk_wait_remove_object(struct whelk_wait *wait,HANDLE object) +{ + int i; + for(i=0;ino_objects;i++) + if (wait->objects[i]==object) + break; + if (i!=wait->no_objects-1) + memcpy(wait->objects+i,wait->objects+i+1, + (wait->no_objects-i-1)*sizeof(*wait->objects)); + wait->no_objects--; +} + +/* + * Where two or more objects are ready, + * the object added first is returned. + */ + +HANDLE whelk_wait_poll(struct whelk_wait *wait) +{ + DWORD retval; + retval=WaitForMultipleObjects(wait->no_objects,wait->objects,FALSE, + INFINITE); + if (retval>=WAIT_OBJECT_0 && retval-WAIT_OBJECT_0no_objects) + return wait->objects[retval-WAIT_OBJECT_0]; + else + return INVALID_HANDLE_VALUE; +} + +void whelk_wait_free(struct whelk_wait *wait) +{ + free(wait->objects); +} diff --git a/whelk/whelk.c b/whelk/whelk.c index dc3c155..ab32cf4 100644 --- a/whelk/whelk.c +++ b/whelk/whelk.c @@ -33,6 +33,7 @@ static int whelk_unsupported(lua_State *L) static const luaL_reg whelk_functions[]={ { "GetFolderPath",WIN32_ONLY(whelk_get_folder_path) }, { "CreateShortCut",WIN32_ONLY(whelk_create_short_cut) }, + { "Spawn",WIN32_ONLY(whelk_spawn) }, { NULL } }; -- 1.7.1