/* ************************************************************************ */ /* */ /* Neko Standard Library */ /* Copyright (c)2005-2007 Motion-Twin */ /* */ /* This library is free software; you can redistribute it and/or */ /* modify it under the terms of the GNU Lesser General Public */ /* License as published by the Free Software Foundation; either */ /* version 2.1 of the License, or (at your option) any later version. */ /* */ /* This library is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ /* Lesser General Public License or the LICENSE file for more details. */ /* */ /* ************************************************************************ */ #include #ifdef NEKO_WINDOWS # include #else # include # include # include # ifndef NEKO_MAC # include # endif #endif #include #include typedef struct { #ifdef NEKO_WINDOWS 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)) /**

Process

An API for starting and communication with sub processes.

**/ #ifndef NEKO_WINDOWS 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 NEKO_WINDOWS 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 -> 'process Start a process using a command and the specified arguments. **/ static value process_run( value cmd, value vargs ) { int i; vprocess *p; val_check(cmd,string); val_check(vargs,array); # ifdef NEKO_WINDOWS { SECURITY_ATTRIBUTES sattr; STARTUPINFO sinf; HANDLE proc = GetCurrentProcess(); HANDLE oread,eread,iwrite; // creates commandline buffer b = alloc_buffer(NULL); value sargs; buffer_append_char(b,'"'); val_buffer(b,cmd); buffer_append_char(b,'"'); for(i=0;ioread,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) ) neko_error(); // close unused pipes CloseHandle(sinf.hStdOutput); CloseHandle(sinf.hStdError); CloseHandle(sinf.hStdInput); } # else char **argv = (char**)alloc_private(sizeof(char*)*(val_array_size(vargs)+2)); argv[0] = val_string(cmd); for(i=0;ipid = fork(); if( p->pid == -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(val_string(cmd),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 { value vp = alloc_abstract(k_process,p); val_gc(vp,free_process); return 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 Read up to [len] bytes in [buf] starting at [pos] from the process stdout. Returns the number of bytes readed this way. Raise an exception if this process stdout is closed and no more data is available for reading. **/ static value process_stdout_read( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { 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 = read(p->oread,val_string(str)+val_int(pos),val_int(len)); if( nbytes <= 0 ) neko_error(); return alloc_int(nbytes); # endif } /** process_stderr_read : 'process -> buf:string -> pos:int -> len:int -> int Read up to [len] bytes in [buf] starting at [pos] from the process stderr. Returns the number of bytes readed this way. Raise an exception if this process stderr is closed and no more data is available for reading. **/ static value process_stderr_read( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { 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 = read(p->eread,val_string(str)+val_int(pos),val_int(len)); if( nbytes <= 0 ) neko_error(); return alloc_int(nbytes); # endif } /** process_stdin_write : 'process -> buf:string -> pos:int -> len:int -> int 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. **/ static value process_stdin_write( value vp, value str, value pos, value len ) { CHECK_ARGS(); # ifdef NEKO_WINDOWS { 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 = write(p->iwrite,val_string(str)+val_int(pos),val_int(len)); if( nbytes == -1 ) neko_error(); return alloc_int(nbytes); # endif } /** process_stdin_close : 'process -> void Close the process standard input. **/ static value process_stdin_close( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS 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 Wait until the process terminate, then returns its exit code. **/ static value process_exit( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS { 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 Returns the process id. **/ static value process_pid( value vp ) { vprocess *p; val_check_kind(vp,k_process); p = val_process(vp); # ifdef NEKO_WINDOWS return alloc_int(p->pinf.dwProcessId); # else return alloc_int(p->pid); # endif } /** process_close : 'process -> void Close the process I/O. **/ static 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; } DEFINE_PRIM(process_run,2); DEFINE_PRIM(process_stdout_read,4); DEFINE_PRIM(process_stderr_read,4); DEFINE_PRIM(process_stdin_close,1); DEFINE_PRIM(process_stdin_write,4); DEFINE_PRIM(process_exit,1); DEFINE_PRIM(process_pid,1); DEFINE_PRIM(process_close,1); /* ************************************************************************ */