mirror of
https://github.com/gechandesu/runcmd.git
synced 2026-01-23 14:54:13 +03:00
feat,breaking: add pre_exec_hooks support, rework Process hooks
This commit is contained in:
52
cmd.v
52
cmd.v
@@ -27,9 +27,9 @@ pub mut:
|
|||||||
// `maps` module: `maps.merge(os.environ(), {'MYENV': 'value'})`
|
// `maps` module: `maps.merge(os.environ(), {'MYENV': 'value'})`
|
||||||
env map[string]string
|
env map[string]string
|
||||||
|
|
||||||
// dir specifies the current working directory for the child
|
// dir specifies the working directory for the child process.
|
||||||
// process. If not specified, the current working directory will
|
// If not specified, the current working directory of parent
|
||||||
// be used.
|
// will be used.
|
||||||
dir string
|
dir string
|
||||||
|
|
||||||
// If true create pipes for standart in/out/err streams and
|
// If true create pipes for standart in/out/err streams and
|
||||||
@@ -69,6 +69,11 @@ pub mut:
|
|||||||
// if context is timed out or canceled.
|
// if context is timed out or canceled.
|
||||||
cancel ?CommandCancelFn
|
cancel ?CommandCancelFn
|
||||||
|
|
||||||
|
// pre_exec_hooks will be called before starting the command in
|
||||||
|
// the child process. Hooks can be used to modify a child's envi-
|
||||||
|
// ronment, for example to perform a chroot.
|
||||||
|
pre_exec_hooks []ProcessHookFn
|
||||||
|
|
||||||
// process holds the underlying Process once started.
|
// process holds the underlying Process once started.
|
||||||
process ?&Process
|
process ?&Process
|
||||||
|
|
||||||
@@ -94,7 +99,7 @@ mut:
|
|||||||
stdio_copy_fns []IOCopyFn
|
stdio_copy_fns []IOCopyFn
|
||||||
}
|
}
|
||||||
|
|
||||||
// run starts a specified command and waits for it. After call see the .state
|
// run starts a specified command and waits for it. After call see the `.state`
|
||||||
// value to get finished process identifier, exit status and other attributes.
|
// value to get finished process identifier, exit status and other attributes.
|
||||||
// `run()` is shorthand for:
|
// `run()` is shorthand for:
|
||||||
// ```v
|
// ```v
|
||||||
@@ -110,12 +115,15 @@ pub fn (mut c Command) run() ! {
|
|||||||
// status is non-zero `ExitError` error is returned.
|
// status is non-zero `ExitError` error is returned.
|
||||||
// Example:
|
// Example:
|
||||||
// ```v
|
// ```v
|
||||||
|
// import runcmd
|
||||||
|
//
|
||||||
// mut okcmd := runcmd.new('sh', '-c', 'echo Hello, World!')
|
// mut okcmd := runcmd.new('sh', '-c', 'echo Hello, World!')
|
||||||
// output := okcmd.output()!
|
// ok_out := okcmd.output()!
|
||||||
|
// println(ok_out)
|
||||||
// // Hello, World!
|
// // Hello, World!
|
||||||
//
|
//
|
||||||
// mut badcmd := runcmd.new('sh', '-c', 'echo -n Error! >&2; false')
|
// mut badcmd := runcmd.new('sh', '-c', 'echo -n Error! >&2; false')
|
||||||
// output := badcmd.output() or {
|
// bad_out := badcmd.output() or {
|
||||||
// if err is runcmd.ExitError {
|
// if err is runcmd.ExitError {
|
||||||
// eprintln(err)
|
// eprintln(err)
|
||||||
// exit(err.code())
|
// exit(err.code())
|
||||||
@@ -124,14 +132,15 @@ pub fn (mut c Command) run() ! {
|
|||||||
// panic(err)
|
// panic(err)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
// println(bad_out)
|
||||||
// // &runcmd.ExitError{
|
// // &runcmd.ExitError{
|
||||||
// // state: exit status 1
|
// // state: exit status 1
|
||||||
// // stderr: 'Error!'
|
// // stderr: 'Error!'
|
||||||
// // }
|
// // }
|
||||||
// ```
|
// ```
|
||||||
pub fn (mut c Command) output() !string {
|
pub fn (mut c Command) output() !string {
|
||||||
mut out := strings.new_builder(4096)
|
mut out := strings.new_builder(2048)
|
||||||
mut err := strings.new_builder(4096)
|
mut err := strings.new_builder(2048)
|
||||||
c.redirect_stdio = true
|
c.redirect_stdio = true
|
||||||
c.stdout = out
|
c.stdout = out
|
||||||
c.stderr = err
|
c.stderr = err
|
||||||
@@ -152,8 +161,10 @@ pub fn (mut c Command) output() !string {
|
|||||||
// reading from the corresponding file descriptors is done concurrently.
|
// reading from the corresponding file descriptors is done concurrently.
|
||||||
// Example:
|
// Example:
|
||||||
// ```v
|
// ```v
|
||||||
|
// import runcmd
|
||||||
// mut cmd := runcmd.new('sh', '-c', 'echo Hello, STDOUT!; echo Hello, STDERR! >&2')
|
// mut cmd := runcmd.new('sh', '-c', 'echo Hello, STDOUT!; echo Hello, STDERR! >&2')
|
||||||
// output := cmd.combined_output()!
|
// output := cmd.combined_output()!
|
||||||
|
// println(output)
|
||||||
// // Hello, STDOUT!
|
// // Hello, STDOUT!
|
||||||
// // Hello, STDERR!
|
// // Hello, STDERR!
|
||||||
// ```
|
// ```
|
||||||
@@ -183,7 +194,7 @@ pub fn (mut c Command) start() !int {
|
|||||||
pipes[2] = pipe()! // stderr
|
pipes[2] = pipe()! // stderr
|
||||||
}
|
}
|
||||||
|
|
||||||
post_fork_parent_cb := fn [mut c, pipes] (mut p Process) ! {
|
parent_pipes_hook := fn [mut c, pipes] (mut p Process) ! {
|
||||||
if !c.redirect_stdio {
|
if !c.redirect_stdio {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -195,7 +206,8 @@ pub fn (mut c Command) start() !int {
|
|||||||
fd_close(pipes[2].w)!
|
fd_close(pipes[2].w)!
|
||||||
}
|
}
|
||||||
|
|
||||||
post_fork_child_cb := fn [mut c, pipes] (mut p Process) ! {
|
child_pipes_hook := fn [mut c, pipes] (mut p Process) ! {
|
||||||
|
printdbg('child pipes hook!')
|
||||||
if !c.redirect_stdio {
|
if !c.redirect_stdio {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -210,6 +222,9 @@ pub fn (mut c Command) start() !int {
|
|||||||
fd_close(pipes[2].w)!
|
fd_close(pipes[2].w)!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mut pre_exec_hooks := [child_pipes_hook]
|
||||||
|
pre_exec_hooks << c.pre_exec_hooks
|
||||||
|
|
||||||
if c.redirect_stdio {
|
if c.redirect_stdio {
|
||||||
if c.stdin != none {
|
if c.stdin != none {
|
||||||
c.stdio_copy_fns << fn [mut c] () ! {
|
c.stdio_copy_fns << fn [mut c] () ! {
|
||||||
@@ -247,14 +262,15 @@ pub fn (mut c Command) start() !int {
|
|||||||
|
|
||||||
// Prepare and start child process.
|
// Prepare and start child process.
|
||||||
path := look_path(c.path)!
|
path := look_path(c.path)!
|
||||||
|
printdbg('${@METHOD}: executable found: ${path}')
|
||||||
c.path = path
|
c.path = path
|
||||||
c.process = &Process{
|
c.process = &Process{
|
||||||
path: path
|
path: path
|
||||||
argv: c.args
|
argv: c.args
|
||||||
env: if c.env.len == 0 { os.environ() } else { c.env }
|
env: if c.env.len == 0 { os.environ() } else { c.env }
|
||||||
dir: if c.dir == '' { os.getwd() } else { c.dir }
|
dir: c.dir
|
||||||
post_fork_parent_cb: post_fork_parent_cb
|
post_fork: [parent_pipes_hook]
|
||||||
post_fork_child_cb: post_fork_child_cb
|
pre_exec: pre_exec_hooks
|
||||||
}
|
}
|
||||||
|
|
||||||
mut pid := -1
|
mut pid := -1
|
||||||
@@ -335,8 +351,8 @@ pub fn (mut c Command) release() ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// stdin returns an open file descriptor associated with the standard
|
// stdin returns an open file descriptor associated with the standard
|
||||||
// input stream of the child process. This descriptor is writable only
|
// input stream of the child process. This descriptor is write-only for
|
||||||
// by the parent process.
|
// the parent process.
|
||||||
pub fn (c Command) stdin() !WriteFd {
|
pub fn (c Command) stdin() !WriteFd {
|
||||||
return if c.stdio[0] != -1 {
|
return if c.stdio[0] != -1 {
|
||||||
WriteFd{c.stdio[0]}
|
WriteFd{c.stdio[0]}
|
||||||
|
|||||||
53
proc.c.v
53
proc.c.v
@@ -2,7 +2,7 @@ module runcmd
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
pub type ProcCallbackFn = fn (mut p Process) !
|
pub type ProcessHookFn = fn (mut p Process) !
|
||||||
|
|
||||||
pub struct Process {
|
pub struct Process {
|
||||||
pub:
|
pub:
|
||||||
@@ -18,14 +18,13 @@ pub:
|
|||||||
// Working directory for the child process.
|
// Working directory for the child process.
|
||||||
dir string
|
dir string
|
||||||
|
|
||||||
// The *_cb fields stores callback functions that will be executed respectively:
|
// The pre_* and post_* fields store the functions that will be executed, respectively:
|
||||||
// - before calling fork();
|
// - before fork() call in the parent process;
|
||||||
// - after calling fork() in the parent process, until the function exits;
|
// - after fork() call in the parent process;
|
||||||
// - after calling fork() in the child process, until the working directory is
|
// - after fork() call and before execve() call in the child process.
|
||||||
// changed and execve() is called.
|
pre_fork []ProcessHookFn
|
||||||
pre_fork_cb ProcCallbackFn = fn (mut p Process) ! {}
|
post_fork []ProcessHookFn
|
||||||
post_fork_parent_cb ProcCallbackFn = fn (mut p Process) ! {}
|
pre_exec []ProcessHookFn
|
||||||
post_fork_child_cb ProcCallbackFn = fn (mut p Process) ! {}
|
|
||||||
mut:
|
mut:
|
||||||
pid int = -1
|
pid int = -1
|
||||||
}
|
}
|
||||||
@@ -101,37 +100,51 @@ pub fn (mut p Process) start() !int {
|
|||||||
if p.pid != -1 {
|
if p.pid != -1 {
|
||||||
return error('runcmd: process already started')
|
return error('runcmd: process already started')
|
||||||
}
|
}
|
||||||
printdbg('${@METHOD}: current pid before fork() = ${v_getpid()}')
|
|
||||||
printdbg('${@METHOD}: executing pre-fork callback')
|
printdbg('${@METHOD}: executing pre-fork hooks (parent)')
|
||||||
p.pre_fork_cb(mut p)!
|
for hook in p.pre_fork {
|
||||||
|
hook(mut p)!
|
||||||
|
}
|
||||||
|
|
||||||
pid := os.fork()
|
pid := os.fork()
|
||||||
p.pid = pid
|
p.pid = pid
|
||||||
if pid == -1 {
|
if pid == -1 {
|
||||||
return os.last_error()
|
return os.last_error()
|
||||||
}
|
}
|
||||||
printdbg('${@METHOD}: pid after fork() = ${pid}')
|
|
||||||
|
printdbg('${@METHOD}: child pid = ${pid}')
|
||||||
|
|
||||||
if pid != 0 {
|
if pid != 0 {
|
||||||
//
|
//
|
||||||
// This is the parent process
|
// This is the parent process
|
||||||
//
|
//
|
||||||
|
|
||||||
printdbg('${@METHOD}: executing post-fork parent callback')
|
printdbg('${@METHOD}: executing post-fork hooks (parent)')
|
||||||
p.post_fork_parent_cb(mut p)!
|
for hook in p.post_fork {
|
||||||
|
hook(mut p)!
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
//
|
//
|
||||||
// This is the child process
|
// This is the child process
|
||||||
//
|
//
|
||||||
|
|
||||||
printdbg('${@METHOD}: executing post-fork child callback')
|
printdbg('${@METHOD}: executing pre-exec hooks (child)')
|
||||||
p.post_fork_child_cb(mut p)!
|
for hook in p.pre_exec {
|
||||||
if p.dir != '' {
|
hook(mut p)!
|
||||||
os.chdir(p.dir)!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mut env := []string{}
|
mut env := []string{}
|
||||||
for k, v in p.env {
|
for k, v in p.env {
|
||||||
env << k + '=' + v
|
env << k + '=' + v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the working directory change was performed in a pre-exec hook,
|
||||||
|
// then calling os.chdir() again should be avoided. So current working
|
||||||
|
// directory could be checked here.
|
||||||
|
if p.dir != '' && os.getwd() != p.dir {
|
||||||
|
os.chdir(p.dir)!
|
||||||
|
}
|
||||||
|
|
||||||
os.execve(p.path, p.argv, env)!
|
os.execve(p.path, p.argv, env)!
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +176,7 @@ pub fn (p &Process) signal(sig os.Signal) ! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// kill send SIGKILL to the child process.
|
// kill sends SIGKILL to the child process.
|
||||||
pub fn (p &Process) kill() ! {
|
pub fn (p &Process) kill() ! {
|
||||||
p.signal(.kill)!
|
p.signal(.kill)!
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user