123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- /*
- * Copyright (C)2005-2015 Haxe Foundation
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
- // ported from NekoVM
- #include <caml/alloc.h>
- #include <caml/memory.h>
- #include <caml/callback.h>
- #include <caml/mlvalues.h>
- #include <caml/fail.h>
- #ifdef _WIN32
- # include <windows.h>
- #else
- # include <sys/types.h>
- # include <signal.h>
- # include <unistd.h>
- # include <errno.h>
- # include <string.h>
- # ifndef __APPLE__
- # if defined(__FreeBSD__) || defined(__DragonFly__)
- # include <sys/wait.h>
- # else
- # include <wait.h>
- # endif
- # endif
- #endif
- #ifdef _WIN32
- # define POSIX_LABEL(name)
- # define HANDLE_EINTR(label)
- # define HANDLE_FINTR(f,label)
- #else
- # include <errno.h>
- # define POSIX_LABEL(name) name:
- # define HANDLE_EINTR(label) if( errno == EINTR ) goto label
- # define HANDLE_FINTR(f,label) if( ferror(f) && errno == EINTR ) goto label
- #endif
- // --- neko-to-caml api --
- #define val_check(v,t)
- #define val_check_kind(v,k)
- #define val_data(v) v
- #define val_array_size(v) Wosize_val(v)
- #define val_array_ptr(v) (&Field(v,0))
- #define val_string(v) String_val(v)
- #define val_strlen(v) caml_string_length(v)
- #define alloc_abstract(_,data) ((value)data)
- #define alloc_int(i) Val_int(i)
- #define val_gc(v,callb)
- #define val_null Val_int(0)
- #define val_some(v) Field(v,0)
- #define val_int(v) Int_val(v)
- #define DEFINE_KIND(_)
- #define neko_error() failwith(__FUNCTION__)
- static value alloc_private( int size ) {
- return alloc((size + sizeof(value) - 1) / sizeof(value), Abstract_tag);
- }
- // --- buffer api
- #define EXTERN
- typedef struct _stringitem {
- char *str;
- int size;
- int len;
- struct _stringitem *next;
- } * stringitem;
- struct _buffer {
- int totlen;
- int blen;
- stringitem data;
- };
- typedef struct _buffer *buffer;
- static void buffer_append_new( buffer b, const char *s, int len ) {
- int size;
- stringitem it;
- while( b->totlen >= (b->blen << 2) )
- b->blen <<= 1;
- size = (len < b->blen)?b->blen:len;
- it = (stringitem)malloc(sizeof(struct _stringitem));
- it->str = (char*)malloc(size);
- memcpy(it->str,s,len);
- it->size = size;
- it->len = len;
- it->next = b->data;
- b->data = it;
- }
- EXTERN void buffer_append_sub( buffer b, const char *s, int len ) {
- stringitem it;
- if( s == NULL || len <= 0 )
- return;
- b->totlen += len;
- it = b->data;
- if( it ) {
- int free = it->size - it->len;
- if( free >= len ) {
- memcpy(it->str + it->len,s,len);
- it->len += len;
- return;
- } else {
- memcpy(it->str + it->len,s,free);
- it->len += free;
- s += free;
- len -= free;
- }
- }
- buffer_append_new(b,s,len);
- }
- EXTERN void buffer_append_str( buffer b, const char *s ) {
- if( s == NULL )
- return;
- buffer_append_sub(b,s,strlen(s));
- }
- EXTERN buffer alloc_buffer( const char *init ) {
- buffer b = (buffer)malloc(sizeof(struct _buffer));
- b->totlen = 0;
- b->blen = 16;
- b->data = NULL;
- if( init )
- buffer_append_str(b,init);
- return b;
- }
- EXTERN void buffer_append_char( buffer b, char c ) {
- stringitem it;
- b->totlen++;
- it = b->data;
- if( it && it->len != it->size ) {
- it->str[it->len++] = c;
- return;
- }
- buffer_append_new(b,&c,1);
- }
- EXTERN char *buffer_to_string( buffer b ) {
- char *v = (char*)malloc(b->totlen + 1);
- stringitem it = b->data;
- char *s = v + b->totlen;
- *s = 0;
- while( it != NULL ) {
- stringitem tmp;
- s -= it->len;
- memcpy(s,it->str,it->len);
- tmp = it->next;
- free(it->str);
- free(it);
- it = tmp;
- }
- free(b);
- return v;
- }
- EXTERN int buffer_length( buffer b ) {
- return b->totlen;
- }
- // ---------------
- #include <stdio.h>
- #include <stdlib.h>
- typedef struct {
- #ifdef _WIN32
- HANDLE oread;
- HANDLE eread;
- HANDLE iwrite;
- PROCESS_INFORMATION pinf;
- #else
- int oread;
- int eread;
- int iwrite;
- int pid;
- #endif
- } vprocess;
- DEFINE_KIND(k_process);
- #define val_process(v) ((vprocess*)val_data(v))
- /**
- <doc>
- <h1>Process</h1>
- <p>
- An API for starting and communication with sub processes.
- </p>
- </doc>
- **/
- #ifndef _WIN32
- static int do_close( int fd ) {
- POSIX_LABEL(close_again);
- if( close(fd) != 0 ) {
- HANDLE_EINTR(close_again);
- return 1;
- }
- return 0;
- }
- #endif
- static void free_process( value vp ) {
- vprocess *p = val_process(vp);
- # ifdef _WIN32
- CloseHandle(p->eread);
- CloseHandle(p->oread);
- CloseHandle(p->iwrite);
- CloseHandle(p->pinf.hProcess);
- CloseHandle(p->pinf.hThread);
- # else
- do_close(p->eread);
- do_close(p->oread);
- do_close(p->iwrite);
- # endif
- }
- /**
- process_run : cmd:string -> args:string array option -> 'process
- <doc>
- Start a process using a command and the specified arguments.
- When args is not null, cmd and args will be auto-quoted/escaped.
- If no auto-quoting/escaping is desired, you should append necessary
- arguments to cmd as if it is inputted to the shell directly, and pass
- null as args.
- </doc>
- **/
- CAMLprim value process_run( value cmd, value vargs ) {
- CAMLparam2(cmd,vargs);
- int i, isRaw;
- vprocess *p;
- val_check(cmd,string);
- isRaw = vargs == val_null;
- if (!isRaw) {
- val_check(vargs,array);
- vargs = val_some(vargs);
- }
- # ifdef _WIN32
- {
- SECURITY_ATTRIBUTES sattr;
- STARTUPINFO sinf;
- HANDLE proc = GetCurrentProcess();
- HANDLE oread,eread,iwrite;
- // creates commandline
- buffer b = alloc_buffer(NULL);
- char *sargs;
- if (isRaw) {
- char* cmdexe;
- buffer_append_char(b,'"');
- cmdexe = getenv("COMSPEC");
- if (!cmdexe) cmdexe = "cmd.exe";
- buffer_append_str(b,cmdexe);
- buffer_append_char(b,'"');
- buffer_append_str(b,"/C \"");
- buffer_append_str(b,val_string(cmd));
- buffer_append_char(b,'"');
- } else {
- buffer_append_char(b,'"');
- buffer_append_str(b,val_string(cmd));
- buffer_append_char(b,'"');
- for(i=0;i<val_array_size(vargs);i++) {
- value v = val_array_ptr(vargs)[i];
- int j,len;
- unsigned int bs_count = 0;
- unsigned int k;
- val_check(v,string);
- len = val_strlen(v);
- buffer_append_str(b," \"");
- for(j=0;j<len;j++) {
- char c = val_string(v)[j];
- switch( c ) {
- case '"':
- // Double backslashes.
- for (k=0;k<bs_count*2;k++) {
- buffer_append_char(b,'\\');
- }
- bs_count = 0;
- buffer_append_str(b, "\\\"");
- break;
- case '\\':
- // Don't know if we need to double yet.
- bs_count++;
- break;
- default:
- // Normal char
- for (k=0;k<bs_count;k++) {
- buffer_append_char(b,'\\');
- }
- bs_count = 0;
- buffer_append_char(b,c);
- break;
- }
- }
- // Add remaining backslashes, if any.
- for (k=0;k<bs_count*2;k++) {
- buffer_append_char(b,'\\');
- }
- buffer_append_char(b,'"');
- }
- }
- sargs = buffer_to_string(b);
- p = (vprocess*)alloc_private(sizeof(vprocess));
- // startup process
- sattr.nLength = sizeof(sattr);
- sattr.bInheritHandle = TRUE;
- sattr.lpSecurityDescriptor = NULL;
- memset(&sinf,0,sizeof(sinf));
- sinf.cb = sizeof(sinf);
- sinf.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
- sinf.wShowWindow = SW_HIDE;
- CreatePipe(&oread,&sinf.hStdOutput,&sattr,0);
- CreatePipe(&eread,&sinf.hStdError,&sattr,0);
- CreatePipe(&sinf.hStdInput,&iwrite,&sattr,0);
- DuplicateHandle(proc,oread,proc,&p->oread,0,FALSE,DUPLICATE_SAME_ACCESS);
- DuplicateHandle(proc,eread,proc,&p->eread,0,FALSE,DUPLICATE_SAME_ACCESS);
- DuplicateHandle(proc,iwrite,proc,&p->iwrite,0,FALSE,DUPLICATE_SAME_ACCESS);
- CloseHandle(oread);
- CloseHandle(eread);
- CloseHandle(iwrite);
- if( !CreateProcess(NULL,val_string(sargs),NULL,NULL,TRUE,0,NULL,NULL,&sinf,&p->pinf) ) {
- CloseHandle(p->eread);
- CloseHandle(p->oread);
- CloseHandle(p->iwrite);
- free(sargs);
- neko_error();
- }
- free(sargs);
- // close unused pipes
- CloseHandle(sinf.hStdOutput);
- CloseHandle(sinf.hStdError);
- CloseHandle(sinf.hStdInput);
- }
- # else
- char **argv;
- if (isRaw) {
- argv = (char**)alloc_private(sizeof(char*)*4);
- argv[0] = "/bin/sh";
- argv[1] = "-c";
- argv[2] = val_string(cmd);
- argv[3] = NULL;
- } else {
- argv = (char**)alloc_private(sizeof(char*)*(val_array_size(vargs)+2));
- argv[0] = val_string(cmd);
- for(i=0;i<val_array_size(vargs);i++) {
- value v = val_array_ptr(vargs)[i];
- val_check(v,string);
- argv[i+1] = val_string(v);
- }
- argv[i+1] = NULL;
- }
- int input[2], output[2], error[2];
- if( pipe(input) || pipe(output) || pipe(error) )
- neko_error();
- p = (vprocess*)alloc_private(sizeof(vprocess));
- p->pid = fork();
- if( p->pid == -1 ) {
- do_close(input[0]);
- do_close(input[1]);
- do_close(output[0]);
- do_close(output[1]);
- do_close(error[0]);
- do_close(error[1]);
- neko_error();
- }
- // child
- if( p->pid == 0 ) {
- close(input[1]);
- close(output[0]);
- close(error[0]);
- dup2(input[0],0);
- dup2(output[1],1);
- dup2(error[1],2);
- execvp(argv[0],argv);
- fprintf(stderr,"Command not found : %s\n",val_string(cmd));
- exit(1);
- }
- // parent
- do_close(input[0]);
- do_close(output[1]);
- do_close(error[1]);
- p->iwrite = input[1];
- p->oread = output[0];
- p->eread = error[0];
- # endif
- {
- CAMLlocal1(vp);
- vp = alloc_abstract(k_process,p);
- val_gc(vp,free_process);
- CAMLreturn(vp);
- }
- }
- #define CHECK_ARGS() \
- vprocess *p; \
- val_check_kind(vp,k_process); \
- val_check(str,string); \
- val_check(pos,int); \
- val_check(len,int); \
- if( val_int(pos) < 0 || val_int(len) < 0 || val_int(pos) + val_int(len) > val_strlen(str) ) \
- neko_error(); \
- p = val_process(vp); \
- /**
- process_stdout_read : 'process -> buf:string -> pos:int -> len:int -> int
- <doc>
- Read up to [len] bytes in [buf] starting at [pos] from the process stdout.
- Returns the number of bytes read this way. Raise an exception if this
- process stdout is closed and no more data is available for reading.
- </doc>
- **/
- CAMLprim value process_stdout_read( value vp, value str, value pos, value len ) {
- CHECK_ARGS();
- # ifdef _WIN32
- {
- DWORD nbytes;
- if( !ReadFile(p->oread,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) )
- neko_error();
- return alloc_int(nbytes);
- }
- # else
- int nbytes;
- POSIX_LABEL(stdout_read_again);
- nbytes = read(p->oread,val_string(str)+val_int(pos),val_int(len));
- if( nbytes < 0 ) {
- HANDLE_EINTR(stdout_read_again);
- neko_error();
- }
- if( nbytes == 0 )
- neko_error();
- return alloc_int(nbytes);
- # endif
- }
- /**
- process_stderr_read : 'process -> buf:string -> pos:int -> len:int -> int
- <doc>
- Read up to [len] bytes in [buf] starting at [pos] from the process stderr.
- Returns the number of bytes read this way. Raise an exception if this
- process stderr is closed and no more data is available for reading.
- </doc>
- **/
- CAMLprim value process_stderr_read( value vp, value str, value pos, value len ) {
- CHECK_ARGS();
- # ifdef _WIN32
- {
- DWORD nbytes;
- if( !ReadFile(p->eread,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) )
- neko_error();
- return alloc_int(nbytes);
- }
- # else
- int nbytes;
- POSIX_LABEL(stderr_read_again);
- nbytes = read(p->eread,val_string(str)+val_int(pos),val_int(len));
- if( nbytes < 0 ) {
- HANDLE_EINTR(stderr_read_again);
- neko_error();
- }
- if( nbytes == 0 )
- neko_error();
- return alloc_int(nbytes);
- # endif
- }
- /**
- process_stdin_write : 'process -> buf:string -> pos:int -> len:int -> int
- <doc>
- Write up to [len] bytes from [buf] starting at [pos] to the process stdin.
- Returns the number of bytes writen this way. Raise an exception if this
- process stdin is closed.
- </doc>
- **/
- CAMLprim value process_stdin_write( value vp, value str, value pos, value len ) {
- CHECK_ARGS();
- # ifdef _WIN32
- {
- DWORD nbytes;
- if( !WriteFile(p->iwrite,val_string(str)+val_int(pos),val_int(len),&nbytes,NULL) )
- neko_error();
- return alloc_int(nbytes);
- }
- # else
- int nbytes;
- POSIX_LABEL(stdin_write_again);
- nbytes = write(p->iwrite,val_string(str)+val_int(pos),val_int(len));
- if( nbytes == -1 ) {
- HANDLE_EINTR(stdin_write_again);
- neko_error();
- }
- return alloc_int(nbytes);
- # endif
- }
- /**
- process_stdin_close : 'process -> void
- <doc>
- Close the process standard input.
- </doc>
- **/
- CAMLprim value process_stdin_close( value vp ) {
- vprocess *p;
- val_check_kind(vp,k_process);
- p = val_process(vp);
- # ifdef _WIN32
- if( !CloseHandle(p->iwrite) )
- neko_error();
- # else
- if( do_close(p->iwrite) )
- neko_error();
- p->iwrite = -1;
- # endif
- return val_null;
- }
- /**
- process_exit : 'process -> int
- <doc>
- Wait until the process terminate, then returns its exit code.
- </doc>
- **/
- CAMLprim value process_exit( value vp ) {
- vprocess *p;
- val_check_kind(vp,k_process);
- p = val_process(vp);
- # ifdef _WIN32
- {
- DWORD rval;
- WaitForSingleObject(p->pinf.hProcess,INFINITE);
- if( !GetExitCodeProcess(p->pinf.hProcess,&rval) )
- neko_error();
- return alloc_int(rval);
- }
- # else
- int rval;
- while( waitpid(p->pid,&rval,0) != p->pid ) {
- if( errno == EINTR )
- continue;
- neko_error();
- }
- if( !WIFEXITED(rval) )
- neko_error();
- return alloc_int(WEXITSTATUS(rval));
- # endif
- }
- /**
- process_pid : 'process -> int
- <doc>
- Returns the process id.
- </doc>
- **/
- CAMLprim value process_pid( value vp ) {
- vprocess *p;
- val_check_kind(vp,k_process);
- p = val_process(vp);
- # ifdef _WIN32
- return alloc_int(p->pinf.dwProcessId);
- # else
- return alloc_int(p->pid);
- # endif
- }
- /**
- process_close : 'process -> void
- <doc>
- Close the process I/O.
- </doc>
- **/
- CAMLprim value process_close( value vp ) {
- val_check_kind(vp,k_process);
- free_process(vp);
- //val_kind(vp) = NULL;
- //val_gc(vp,NULL);
- return val_null;
- }
- /**
- process_kill : 'process -> void
- <doc>
- Terminates a running process.
- </doc>
- **/
- CAMLprim value process_kill( value vp ) {
- val_check_kind(vp,k_process);
- # ifdef _WIN32
- TerminateProcess(val_process(vp)->pinf.hProcess,-1);
- # else
- kill(val_process(vp)->pid,9);
- # endif
- return val_null;
- }
- /* ************************************************************************ */
|