os: add Process (#6786)
parent
8e473181ed
commit
d633261a99
|
@ -0,0 +1,81 @@
|
|||
module main
|
||||
|
||||
import os
|
||||
import time
|
||||
import os.cmdline
|
||||
|
||||
enum Target {
|
||||
both
|
||||
stderr
|
||||
stdout
|
||||
alternate
|
||||
}
|
||||
|
||||
fn s2target(s string) Target {
|
||||
return match s {
|
||||
'both' { Target.both }
|
||||
'stderr' { Target.stderr }
|
||||
'alternate' { Target.alternate }
|
||||
else { Target.stdout }
|
||||
}
|
||||
}
|
||||
|
||||
struct Context {
|
||||
mut:
|
||||
timeout_ms int
|
||||
period_ms int
|
||||
exitcode int
|
||||
target Target
|
||||
omode Target
|
||||
is_verbose bool
|
||||
}
|
||||
|
||||
fn (mut ctx Context) println(s string) {
|
||||
if ctx.target == .alternate {
|
||||
ctx.omode = if ctx.omode == .stderr { Target.stdout } else { Target.stderr }
|
||||
}
|
||||
if ctx.target in [.both, .stdout] || ctx.omode == .stdout {
|
||||
println('stdout, $s')
|
||||
}
|
||||
if ctx.target in [.both, .stderr] || ctx.omode == .stderr {
|
||||
eprintln('stderr, $s')
|
||||
}
|
||||
}
|
||||
|
||||
fn do_timeout(c &Context) {
|
||||
mut ctx := c
|
||||
time.sleep_ms(ctx.timeout_ms)
|
||||
exit(ctx.exitcode)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut ctx := Context{}
|
||||
args := os.args[1..]
|
||||
if '-h' in args || '--help' in args {
|
||||
println("Usage:
|
||||
test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-exitcode 0] [-timeout_ms 1000] [-period_ms 100]
|
||||
Prints lines periodically (-period_ms), to stdout/stderr (-target).
|
||||
After a while (-timeout_ms), exit with (-exitcode).
|
||||
This program is useful for platform independent testing
|
||||
of child process/standart input/output control.
|
||||
It is used in V\'s `os` module tests.
|
||||
")
|
||||
}
|
||||
ctx.is_verbose = '-v' in args
|
||||
ctx.target = s2target(cmdline.option(args, '-target', 'both'))
|
||||
ctx.exitcode = cmdline.option(args, '-exitcode', '0').int()
|
||||
ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '1000').int()
|
||||
ctx.period_ms = cmdline.option(args, '-period_ms', '100').int()
|
||||
if ctx.target == .alternate {
|
||||
ctx.omode = .stdout
|
||||
}
|
||||
if ctx.is_verbose {
|
||||
eprintln('> args: $args | context: $ctx')
|
||||
}
|
||||
go do_timeout(&ctx)
|
||||
for i := 1; true; i++ {
|
||||
ctx.println('$i')
|
||||
time.sleep_ms(ctx.period_ms)
|
||||
}
|
||||
time.sleep(100000)
|
||||
}
|
|
@ -4,388 +4,307 @@ module builtin
|
|||
fn C.memcpy(byteptr, byteptr, int) voidptr
|
||||
|
||||
fn C.memcmp(byteptr, byteptr, int) int
|
||||
fn C.memmove(byteptr, byteptr, int) voidptr
|
||||
fn C.calloc(int) byteptr
|
||||
fn C.malloc(int) byteptr
|
||||
fn C.realloc(a byteptr, b int) byteptr
|
||||
fn C.free(ptr voidptr)
|
||||
fn C.exit(code int)
|
||||
|
||||
fn C.memmove(byteptr, byteptr, int) voidptr
|
||||
|
||||
fn C.calloc(int) byteptr
|
||||
|
||||
fn C.malloc(int) byteptr
|
||||
|
||||
fn C.realloc(a byteptr, b int) byteptr
|
||||
|
||||
fn C.free(ptr voidptr)
|
||||
|
||||
fn C.exit(code int)
|
||||
|
||||
fn C.qsort(voidptr, int, int, qsort_callback_func)
|
||||
|
||||
|
||||
fn C.sprintf(a ...voidptr) int
|
||||
|
||||
|
||||
fn C.strlen(s charptr) int
|
||||
|
||||
fn C.sscanf(byteptr, byteptr,...byteptr) int
|
||||
fn C.sscanf(byteptr, byteptr, ...byteptr) int
|
||||
|
||||
fn C.isdigit(s byteptr) bool
|
||||
|
||||
// stdio.h
|
||||
fn C.popen(c charptr, t charptr) voidptr
|
||||
|
||||
// <execinfo.h>
|
||||
fn C.backtrace(a &voidptr, size int) int
|
||||
|
||||
fn C.backtrace_symbols(a &voidptr, size int) &charptr
|
||||
|
||||
fn C.backtrace_symbols_fd(a &voidptr, size int, fd int)
|
||||
|
||||
// <libproc.h>
|
||||
pub fn proc_pidpath(int, voidptr, int) int
|
||||
|
||||
|
||||
fn C.realpath(charptr, charptr) &char
|
||||
|
||||
|
||||
fn C.chmod(byteptr, int) int
|
||||
|
||||
|
||||
fn C.printf(byteptr, ...byteptr) int
|
||||
|
||||
fn C.puts(byteptr) int
|
||||
|
||||
fn C.fputs(byteptr) int
|
||||
|
||||
|
||||
fn C.fflush(byteptr) int
|
||||
|
||||
// TODO define args in these functions
|
||||
fn C.fseek() int
|
||||
|
||||
|
||||
fn C.fopen() voidptr
|
||||
|
||||
|
||||
fn C.fileno(voidptr) int
|
||||
|
||||
|
||||
fn C.fwrite() int
|
||||
|
||||
|
||||
fn C.fclose() int
|
||||
|
||||
|
||||
fn C.pclose() int
|
||||
|
||||
// process execution, os.process:
|
||||
fn C.getpid() int
|
||||
|
||||
fn C.system() int
|
||||
fn C.posix_spawn(&int, charptr, voidptr, voidptr, &charptr, voidptr) int
|
||||
fn C.waitpid(int, voidptr, int) int
|
||||
|
||||
fn C.posix_spawn(child_pid &int, path charptr, file_actions voidptr, attrp voidptr, argv &charptr, envp &charptr) int
|
||||
|
||||
fn C.posix_spawnp(child_pid &int, exefile charptr, file_actions voidptr, attrp voidptr, argv &charptr, envp &charptr) int
|
||||
|
||||
fn C.execve(cmd_path charptr, args voidptr, envs voidptr) int
|
||||
|
||||
fn C.fork() int
|
||||
|
||||
fn C.wait(status &int) int
|
||||
|
||||
fn C.waitpid(pid int, status &int, options int) int
|
||||
|
||||
fn C.kill(pid int, sig int) int
|
||||
|
||||
fn C.setenv(charptr) int
|
||||
|
||||
|
||||
fn C.unsetenv(charptr) int
|
||||
|
||||
|
||||
fn C.access() int
|
||||
|
||||
|
||||
fn C.remove() int
|
||||
|
||||
|
||||
fn C.rmdir() int
|
||||
|
||||
|
||||
fn C.chdir() int
|
||||
|
||||
|
||||
fn C.fread() int
|
||||
|
||||
|
||||
fn C.rewind() int
|
||||
|
||||
|
||||
fn C.stat(charptr) int
|
||||
|
||||
|
||||
fn C.lstat() int
|
||||
|
||||
|
||||
fn C.rename() int
|
||||
|
||||
|
||||
fn C.fgets() int
|
||||
|
||||
|
||||
fn C.memset() int
|
||||
|
||||
|
||||
fn C.sigemptyset() int
|
||||
|
||||
|
||||
fn C.getcwd() int
|
||||
|
||||
|
||||
fn C.signal() int
|
||||
|
||||
fn C.signal(signal int, handlercb voidptr) voidptr
|
||||
|
||||
fn C.mktime() int
|
||||
|
||||
|
||||
fn C.gettimeofday() int
|
||||
|
||||
|
||||
[trusted]
|
||||
fn C.sleep(int) int
|
||||
|
||||
|
||||
fn C.usleep() int
|
||||
|
||||
|
||||
fn C.opendir() voidptr
|
||||
|
||||
|
||||
fn C.closedir() int
|
||||
|
||||
|
||||
fn C.mkdir() int
|
||||
|
||||
|
||||
fn C.srand() int
|
||||
|
||||
|
||||
fn C.atof() int
|
||||
|
||||
|
||||
fn C.tolower() int
|
||||
|
||||
|
||||
fn C.toupper() int
|
||||
|
||||
|
||||
[trusted]
|
||||
fn C.getchar() int
|
||||
|
||||
|
||||
[trusted]
|
||||
fn C.strerror(int) charptr
|
||||
|
||||
|
||||
fn C.snprintf() int
|
||||
|
||||
|
||||
fn C.fprintf(byteptr, ...byteptr)
|
||||
|
||||
|
||||
fn C.WIFEXITED() bool
|
||||
|
||||
|
||||
fn C.WEXITSTATUS() int
|
||||
|
||||
|
||||
fn C.WIFSIGNALED() bool
|
||||
|
||||
|
||||
fn C.WTERMSIG() int
|
||||
|
||||
|
||||
fn C.isatty() int
|
||||
|
||||
|
||||
fn C.syscall() int
|
||||
|
||||
|
||||
fn C.sysctl() int
|
||||
|
||||
|
||||
fn C._fileno(int) int
|
||||
|
||||
|
||||
fn C._get_osfhandle(fd int) C.intptr_t
|
||||
|
||||
|
||||
fn C.GetModuleFileName() int
|
||||
|
||||
fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32
|
||||
|
||||
|
||||
fn C.CreateFile() voidptr
|
||||
fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32
|
||||
|
||||
fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32
|
||||
|
||||
fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int
|
||||
|
||||
|
||||
fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool
|
||||
|
||||
|
||||
fn C.SetHandleInformation(hObject voidptr, dwMask u32, dw_flags u32) bool
|
||||
|
||||
|
||||
fn C.ExpandEnvironmentStringsW(lpSrc &u16, lpDst &u16, nSize u32) u32
|
||||
|
||||
|
||||
fn C.SendMessageTimeout() u32
|
||||
fn C.SendMessageTimeoutW(hWnd voidptr, Msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32
|
||||
|
||||
fn C.SendMessageTimeoutW(hWnd voidptr, Msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32
|
||||
|
||||
fn C.CreateProcessW(lpApplicationName &u16, lpCommandLine &u16, lpProcessAttributes voidptr, lpThreadAttributes voidptr, bInheritHandles bool, dwCreationFlags u32, lpEnvironment voidptr, lpCurrentDirectory &u16, lpStartupInfo voidptr, lpProcessInformation voidptr) bool
|
||||
|
||||
|
||||
fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumberOfBytesRead C.LPDWORD, lpOverlapped voidptr) bool
|
||||
|
||||
|
||||
fn C.GetFileAttributesW(lpFileName byteptr) u32
|
||||
|
||||
|
||||
fn C.RegQueryValueEx() voidptr
|
||||
|
||||
fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData byteptr, lpcbData &u32) int
|
||||
|
||||
|
||||
fn C.RegOpenKeyEx() voidptr
|
||||
|
||||
fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int
|
||||
|
||||
|
||||
fn C.RegSetValueEx() voidptr
|
||||
fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, Reserved u32, dwType u32, lpData byteptr, lpcbData u32) int
|
||||
|
||||
fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, Reserved u32, dwType u32, lpData byteptr, lpcbData u32) int
|
||||
|
||||
fn C.RegCloseKey()
|
||||
|
||||
|
||||
fn C.RemoveDirectory() int
|
||||
|
||||
|
||||
//fn C.GetStdHandle() voidptr
|
||||
// fn C.GetStdHandle() voidptr
|
||||
fn C.GetStdHandle(u32) voidptr
|
||||
|
||||
|
||||
//fn C.SetConsoleMode()
|
||||
// fn C.SetConsoleMode()
|
||||
fn C.SetConsoleMode(voidptr, u32)
|
||||
|
||||
|
||||
//fn C.GetConsoleMode() int
|
||||
// fn C.GetConsoleMode() int
|
||||
fn C.GetConsoleMode(voidptr, &u32) int
|
||||
|
||||
fn C.GetCurrentProcessId() int
|
||||
|
||||
fn C.wprintf()
|
||||
|
||||
|
||||
//fn C.setbuf()
|
||||
// fn C.setbuf()
|
||||
fn C.setbuf(voidptr, charptr)
|
||||
|
||||
|
||||
fn C.SymCleanup()
|
||||
|
||||
|
||||
fn C.MultiByteToWideChar() int
|
||||
|
||||
|
||||
fn C.wcslen() int
|
||||
|
||||
|
||||
fn C.WideCharToMultiByte() int
|
||||
|
||||
|
||||
fn C._wstat()
|
||||
|
||||
|
||||
fn C._wrename()
|
||||
|
||||
|
||||
fn C._wfopen() voidptr
|
||||
|
||||
|
||||
fn C._wpopen() voidptr
|
||||
|
||||
|
||||
fn C._pclose() int
|
||||
|
||||
|
||||
fn C._wsystem() int
|
||||
|
||||
|
||||
fn C._wgetenv() voidptr
|
||||
|
||||
|
||||
fn C._putenv() int
|
||||
|
||||
|
||||
fn C._waccess() int
|
||||
|
||||
|
||||
fn C._wremove() int
|
||||
|
||||
|
||||
fn C.ReadConsole() voidptr
|
||||
|
||||
|
||||
fn C.WriteConsole() voidptr
|
||||
|
||||
|
||||
fn C.WriteFile() voidptr
|
||||
|
||||
|
||||
fn C._wchdir()
|
||||
|
||||
|
||||
fn C._wgetcwd() int
|
||||
|
||||
|
||||
fn C._fullpath() int
|
||||
|
||||
|
||||
fn C.GetCommandLine() voidptr
|
||||
|
||||
|
||||
fn C.LocalFree()
|
||||
|
||||
|
||||
fn C.FindFirstFileW() voidptr
|
||||
|
||||
|
||||
fn C.FindFirstFile() voidptr
|
||||
|
||||
|
||||
fn C.FindNextFile() int
|
||||
|
||||
|
||||
fn C.FindClose()
|
||||
|
||||
|
||||
fn C.MAKELANGID() int
|
||||
|
||||
|
||||
fn C.FormatMessage() voidptr
|
||||
|
||||
|
||||
fn C.CloseHandle(voidptr) int
|
||||
|
||||
|
||||
fn C.GetExitCodeProcess()
|
||||
|
||||
|
||||
|
||||
|
||||
fn C.GetTickCount() i64
|
||||
|
||||
|
||||
fn C.Sleep()
|
||||
|
||||
|
||||
fn C.WSAStartup(u16, &voidptr) int
|
||||
|
||||
|
||||
fn C.WSAGetLastError() int
|
||||
|
||||
|
||||
fn C.closesocket(int) int
|
||||
|
||||
|
||||
fn C.vschannel_init(&C.TlsContext)
|
||||
|
||||
|
||||
fn C.request(&C.TlsContext, int, &u16, byteptr, &byteptr)
|
||||
|
||||
|
||||
fn C.vschannel_cleanup(&C.TlsContext)
|
||||
|
||||
|
||||
fn C.URLDownloadToFile(int, &u16, &u16, int, int)
|
||||
|
||||
|
||||
fn C.GetLastError() u32
|
||||
|
||||
|
||||
fn C.CreateDirectory(byteptr, int) bool
|
||||
|
||||
// win crypto
|
||||
|
@ -393,60 +312,102 @@ fn C.BCryptGenRandom(int, voidptr, int, int) int
|
|||
|
||||
// win synchronization
|
||||
fn C.CreateMutex(int, bool, byteptr) voidptr
|
||||
|
||||
fn C.WaitForSingleObject(voidptr, int) int
|
||||
|
||||
fn C.ReleaseMutex(voidptr) bool
|
||||
|
||||
fn C.CreateEvent(int, bool, bool, byteptr) voidptr
|
||||
|
||||
fn C.SetEvent(voidptr) int
|
||||
|
||||
fn C.CreateSemaphore(voidptr, int, int, voidptr) voidptr
|
||||
|
||||
fn C.ReleaseSemaphore(voidptr, int, voidptr) voidptr
|
||||
|
||||
fn C.InitializeSRWLock(voidptr)
|
||||
|
||||
fn C.AcquireSRWLockShared(voidptr)
|
||||
|
||||
fn C.AcquireSRWLockExclusive(voidptr)
|
||||
|
||||
fn C.ReleaseSRWLockShared(voidptr)
|
||||
|
||||
fn C.ReleaseSRWLockExclusive(voidptr)
|
||||
|
||||
// pthread.h
|
||||
fn C.pthread_mutex_init(voidptr, voidptr) int
|
||||
|
||||
fn C.pthread_mutex_lock(voidptr) int
|
||||
|
||||
fn C.pthread_mutex_unlock(voidptr) int
|
||||
|
||||
fn C.pthread_mutex_destroy(voidptr) int
|
||||
|
||||
fn C.pthread_rwlockattr_init(voidptr) int
|
||||
|
||||
fn C.pthread_rwlockattr_setkind_np(voidptr, int) int
|
||||
|
||||
fn C.pthread_rwlockattr_setpshared(voidptr, int) int
|
||||
|
||||
fn C.pthread_rwlock_init(voidptr, voidptr) int
|
||||
|
||||
fn C.pthread_rwlock_rdlock(voidptr) int
|
||||
|
||||
fn C.pthread_rwlock_wrlock(voidptr) int
|
||||
|
||||
fn C.pthread_rwlock_unlock(voidptr) int
|
||||
|
||||
fn C.pthread_condattr_init(voidptr) int
|
||||
|
||||
fn C.pthread_condattr_setpshared(voidptr, int) int
|
||||
|
||||
fn C.pthread_condattr_destroy(voidptr) int
|
||||
|
||||
fn C.pthread_cond_init(voidptr, voidptr) int
|
||||
|
||||
fn C.pthread_cond_signal(voidptr) int
|
||||
|
||||
fn C.pthread_cond_wait(voidptr, voidptr) int
|
||||
|
||||
fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int
|
||||
|
||||
fn C.pthread_cond_destroy(voidptr) int
|
||||
|
||||
fn C.sem_init(voidptr, int, u32) int
|
||||
|
||||
fn C.sem_post(voidptr) int
|
||||
|
||||
fn C.sem_wait(voidptr) int
|
||||
|
||||
fn C.sem_trywait(voidptr) int
|
||||
|
||||
fn C.sem_timedwait(voidptr, voidptr) int
|
||||
|
||||
fn C.sem_destroy(voidptr) int
|
||||
|
||||
// MacOS semaphore functions
|
||||
fn C.dispatch_semaphore_create(i64) voidptr
|
||||
|
||||
fn C.dispatch_semaphore_signal(voidptr) i64
|
||||
|
||||
fn C.dispatch_semaphore_wait(voidptr, u64) i64
|
||||
|
||||
fn C.dispatch_time(u64, i64) u64
|
||||
|
||||
fn C.dispatch_release(voidptr)
|
||||
|
||||
// file descriptor based reading/writing
|
||||
fn C.read(fd int, buf voidptr, count size_t) int
|
||||
|
||||
fn C.write(fd int, buf voidptr, count size_t) int
|
||||
|
||||
fn C.close(fd int) int
|
||||
|
||||
// pipes
|
||||
fn C.pipe(pipefds &int) int
|
||||
|
||||
fn C.dup2(oldfd int, newfd int) int
|
||||
|
||||
// used by gl, stbi, freetype
|
||||
fn C.glTexImage2D()
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
module os
|
||||
|
||||
// file descriptor based operations:
|
||||
pub fn fd_close(fd int) int {
|
||||
return C.close(fd)
|
||||
}
|
||||
|
||||
pub fn fd_write(fd int, s string) {
|
||||
mut sp := s.str
|
||||
mut remaining := s.len
|
||||
for remaining > 0 {
|
||||
written := C.write(fd, sp, remaining)
|
||||
if written < 0 {
|
||||
return
|
||||
}
|
||||
remaining = remaining - written
|
||||
sp = unsafe {sp + written}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fd_slurp(fd int) []string {
|
||||
mut res := []string{}
|
||||
for {
|
||||
s, b := fd_read(fd, 4096)
|
||||
if b <= 0 {
|
||||
break
|
||||
}
|
||||
res << s
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
pub fn fd_read(fd int, maxbytes int) (string, int) {
|
||||
mut buf := malloc(maxbytes)
|
||||
nbytes := C.read(fd, buf, maxbytes)
|
||||
if nbytes < 0 {
|
||||
free(buf)
|
||||
return '', nbytes
|
||||
}
|
||||
unsafe {
|
||||
buf[nbytes] = 0
|
||||
}
|
||||
return tos(buf, nbytes), nbytes
|
||||
}
|
|
@ -8,8 +8,6 @@ struct C.dirent {
|
|||
|
||||
fn C.readdir(voidptr) &C.dirent
|
||||
|
||||
fn C.getpid() int
|
||||
|
||||
fn C.readlink() int
|
||||
|
||||
fn C.getline(voidptr, voidptr, voidptr) int
|
||||
|
@ -24,10 +22,6 @@ fn C.fdopen(int, string) voidptr
|
|||
|
||||
fn C.CopyFile(&u32, &u32, int) int
|
||||
|
||||
fn C.fork() int
|
||||
|
||||
fn C.wait() int
|
||||
|
||||
// fn C.proc_pidpath(int, byteptr, int) int
|
||||
struct C.stat {
|
||||
st_size int
|
||||
|
|
|
@ -0,0 +1,240 @@
|
|||
module os
|
||||
|
||||
// ProcessState.not_started - the process has not yet started
|
||||
// ProcessState.running - the process is currently running
|
||||
// ProcessState.stopped - the process was running, but was stopped temporarily
|
||||
// ProcessState.exited - the process has finished/exited
|
||||
// ProcessState.aborted - the process was terminated by a signal
|
||||
pub enum ProcessState {
|
||||
not_started
|
||||
running
|
||||
stopped
|
||||
exited
|
||||
aborted
|
||||
}
|
||||
|
||||
[ref_only]
|
||||
pub struct Process {
|
||||
pub:
|
||||
filename string // the process's command file path
|
||||
pub mut:
|
||||
pid int // the PID of the process
|
||||
code int = -1
|
||||
// the exit code of the process, != -1 *only* when status is .exited *and* the process was not aborted
|
||||
status ProcessState = .not_started
|
||||
// the current status of the process
|
||||
err string // if the process fails, contains the reason why
|
||||
args []string // the arguments that the command takes
|
||||
env_is_custom bool // true, when the environment was customized with .set_environment
|
||||
env []string // the environment with which the process was started
|
||||
use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp()
|
||||
stdio_fd [3]int
|
||||
}
|
||||
|
||||
// new_process - create a new process descriptor
|
||||
// NB: new does NOT start the new process.
|
||||
// That is done because you may want to customize it first,
|
||||
// by calling different set_ methods on it.
|
||||
// In order to start it, call p.run() or p.wait()
|
||||
pub fn new_process(filename string) &Process {
|
||||
return &Process{
|
||||
filename: filename
|
||||
}
|
||||
}
|
||||
|
||||
// set_args - set the arguments for the new process
|
||||
pub fn (mut p Process) set_args(pargs []string) &Process {
|
||||
if p.status != .not_started {
|
||||
return p
|
||||
}
|
||||
p.args = pargs
|
||||
return p
|
||||
}
|
||||
|
||||
// set_environment - set a custom environment variable mapping for the new process
|
||||
pub fn (mut p Process) set_environment(envs map[string]string) &Process {
|
||||
if p.status != .not_started {
|
||||
return p
|
||||
}
|
||||
p.env_is_custom = true
|
||||
p.env = []string{}
|
||||
for k, v in envs {
|
||||
p.env << '$k=$v'
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// run - starts the new process
|
||||
pub fn (mut p Process) run() &Process {
|
||||
if p.status != .not_started {
|
||||
return p
|
||||
}
|
||||
p._spawn()
|
||||
return p
|
||||
}
|
||||
|
||||
// signal_kill - kills the process, after that it is no longer running
|
||||
pub fn (mut p Process) signal_kill() &Process {
|
||||
if p.status !in [.running, .stopped] {
|
||||
return p
|
||||
}
|
||||
p._signal_kill()
|
||||
p.status = .aborted
|
||||
return p
|
||||
}
|
||||
|
||||
// signal_stop - stops the process, you can resume it with p.signal_continue()
|
||||
pub fn (mut p Process) signal_stop() &Process {
|
||||
if p.status != .running {
|
||||
return p
|
||||
}
|
||||
p._signal_stop()
|
||||
p.status = .stopped
|
||||
return p
|
||||
}
|
||||
|
||||
// signal_continue - tell a stopped process to continue/resume its work
|
||||
pub fn (mut p Process) signal_continue() &Process {
|
||||
if p.status != .stopped {
|
||||
return p
|
||||
}
|
||||
p._signal_continue()
|
||||
p.status = .running
|
||||
return p
|
||||
}
|
||||
|
||||
// wait - wait for a process to finish.
|
||||
// NB: You have to call p.wait(), otherwise a finished process
|
||||
// would get to a zombie state, and its resources will not get
|
||||
// released fully, until its parent process exits.
|
||||
// NB: This call will block the calling process until the child
|
||||
// process is finished.
|
||||
pub fn (mut p Process) wait() &Process {
|
||||
if p.status == .not_started {
|
||||
p._spawn()
|
||||
}
|
||||
if p.status !in [.running, .stopped] {
|
||||
return p
|
||||
}
|
||||
p._wait()
|
||||
return p
|
||||
}
|
||||
|
||||
//
|
||||
// _spawn - should not be called directly, but only by p.run()/p.wait() .
|
||||
// It encapsulates the fork/execve mechanism that allows the
|
||||
// asynchronous starting of the new child process.
|
||||
fn (mut p Process) _spawn() int {
|
||||
if !p.env_is_custom {
|
||||
p.env = []string{}
|
||||
current_environment := environ()
|
||||
for k, v in current_environment {
|
||||
p.env << '$k=$v'
|
||||
}
|
||||
}
|
||||
mut pid := 0
|
||||
$if windows {
|
||||
pid = p.win_spawn_process()
|
||||
} $else {
|
||||
pid = p.unix_spawn_process()
|
||||
}
|
||||
p.pid = pid
|
||||
p.status = .running
|
||||
return 0
|
||||
}
|
||||
|
||||
// is_alive - query whether the process p.pid is still alive
|
||||
pub fn (mut p Process) is_alive() bool {
|
||||
if p.status in [.running, .stopped] {
|
||||
return p._is_alive()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
//
|
||||
pub fn (mut p Process) set_redirect_stdio() &Process {
|
||||
p.use_stdio_ctl = true
|
||||
return p
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stdin_write(s string) {
|
||||
p._check_redirection_call('stdin_write')
|
||||
fd_write(p.stdio_fd[0], s)
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stdout_slurp() string {
|
||||
p._check_redirection_call('stdout_slurp')
|
||||
return fd_slurp(p.stdio_fd[1]).join('')
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stderr_slurp() string {
|
||||
p._check_redirection_call('stderr_slurp')
|
||||
return fd_slurp(p.stdio_fd[2]).join('')
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stdout_read() string {
|
||||
p._check_redirection_call('stdout_read')
|
||||
s, _ := fd_read(p.stdio_fd[1], 4096)
|
||||
return s
|
||||
}
|
||||
|
||||
pub fn (mut p Process) stderr_read() string {
|
||||
p._check_redirection_call('stderr_read')
|
||||
s, _ := fd_read(p.stdio_fd[2], 4096)
|
||||
return s
|
||||
}
|
||||
|
||||
// _check_redirection_call - should be called just by stdxxx methods
|
||||
fn (mut p Process) _check_redirection_call(fn_name string) {
|
||||
if !p.use_stdio_ctl {
|
||||
panic('Call p.set_redirect_stdio() before calling p.$fn_name')
|
||||
}
|
||||
if p.status == .not_started {
|
||||
panic('Call p.${fn_name}() after you have called p.run()')
|
||||
}
|
||||
}
|
||||
|
||||
// _signal_stop - should not be called directly, except by p.signal_stop
|
||||
fn (mut p Process) _signal_stop() {
|
||||
$if windows {
|
||||
p.win_stop_process()
|
||||
} $else {
|
||||
p.unix_stop_process()
|
||||
}
|
||||
}
|
||||
|
||||
// _signal_continue - should not be called directly, just by p.signal_continue
|
||||
fn (mut p Process) _signal_continue() {
|
||||
$if windows {
|
||||
p.win_resume_process()
|
||||
} $else {
|
||||
p.unix_resume_process()
|
||||
}
|
||||
}
|
||||
|
||||
// _signal_kill - should not be called directly, except by p.signal_kill
|
||||
fn (mut p Process) _signal_kill() {
|
||||
$if windows {
|
||||
p.win_kill_process()
|
||||
} $else {
|
||||
p.unix_kill_process()
|
||||
}
|
||||
}
|
||||
|
||||
// _wait - should not be called directly, except by p.wait()
|
||||
fn (mut p Process) _wait() {
|
||||
$if windows {
|
||||
p.win_wait()
|
||||
} $else {
|
||||
p.unix_wait()
|
||||
}
|
||||
}
|
||||
|
||||
// _is_alive - should not be called directly, except by p.is_alive()
|
||||
fn (mut p Process) _is_alive() bool {
|
||||
$if windows {
|
||||
return p.win_is_alive()
|
||||
} $else {
|
||||
return p.unix_is_alive()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
module os
|
||||
|
||||
fn (mut p Process) unix_spawn_process() int {
|
||||
mut pipeset := [6]int{}
|
||||
if p.use_stdio_ctl {
|
||||
C.pipe(&pipeset[0]) // pipe read end 0 <- 1 pipe write end
|
||||
C.pipe(&pipeset[2]) // pipe read end 2 <- 3 pipe write end
|
||||
C.pipe(&pipeset[4]) // pipe read end 4 <- 5 pipe write end
|
||||
}
|
||||
pid := fork()
|
||||
if pid != 0 {
|
||||
// This is the parent process after the fork.
|
||||
// NB: pid contains the process ID of the child process
|
||||
if p.use_stdio_ctl {
|
||||
p.stdio_fd[0] = pipeset[1] // store the write end of child's in
|
||||
p.stdio_fd[1] = pipeset[2] // store the read end of child's out
|
||||
p.stdio_fd[2] = pipeset[4] // store the read end of child's err
|
||||
// close the rest of the pipe fds, the parent does not need them
|
||||
fd_close(pipeset[0])
|
||||
fd_close(pipeset[3])
|
||||
fd_close(pipeset[5])
|
||||
}
|
||||
return pid
|
||||
}
|
||||
//
|
||||
// Here, we are in the child process.
|
||||
// It still shares file descriptors with the parent process,
|
||||
// but it is otherwise independant and can do stuff *without*
|
||||
// affecting the parent process.
|
||||
if p.use_stdio_ctl {
|
||||
// Redirect the child standart in/out/err to the pipes that
|
||||
// were created in the parent.
|
||||
// Close the parent's pipe fds, the child do not need them:
|
||||
fd_close(pipeset[1])
|
||||
fd_close(pipeset[2])
|
||||
fd_close(pipeset[4])
|
||||
// redirect the pipe fds to the child's in/out/err fds:
|
||||
C.dup2(pipeset[0], 0)
|
||||
C.dup2(pipeset[3], 1)
|
||||
C.dup2(pipeset[5], 2)
|
||||
// close the pipe fdsx after the redirection
|
||||
fd_close(pipeset[0])
|
||||
fd_close(pipeset[3])
|
||||
fd_close(pipeset[5])
|
||||
}
|
||||
mut cargv := []charptr{}
|
||||
mut cenvs := []charptr{}
|
||||
cargv << p.filename.str
|
||||
for i in 0 .. p.args.len {
|
||||
cargv << p.args[i].str
|
||||
}
|
||||
for i in 0 .. p.env.len {
|
||||
cenvs << p.env[i].str
|
||||
}
|
||||
cargv << charptr(0)
|
||||
cenvs << charptr(0)
|
||||
C.execve(p.filename.str, cargv.data, cenvs.data)
|
||||
// NB: normally execve does not return at all.
|
||||
// If it returns, then something went wrong...
|
||||
eprintln(posix_get_error_msg(C.errno))
|
||||
exit(1)
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_stop_process() {
|
||||
C.kill(p.pid, C.SIGSTOP)
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_resume_process() {
|
||||
C.kill(p.pid, C.SIGCONT)
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_kill_process() {
|
||||
C.kill(p.pid, C.SIGKILL)
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_wait() {
|
||||
cstatus := 0
|
||||
ret := C.waitpid(p.pid, &cstatus, 0)
|
||||
if ret == -1 {
|
||||
p.err = posix_get_error_msg(C.errno)
|
||||
return
|
||||
}
|
||||
pret, is_signaled := posix_wait4_to_exit_status(cstatus)
|
||||
if is_signaled {
|
||||
p.status = .aborted
|
||||
p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})'
|
||||
} else {
|
||||
p.status = .exited
|
||||
}
|
||||
p.code = pret
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_is_alive() bool {
|
||||
cstatus := 0
|
||||
ret := C.waitpid(p.pid, &cstatus, C.WNOHANG)
|
||||
if ret == -1 {
|
||||
p.err = posix_get_error_msg(C.errno)
|
||||
return false
|
||||
}
|
||||
if ret == 0 {
|
||||
return true
|
||||
}
|
||||
pret, is_signaled := posix_wait4_to_exit_status(cstatus)
|
||||
if is_signaled {
|
||||
p.status = .aborted
|
||||
p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})'
|
||||
} else {
|
||||
p.status = .exited
|
||||
}
|
||||
p.code = pret
|
||||
return false
|
||||
}
|
||||
|
||||
// these are here to make v_win.c/v.c generation work in all cases:
|
||||
fn (mut p Process) win_spawn_process() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (mut p Process) win_stop_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) win_resume_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) win_kill_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) win_wait() {
|
||||
}
|
||||
|
||||
fn (mut p Process) win_is_alive() bool {
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
import os
|
||||
import time
|
||||
|
||||
fn test_getpid() {
|
||||
pid := os.getpid()
|
||||
eprintln('current pid: $pid')
|
||||
assert pid != 0
|
||||
}
|
||||
|
||||
fn test_run() {
|
||||
if os.user_os() == 'windows' {
|
||||
return
|
||||
}
|
||||
//
|
||||
mut p := os.new_process('/bin/sleep')
|
||||
p.set_args(['0.2'])
|
||||
p.run()
|
||||
assert p.status == .running
|
||||
assert p.pid > 0
|
||||
assert p.pid != os.getpid()
|
||||
mut i := 0
|
||||
for {
|
||||
if !p.is_alive() {
|
||||
break
|
||||
}
|
||||
os.system('ps -opid= -oppid= -ouser= -onice= -of= -ovsz= -orss= -otime= -oargs= -p $p.pid')
|
||||
time.sleep_ms(50)
|
||||
i++
|
||||
}
|
||||
p.wait()
|
||||
assert p.code == 0
|
||||
assert p.status == .exited
|
||||
//
|
||||
eprintln('polling iterations: $i')
|
||||
assert i > 1
|
||||
assert i < 20
|
||||
}
|
||||
|
||||
fn test_wait() {
|
||||
if os.user_os() == 'windows' {
|
||||
return
|
||||
}
|
||||
mut p := os.new_process('/bin/date')
|
||||
p.wait()
|
||||
assert p.pid != os.getpid()
|
||||
assert p.code == 0
|
||||
assert p.status == .exited
|
||||
}
|
||||
|
||||
fn test_slurping_output() {
|
||||
if os.user_os() == 'windows' {
|
||||
return
|
||||
}
|
||||
mut p := os.new_process('/bin/date')
|
||||
p.set_redirect_stdio()
|
||||
p.wait()
|
||||
assert p.code == 0
|
||||
assert p.status == .exited
|
||||
output := p.stdout_slurp().trim_space()
|
||||
errors := p.stderr_slurp().trim_space()
|
||||
eprintln('p output: "$output"')
|
||||
eprintln('p errors: "$errors"')
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
module os
|
||||
|
||||
fn (mut p Process) win_spawn_process() int {
|
||||
eprintln('TODO implement waiting for a process on windows')
|
||||
return 12345
|
||||
}
|
||||
|
||||
fn (mut p Process) win_stop_process() {
|
||||
eprintln('TODO implement stopping a process on windows')
|
||||
}
|
||||
|
||||
fn (mut p Process) win_resume_process() {
|
||||
eprintln('TODO implement resuming a process on windows')
|
||||
}
|
||||
|
||||
fn (mut p Process) win_kill_process() {
|
||||
eprintln('TODO implement killing a process on windows')
|
||||
}
|
||||
|
||||
fn (mut p Process) win_wait() {
|
||||
eprintln('TODO implement waiting for a process on windows')
|
||||
p.status = .exited
|
||||
p.code = 0
|
||||
}
|
||||
|
||||
fn (mut p Process) win_is_alive() bool {
|
||||
eprintln('TODO implement checking whether the process is still alive on windows')
|
||||
return false
|
||||
}
|
||||
|
||||
//
|
||||
// these are here to make v_win.c/v.c generation work in all cases:
|
||||
fn (mut p Process) unix_spawn_process() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_stop_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_resume_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_kill_process() {
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_wait() {
|
||||
}
|
||||
|
||||
fn (mut p Process) unix_is_alive() bool {
|
||||
return false
|
||||
}
|
|
@ -38,8 +38,6 @@ fn C.tcsetattr() int
|
|||
|
||||
fn C.raise()
|
||||
|
||||
fn C.kill(int, int) int
|
||||
|
||||
fn C.getppid() int
|
||||
|
||||
// Enable the raw mode of the terminal
|
||||
|
|
Loading…
Reference in New Issue