grim/convey
Clone
Summary
Browse
Changes
Graph
Add a .reviewboardrc file
2022-03-26, Gary Kramlich
8fea0c778f8e
Add a .reviewboardrc file
// Convey
// Copyright 2016-2018 Gary Kramlich <grim@reaperworld.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// Package exec provides utilities for executing external commands.
package
exec
import
(
"bufio"
"fmt"
"io"
"os/exec"
"sync"
"time"
log
"github.com/sirupsen/logrus"
"keep.imfreedom.org/grim/convey/logging"
)
// Run runs the command specified in argv and logs stderr and stdout to a new
// log adapter.
func
Run
(
name
string
,
cmdv
[]
string
,
timeout
time
.
Duration
)
error
{
var
(
logger
=
logging
.
NewAdapter
(
name
)
outCollector
=
newLogCollector
(
logger
,
log
.
InfoLevel
)
errCollector
=
newLogCollector
(
logger
,
log
.
ErrorLevel
)
)
return
run
(
name
,
cmdv
,
nil
,
timeout
,
&
sync
.
WaitGroup
{},
outCollector
,
errCollector
)
}
// RunWithStdin runs the command specified in argv and passes stdin to the
// process's stdin. stderr and stdout are logged to a new log adapter.
func
RunWithStdin
(
name
string
,
cmdv
[]
string
,
stdin
io
.
Reader
,
timeout
time
.
Duration
)
error
{
var
(
logger
=
logging
.
NewAdapter
(
name
)
outCollector
=
newLogCollector
(
logger
,
log
.
InfoLevel
)
errCollector
=
newLogCollector
(
logger
,
log
.
ErrorLevel
)
)
return
run
(
name
,
cmdv
,
stdin
,
timeout
,
&
sync
.
WaitGroup
{},
outCollector
,
errCollector
)
}
// RunOutput works just like Run but returns stdout and stderr instead of
// logging it.
func
RunOutput
(
name
string
,
cmdv
[]
string
,
timeout
time
.
Duration
)
(
string
,
string
,
error
)
{
var
(
wg
=
&
sync
.
WaitGroup
{}
outCollector
=
newStringCollector
(
wg
)
errCollector
=
newStringCollector
(
wg
)
)
err
:=
run
(
name
,
cmdv
,
nil
,
timeout
,
wg
,
outCollector
.
handler
,
errCollector
.
handler
)
wg
.
Wait
()
return
outCollector
.
output
,
errCollector
.
output
,
err
}
func
run
(
name
string
,
cmdv
[]
string
,
stdin
io
.
Reader
,
timeout
time
.
Duration
,
wg
*
sync
.
WaitGroup
,
outHandler
,
errHandler
collector
)
error
{
logger
:=
logging
.
NewAdapter
(
name
)
logger
.
Debugf
(
"running command \"%v\""
,
cmdv
)
cmd
:=
exec
.
Command
(
cmdv
[
0
],
cmdv
[
1
:]
...
)
// nolint: gas
// setup the stdout wrapper
outReader
,
err
:=
cmd
.
StdoutPipe
()
if
err
!=
nil
{
return
err
}
// setup the stderr wrapper
errReader
,
err
:=
cmd
.
StderrPipe
()
if
err
!=
nil
{
return
err
}
if
stdin
!=
nil
{
stdinPipe
,
err
:=
cmd
.
StdinPipe
()
if
err
!=
nil
{
return
err
}
go
func
()
{
defer
stdinPipe
.
Close
()
io
.
Copy
(
stdinPipe
,
stdin
)
}()
}
// at this point we're committed to running the log handlers so increment
// the waitgroup
wg
.
Add
(
2
)
// Pass output to the handlers
go
outHandler
(
bufio
.
NewScanner
(
outReader
))
go
errHandler
(
bufio
.
NewScanner
(
errReader
))
// now run the command
err
=
cmd
.
Start
()
if
err
!=
nil
{
return
err
}
var
timer
*
time
.
Timer
if
timeout
!=
0
{
timer
=
time
.
AfterFunc
(
timeout
,
func
()
{
// kill process, unblock the cmd.Wait() below
killErr
:=
cmd
.
Process
.
Kill
()
if
killErr
!=
nil
{
fmt
.
Printf
(
"error killing process: %s\n"
,
killErr
)
}
// log that the process timed out
logger
.
Errorf
(
"command %v timed out after %v"
,
cmdv
,
timeout
)
})
}
err
=
cmd
.
Wait
()
// if cmd.Wait() returns before the timeout, we'll have a timer stop
if
timer
!=
nil
{
timer
.
Stop
()
}
return
err
}