whelk/spawn.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Aug 18 14:11:42 2009 +0100 (2009-08-18)
changeset 3 47fa028d40b3
child 6 62d265f584c4
permissions -rw-r--r--
Add new function to spawn child
     1 #include <stdlib.h>
     2 #include <errno.h>
     3 #include <windows.h>
     4 #include <lua.h>
     5 #include <lualib.h>
     6 #include <lauxlib.h>
     7 #include "_whelk.h"
     8 
     9 static void whelk_close_pipe(HANDLE pipe[2])
    10 {
    11     CloseHandle(pipe[0]);
    12     CloseHandle(pipe[1]);
    13 }
    14 
    15 /* Create a pipe with one end inheritable */
    16 
    17 static BOOL whelk_child_pipe(HANDLE pipe[2],BOOL to_child)
    18 {
    19     SECURITY_ATTRIBUTES sa={0};
    20     HANDLE h;
    21     sa.nLength=sizeof(sa);
    22     sa.bInheritHandle=TRUE;
    23     if (!CreatePipe(pipe+0,pipe+1,&sa,0))
    24 	return FALSE;
    25     if (!DuplicateHandle(GetCurrentProcess(),pipe[to_child?0:1],
    26       GetCurrentProcess(),&h,0,TRUE,DUPLICATE_SAME_ACCESS))
    27     {
    28 	whelk_close_pipe(pipe);
    29 	return FALSE;
    30     }
    31     CloseHandle(pipe[to_child?0:1]);
    32     pipe[to_child?0:1]=h;
    33     return TRUE;
    34 }
    35 
    36 /*
    37  * spawn(application_name,command-line,working-directory,standard-input)
    38  *
    39  * Returns two strings (the data written to standard output and standard error
    40  * respectively) and the exit code.
    41  */
    42 
    43 int whelk_spawn(lua_State *L)
    44 {
    45     STARTUPINFOW si={0};
    46     PROCESS_INFORMATION pi={0};
    47     WCHAR *application_name=whelk_utf8_to_utf16(luaL_checkstring(L,1),-1);
    48     WCHAR *command_line=whelk_utf8_to_utf16(luaL_checkstring(L,2),-1);
    49     WCHAR *working_directory=whelk_utf8_to_utf16(luaL_checkstring(L,3),-1);
    50     size_t stdin_len;
    51     const char *standard_input=luaL_checklstring(L,4,&stdin_len);
    52     struct whelk_string standard_output={0};
    53     struct whelk_string standard_error={0};
    54     char *buffer;
    55     HANDLE stdin_pipe[2],stdout_pipe[2],stderr_pipe[2],h;
    56     struct whelk_wait wait={0};
    57     DWORD nb;
    58     int exitcode;
    59     if (!whelk_child_pipe(stdin_pipe,TRUE))
    60     {
    61 	free(application_name);
    62 	free(command_line);
    63 	free(working_directory);
    64 	errno=EMFILE;
    65 	return whelk_perror(L,NULL);
    66     }
    67     if (!whelk_child_pipe(stdout_pipe,FALSE))
    68     {
    69 	free(application_name);
    70 	free(command_line);
    71 	free(working_directory);
    72 	whelk_close_pipe(stdin_pipe);
    73 	errno=EMFILE;
    74 	return whelk_perror(L,NULL);
    75     }
    76     if (!whelk_child_pipe(stderr_pipe,FALSE))
    77     {
    78 	free(application_name);
    79 	free(command_line);
    80 	free(working_directory);
    81 	whelk_close_pipe(stdin_pipe);
    82 	whelk_close_pipe(stdout_pipe);
    83 	errno=EMFILE;
    84 	return whelk_perror(L,NULL);
    85     }
    86     si.cb=sizeof(si);
    87     si.dwFlags=STARTF_USESTDHANDLES;
    88     si.hStdInput=stdin_pipe[0];
    89     si.hStdOutput=stdout_pipe[1];
    90     si.hStdError=stderr_pipe[1];
    91     if (!CreateProcessW(application_name,command_line,NULL,NULL,TRUE,
    92       CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS,NULL,working_directory,
    93       &si,&pi))
    94     {
    95 	free(application_name);
    96 	free(command_line);
    97 	free(working_directory);
    98 	whelk_close_pipe(stdin_pipe);
    99 	whelk_close_pipe(stdout_pipe);
   100 	whelk_close_pipe(stderr_pipe);
   101 	errno=EACCES;
   102 	return whelk_perror(L,NULL);
   103     }
   104     free(application_name);
   105     free(command_line);
   106     free(working_directory);
   107     CloseHandle(stdout_pipe[1]);
   108     CloseHandle(stderr_pipe[1]);
   109     whelk_wait_add_object(&wait,stdout_pipe[0]);
   110     whelk_wait_add_object(&wait,stderr_pipe[0]);
   111     whelk_wait_add_object(&wait,pi.hProcess);
   112     if (standard_input)
   113 	whelk_wait_add_object(&wait,stdin_pipe[1]);
   114     else
   115     {
   116 	whelk_close_pipe(stdin_pipe);
   117 	stdin_pipe[1]=INVALID_HANDLE_VALUE;
   118     }
   119     for(;;)
   120     {
   121 	h=whelk_wait_poll(&wait);
   122 	if (h==INVALID_HANDLE_VALUE)
   123 	    break;
   124 	else if (h==stdout_pipe[0])
   125 	{
   126 	    buffer=whelk_string_prealloc(&standard_output,1024);
   127 	    if (ReadFile(stdout_pipe[0],buffer,1024,&nb,NULL))
   128 		whelk_string_seek(&standard_output,nb);
   129 	    else
   130 		whelk_wait_remove_object(&wait,stdout_pipe[0]);
   131 	}
   132 	else if (h==stderr_pipe[0])
   133 	{
   134 	    buffer=whelk_string_prealloc(&standard_error,1024);
   135 	    if (ReadFile(stderr_pipe[0],buffer,1024,&nb,NULL))
   136 		whelk_string_seek(&standard_error,nb);
   137 	    else
   138 		whelk_wait_remove_object(&wait,stderr_pipe[0]);
   139 	}
   140 	else if (h==pi.hProcess)
   141 	    break;
   142 	else if (h==stdin_pipe[1])
   143 	{
   144 	    nb=max(stdin_len,512);
   145 	    WriteFile(stdin_pipe[1],standard_input,nb,NULL,NULL);
   146 	    standard_input+=nb;
   147 	    stdin_len-=nb;
   148 	    if (!stdin_len)
   149 	    {
   150 		whelk_wait_remove_object(&wait,stdin_pipe[1]);
   151 		whelk_close_pipe(stdin_pipe);
   152 		standard_input=NULL;
   153 	    }
   154 	}
   155     }
   156     whelk_wait_free(&wait);
   157     if (standard_input)
   158 	whelk_close_pipe(stdin_pipe);
   159     if (GetExitCodeProcess(pi.hProcess,&nb))
   160 	exitcode=(int)nb;
   161     else
   162 	exitcode=-1;
   163     CloseHandle(pi.hThread);
   164     CloseHandle(pi.hProcess);
   165     CloseHandle(stdout_pipe[0]);
   166     CloseHandle(stderr_pipe[0]);
   167     whelk_string_finalize(&standard_output);
   168     whelk_string_finalize(&standard_error);
   169     lua_pushlstring(L,standard_output.buffer,standard_output.len);
   170     lua_pushlstring(L,standard_error.buffer,standard_error.len);
   171     whelk_string_free(&standard_output);
   172     whelk_string_free(&standard_error);
   173     lua_pushnumber(L,exitcode);
   174     return 3;
   175 }