mirror of
https://github.com/reactos/syzkaller.git
synced 2025-03-02 16:45:38 +00:00
all: use more respective language
Some terms are normalised on the technical level but may be oppressive on a societal level. Replace them with more technically neutral terms. See the following doc for a longer version: https://tools.ietf.org/id/draft-knodel-terminology-00.html
This commit is contained in:
parent
829fd56fbf
commit
588020678f
7
Makefile
7
Makefile
@ -99,7 +99,7 @@ endif
|
||||
bin/syz-extract bin/syz-fmt \
|
||||
extract generate generate_go generate_sys \
|
||||
format format_go format_cpp format_sys \
|
||||
tidy test test_race check_copyright check_links check_diff \
|
||||
tidy test test_race check_copyright check_language check_links check_diff \
|
||||
arch arch_darwin_amd64_host arch_linux_amd64_host \
|
||||
arch_freebsd_amd64_host arch_netbsd_amd64_host \
|
||||
arch_linux_amd64_target arch_linux_386_target \
|
||||
@ -321,7 +321,7 @@ presubmit:
|
||||
|
||||
presubmit_smoke:
|
||||
$(MAKE) generate
|
||||
$(MAKE) -j100 check_diff check_copyright check_links presubmit_build
|
||||
$(MAKE) -j100 check_diff check_copyright check_language check_links presubmit_build
|
||||
$(MAKE) test
|
||||
|
||||
presubmit_build:
|
||||
@ -394,6 +394,9 @@ install_prerequisites:
|
||||
check_copyright:
|
||||
./tools/check-copyright.sh
|
||||
|
||||
check_language:
|
||||
./tools/check-language.sh
|
||||
|
||||
check_links:
|
||||
python ./tools/check_links.py $$(pwd) $$(ls ./*.md; find ./docs/ -name '*.md')
|
||||
|
||||
|
@ -39,8 +39,8 @@ var testConfig = &GlobalConfig{
|
||||
Clients: map[string]string{
|
||||
"reporting": "reportingkeyreportingkeyreportingkey",
|
||||
},
|
||||
EmailBlacklist: []string{
|
||||
"\"Bar\" <BlackListed@Domain.com>",
|
||||
EmailBlocklist: []string{
|
||||
"\"Bar\" <Blocked@Domain.com>",
|
||||
},
|
||||
Obsoleting: ObsoletingConfig{
|
||||
MinPeriod: 80 * 24 * time.Hour,
|
||||
|
@ -563,7 +563,7 @@ func TestBisectWrong(t *testing.T) {
|
||||
case 4:
|
||||
flags = dashapi.BisectResultRelease
|
||||
case 5:
|
||||
flags = dashapi.BisectResultBlacklist
|
||||
flags = dashapi.BisectResultIgnore
|
||||
default:
|
||||
t.Fatalf("assign flags")
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ type GlobalConfig struct {
|
||||
CoverPath string
|
||||
// Global API clients that work across namespaces (e.g. external reporting).
|
||||
Clients map[string]string
|
||||
// List of emails blacklisted from issuing test requests.
|
||||
EmailBlacklist []string
|
||||
// List of emails blocked from issuing test requests.
|
||||
EmailBlocklist []string
|
||||
// Bug obsoleting settings. See ObsoletingConfig for details.
|
||||
Obsoleting ObsoletingConfig
|
||||
// Namespace that is shown by default (no namespace selected yet).
|
||||
@ -225,8 +225,8 @@ func checkConfig(cfg *GlobalConfig) {
|
||||
if len(cfg.Namespaces) == 0 {
|
||||
panic("no namespaces found")
|
||||
}
|
||||
for i := range cfg.EmailBlacklist {
|
||||
cfg.EmailBlacklist[i] = email.CanonicalEmail(cfg.EmailBlacklist[i])
|
||||
for i := range cfg.EmailBlocklist {
|
||||
cfg.EmailBlocklist[i] = email.CanonicalEmail(cfg.EmailBlocklist[i])
|
||||
}
|
||||
namespaces := make(map[string]bool)
|
||||
clientNames := make(map[string]bool)
|
||||
|
@ -206,7 +206,7 @@ const (
|
||||
BisectResultMerge JobFlags = 1 << iota
|
||||
BisectResultNoop
|
||||
BisectResultRelease
|
||||
BisectResultBlacklist
|
||||
BisectResultIgnore
|
||||
)
|
||||
|
||||
func (job *Job) isUnreliableBisect() bool {
|
||||
@ -218,7 +218,7 @@ func (job *Job) isUnreliableBisect() bool {
|
||||
return job.Flags&BisectResultMerge != 0 ||
|
||||
job.Flags&BisectResultNoop != 0 ||
|
||||
job.Flags&BisectResultRelease != 0 ||
|
||||
job.Flags&BisectResultBlacklist != 0
|
||||
job.Flags&BisectResultIgnore != 0
|
||||
}
|
||||
|
||||
// Text holds text blobs (crash logs, reports, reproducers, etc).
|
||||
|
@ -24,9 +24,9 @@ func handleTestRequest(c context.Context, bugID, user, extID, link, patch, repo,
|
||||
jobCC []string) string {
|
||||
log.Infof(c, "test request: bug=%q user=%q extID=%q patch=%v, repo=%q branch=%q",
|
||||
bugID, user, extID, len(patch), repo, branch)
|
||||
for _, blacklisted := range config.EmailBlacklist {
|
||||
if user == blacklisted {
|
||||
log.Errorf(c, "test request from blacklisted user: %v", user)
|
||||
for _, blocked := range config.EmailBlocklist {
|
||||
if user == blocked {
|
||||
log.Errorf(c, "test request from blocked user: %v", user)
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ func TestJob(t *testing.T) {
|
||||
c.expectEQ(strings.Contains(body, "does not look like a valid git repo"), true)
|
||||
|
||||
c.incomingEmail(sender, "#syz test: git://git.git/git.git kernel-branch\n"+patch,
|
||||
EmailOptFrom("\"foo\" <blAcklisteD@dOmain.COM>"))
|
||||
EmailOptFrom("\"foo\" <blOcKed@dOmain.COM>"))
|
||||
c.expectNoEmail()
|
||||
pollResp := c.client2.pollJobs(build.Manager)
|
||||
c.expectEQ(pollResp.ID, "")
|
||||
|
@ -153,8 +153,8 @@ for name in to_disable:
|
||||
new_kconf.syms[name].set_value(0)
|
||||
|
||||
# Remove testing related symbols.
|
||||
blacklist = ['COMPILE_TEST']
|
||||
for sym in blacklist:
|
||||
removelist = ['COMPILE_TEST']
|
||||
for sym in removelist:
|
||||
new_kconf.syms[sym].set_value(0)
|
||||
|
||||
new_kconf.write_config()
|
||||
|
@ -177,10 +177,10 @@ const (
|
||||
type JobDoneFlags int64
|
||||
|
||||
const (
|
||||
BisectResultMerge JobDoneFlags = 1 << iota // bisected to a merge commit
|
||||
BisectResultNoop // commit does not affect resulting kernel binary
|
||||
BisectResultRelease // commit is a kernel release
|
||||
BisectResultBlacklist // commit is blacklisted, see syz-ci/jobs.go
|
||||
BisectResultMerge JobDoneFlags = 1 << iota // bisected to a merge commit
|
||||
BisectResultNoop // commit does not affect resulting kernel binary
|
||||
BisectResultRelease // commit is a kernel release
|
||||
BisectResultIgnore // this particular commit should be ignored, see syz-ci/jobs.go
|
||||
)
|
||||
|
||||
func (dash *Dashboard) JobPoll(req *JobPollReq) (*JobPollResp, error) {
|
||||
|
@ -80,8 +80,8 @@ See [this page](kernel_configs.md) for details.
|
||||
|
||||
### VM Setup
|
||||
|
||||
Syzkaller performs kernel fuzzing on slave virtual machines or physical devices.
|
||||
These slave enviroments are referred to as VMs.
|
||||
Syzkaller performs kernel fuzzing on worker virtual machines or physical devices.
|
||||
These worker enviroments are referred to as VMs.
|
||||
Out-of-the-box syzkaller supports QEMU, kvmtool and GCE virtual machines, Android devices and Odroid C2 boards.
|
||||
|
||||
These are the generic requirements for a syzkaller VM:
|
||||
|
@ -302,7 +302,7 @@ Use the following config:
|
||||
"type": "odroid",
|
||||
"vm": {
|
||||
"host_addr": "172.16.0.30",
|
||||
"slave_addr": "172.16.0.31",
|
||||
"device_addr": "172.16.0.31",
|
||||
"console": "/dev/ttyUSB0",
|
||||
"hub_bus": 3,
|
||||
"hub_device": 26,
|
||||
|
@ -3496,13 +3496,13 @@ static void setup_kcsan()
|
||||
}
|
||||
|
||||
#if SYZ_EXECUTOR // currently only used by executor
|
||||
static void setup_kcsan_filterlist(char** frames, int nframes, bool blacklist)
|
||||
static void setup_kcsan_filterlist(char** frames, int nframes, bool ignore)
|
||||
{
|
||||
int fd = open(KCSAN_DEBUGFS_FILE, O_WRONLY);
|
||||
if (fd == -1)
|
||||
fail("failed to open(\"%s\")", KCSAN_DEBUGFS_FILE);
|
||||
|
||||
const char* const filtertype = blacklist ? "blacklist" : "whitelist";
|
||||
const char* const filtertype = ignore ? "blacklist" : "whitelist";
|
||||
printf("adding functions to KCSAN %s: ", filtertype);
|
||||
dprintf(fd, "%s\n", filtertype);
|
||||
for (int i = 0; i < nframes; ++i) {
|
||||
|
@ -344,9 +344,9 @@ int main(int argc, char** argv)
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
if (argc >= 2 && strcmp(argv[1], "setup_kcsan_blacklist") == 0) {
|
||||
if (argc >= 2 && strcmp(argv[1], "setup_kcsan_filterlist") == 0) {
|
||||
#if SYZ_HAVE_KCSAN
|
||||
setup_kcsan_filterlist(argv + 2, argc - 2, /*blacklist=*/true);
|
||||
setup_kcsan_filterlist(argv + 2, argc - 2, /*ignore=*/true);
|
||||
#else
|
||||
fail("KCSAN is not implemented");
|
||||
#endif
|
||||
|
@ -8332,13 +8332,13 @@ static void setup_kcsan()
|
||||
}
|
||||
|
||||
#if SYZ_EXECUTOR
|
||||
static void setup_kcsan_filterlist(char** frames, int nframes, bool blacklist)
|
||||
static void setup_kcsan_filterlist(char** frames, int nframes, bool ignore)
|
||||
{
|
||||
int fd = open(KCSAN_DEBUGFS_FILE, O_WRONLY);
|
||||
if (fd == -1)
|
||||
fail("failed to open(\"%s\")", KCSAN_DEBUGFS_FILE);
|
||||
|
||||
const char* const filtertype = blacklist ? "blacklist" : "whitelist";
|
||||
const char* const filtertype = ignore ? "blacklist" : "whitelist";
|
||||
printf("adding functions to KCSAN %s: ", filtertype);
|
||||
dprintf(fd, "%s\n", filtertype);
|
||||
for (int i = 0; i < nframes; ++i) {
|
||||
|
@ -404,7 +404,6 @@ Index: usb-devel/drivers/usb/core/hub.c
|
||||
+++ usb-devel/drivers/usb/core/hub.c
|
||||
@@ -36,6 +36,10 @@
|
||||
#include "hub.h"
|
||||
#include "otg_whitelist.h"
|
||||
|
||||
+#undef dev_dbg
|
||||
+#define dev_dbg dev_info
|
||||
@ -452,7 +451,6 @@ Index: usb-devel/drivers/usb/core/hub.c
|
||||
+++ usb-devel/drivers/usb/core/hub.c
|
||||
@@ -36,6 +36,10 @@
|
||||
#include "hub.h"
|
||||
#include "otg_whitelist.h"
|
||||
|
||||
+#undef dev_dbg
|
||||
+#define dev_dbg dev_info
|
||||
|
@ -26,7 +26,7 @@ type linux struct {
|
||||
taskContext *regexp.Regexp
|
||||
cpuContext *regexp.Regexp
|
||||
questionableFrame *regexp.Regexp
|
||||
guiltyFileBlacklist []*regexp.Regexp
|
||||
guiltyFileIgnores []*regexp.Regexp
|
||||
reportStartIgnores []*regexp.Regexp
|
||||
infoMessagesWithStack [][]byte
|
||||
eoi []byte
|
||||
@ -54,7 +54,7 @@ func ctorLinux(cfg *config) (Reporter, []string, error) {
|
||||
ctx.cpuContext = regexp.MustCompile(`\[ *C[0-9]+\]`)
|
||||
ctx.questionableFrame = regexp.MustCompile(`(\[\<[0-9a-f]+\>\])? \? `)
|
||||
ctx.eoi = []byte("<EOI>")
|
||||
ctx.guiltyFileBlacklist = []*regexp.Regexp{
|
||||
ctx.guiltyFileIgnores = []*regexp.Regexp{
|
||||
regexp.MustCompile(`.*\.h`),
|
||||
regexp.MustCompile(`^lib/.*`),
|
||||
regexp.MustCompile(`^virt/lib/.*`),
|
||||
@ -423,7 +423,7 @@ func (ctx *linux) extractGuiltyFile(rep *Report) string {
|
||||
if strings.HasPrefix(rep.Title, "INFO: rcu detected stall") {
|
||||
// Special case for rcu stalls.
|
||||
// There are too many frames that we want to skip before actual guilty frames,
|
||||
// we would need to blacklist too many files and that would be fragile.
|
||||
// we would need to ignore too many files and that would be fragile.
|
||||
// So instead we try to extract guilty file starting from the known
|
||||
// interrupt entry point first.
|
||||
if pos := bytes.Index(report, []byte(" apic_timer_interrupt+0x")); pos != -1 {
|
||||
@ -439,7 +439,7 @@ func (ctx *linux) extractGuiltyFileImpl(report []byte) string {
|
||||
files := ctx.extractFiles(report)
|
||||
nextFile:
|
||||
for _, file := range files {
|
||||
for _, re := range ctx.guiltyFileBlacklist {
|
||||
for _, re := range ctx.guiltyFileIgnores {
|
||||
if re.MatchString(file) {
|
||||
continue nextFile
|
||||
}
|
||||
|
@ -422,15 +422,15 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error {
|
||||
if res.IsRelease {
|
||||
resp.Flags |= dashapi.BisectResultRelease
|
||||
}
|
||||
blacklistedCommits := []string{
|
||||
ignoredCommits := []string{
|
||||
// Commit "usb: gadget: add raw-gadget interface" adds a kernel interface for
|
||||
// triggering USB bugs, which ends up being the guilty commit during bisection
|
||||
// for USB bugs introduced before it.
|
||||
"f2c2e717642c66f7fe7e5dd69b2e8ff5849f4d10",
|
||||
}
|
||||
for _, commit := range blacklistedCommits {
|
||||
for _, commit := range ignoredCommits {
|
||||
if res.Commits[0].Hash == commit {
|
||||
resp.Flags |= dashapi.BisectResultBlacklist
|
||||
resp.Flags |= dashapi.BisectResultIgnore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -272,7 +272,7 @@ func (fuzzer *Fuzzer) useBugFrames(r *rpctype.ConnectRes, flagProcs int) func()
|
||||
}
|
||||
|
||||
if r.CheckResult.Features[host.FeatureKCSAN].Enabled && len(r.DataRaceFrames) != 0 {
|
||||
fuzzer.blacklistDataRaceFrames(r.DataRaceFrames)
|
||||
fuzzer.filterDataRaceFrames(r.DataRaceFrames)
|
||||
}
|
||||
|
||||
return gateCallback
|
||||
@ -302,11 +302,11 @@ func (fuzzer *Fuzzer) gateCallback(leakFrames []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func (fuzzer *Fuzzer) blacklistDataRaceFrames(frames []string) {
|
||||
args := append([]string{"setup_kcsan_blacklist"}, frames...)
|
||||
func (fuzzer *Fuzzer) filterDataRaceFrames(frames []string) {
|
||||
args := append([]string{"setup_kcsan_filterlist"}, frames...)
|
||||
output, err := osutil.RunCmd(10*time.Minute, "", fuzzer.config.Executor, args...)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to set KCSAN blacklist: %v", err)
|
||||
log.Fatalf("failed to set KCSAN filterlist: %v", err)
|
||||
}
|
||||
log.Logf(0, "%s", output)
|
||||
}
|
||||
|
31
tools/check-language.sh
Executable file
31
tools/check-language.sh
Executable file
@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2020 syzkaller project authors. All rights reserved.
|
||||
# Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
|
||||
|
||||
FILES=0
|
||||
FAILED=""
|
||||
shopt -s nocasematch
|
||||
for F in $(find . -name "*.go" -o -name "*.sh" -o -name "*.cc" -o -name "*.md" \
|
||||
-o -name "*.S" -o -name "*.py" -o -name "*.yml" -o -name "*.yaml" | \
|
||||
egrep -v "/vendor/|/gen/|executor/syscalls.h|pkg/csource/generated.go|tools/check-language.sh"); do
|
||||
((FILES+=1))
|
||||
L=0
|
||||
while IFS= read -r LINE; do
|
||||
((L+=1))
|
||||
if [[ $LINE =~ (slave|blacklist|whitelist) ]]; then
|
||||
if [[ $LINE =~ bond_enslave ]]; then
|
||||
continue
|
||||
fi
|
||||
SUGGESTIONS="block/allow/ignore/skip"
|
||||
if [[ $LINE =~ (slave) ]]; then
|
||||
SUGGESTIONS="leader/follower/coordinator/worker/parent/helper"
|
||||
fi
|
||||
echo "$F:$L:1: Please use more respectful terminology, consider using ${SUGGESTIONS} instead." \
|
||||
"See https://tools.ietf.org/id/draft-knodel-terminology-00.html for more info."
|
||||
echo "$LINE"
|
||||
FAILED="1"
|
||||
fi
|
||||
done < "$F"
|
||||
done
|
||||
if [ "$FAILED" != "" ]; then exit 1; fi
|
||||
echo "$FILES files checked" >&2
|
@ -55,7 +55,7 @@ typedef std::map<long long, std::string> kallsyms_map_t;
|
||||
|
||||
static __attribute__((noreturn)) __attribute__((format(printf, 1, 2))) void failf(const char* msg, ...);
|
||||
static kallsyms_map_t read_kallsyms();
|
||||
static bool is_blacklisted(const std::string& sym);
|
||||
static bool should_skip(const std::string& sym);
|
||||
static void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
|
||||
const std::string& start_sym, std::function<void(void)> fn);
|
||||
|
||||
@ -102,16 +102,16 @@ void probe_callback(uint64_t* cover, const kallsyms_map_t& kallsyms,
|
||||
if (!started && sym != start_sym)
|
||||
continue;
|
||||
started = true;
|
||||
if (!seen.insert(sym).second || is_blacklisted(sym))
|
||||
if (!seen.insert(sym).second || should_skip(sym))
|
||||
continue;
|
||||
printf("%0llx %s\n", pc, sym.c_str());
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
bool is_blacklisted(const std::string& sym)
|
||||
bool should_skip(const std::string& sym)
|
||||
{
|
||||
static const char* blacklist[] = {
|
||||
static const char* skip[] = {
|
||||
"security",
|
||||
"tomoyo",
|
||||
"selinux",
|
||||
@ -125,8 +125,8 @@ bool is_blacklisted(const std::string& sym)
|
||||
"snprintf",
|
||||
"vsnprintf",
|
||||
};
|
||||
for (size_t i = 0; i < sizeof(blacklist) / sizeof(blacklist[0]); i++) {
|
||||
if (!strncmp(sym.c_str(), blacklist[i], strlen(blacklist[i])))
|
||||
for (size_t i = 0; i < sizeof(skip) / sizeof(skip[0]); i++) {
|
||||
if (!strncmp(sym.c_str(), skip[i], strlen(skip[i])))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -33,12 +33,12 @@ func init() {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Host_Addr string // ip address of the host machine
|
||||
Slave_Addr string // ip address of the Odroid board
|
||||
Console string // console device name (e.g. "/dev/ttyUSB0")
|
||||
Hub_Bus int // host USB bus number for the USB hub
|
||||
Hub_Device int // host USB device number for the USB hub
|
||||
Hub_Port int // port on the USB hub to which Odroid is connected
|
||||
Host_Addr string // ip address of the host machine
|
||||
Device_Addr string // ip address of the Odroid board
|
||||
Console string // console device name (e.g. "/dev/ttyUSB0")
|
||||
Hub_Bus int // host USB bus number for the USB hub
|
||||
Hub_Device int // host USB device number for the USB hub
|
||||
Hub_Port int // port on the USB hub to which Odroid is connected
|
||||
}
|
||||
|
||||
type Pool struct {
|
||||
@ -62,8 +62,8 @@ func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
|
||||
if cfg.Host_Addr == "" {
|
||||
return nil, fmt.Errorf("config param host_addr is empty")
|
||||
}
|
||||
if cfg.Slave_Addr == "" {
|
||||
return nil, fmt.Errorf("config param slave_addr is empty")
|
||||
if cfg.Device_Addr == "" {
|
||||
return nil, fmt.Errorf("config param device_addr is empty")
|
||||
}
|
||||
if cfg.Console == "" {
|
||||
return nil, fmt.Errorf("config param console is empty")
|
||||
@ -133,7 +133,7 @@ func (inst *instance) ssh(command string) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
args := append(vmimpl.SSHArgs(inst.debug, inst.sshkey, 22), "root@"+inst.cfg.Slave_Addr, command)
|
||||
args := append(vmimpl.SSHArgs(inst.debug, inst.sshkey, 22), "root@"+inst.cfg.Device_Addr, command)
|
||||
if inst.debug {
|
||||
Logf(0, "running command: ssh %#v", args)
|
||||
}
|
||||
@ -272,7 +272,7 @@ func (inst *instance) repair() error {
|
||||
}
|
||||
|
||||
func (inst *instance) waitForSSH(timeout time.Duration) error {
|
||||
return vmimpl.WaitForSSH(inst.debug, timeout, inst.cfg.Slave_Addr, inst.sshkey, "root", inst.os, 22)
|
||||
return vmimpl.WaitForSSH(inst.debug, timeout, inst.cfg.Device_Addr, inst.sshkey, "root", inst.os, 22)
|
||||
}
|
||||
|
||||
func (inst *instance) Close() {
|
||||
@ -282,7 +282,7 @@ func (inst *instance) Close() {
|
||||
func (inst *instance) Copy(hostSrc string) (string, error) {
|
||||
basePath := "/data/"
|
||||
vmDst := filepath.Join(basePath, filepath.Base(hostSrc))
|
||||
args := append(vmimpl.SCPArgs(inst.debug, inst.sshkey, 22), hostSrc, "root@"+inst.cfg.Slave_Addr+":"+vmDst)
|
||||
args := append(vmimpl.SCPArgs(inst.debug, inst.sshkey, 22), hostSrc, "root@"+inst.cfg.Device_Addr+":"+vmDst)
|
||||
cmd := osutil.Command("scp", args...)
|
||||
if inst.debug {
|
||||
Logf(0, "running command: scp %#v", args)
|
||||
@ -322,7 +322,7 @@ func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command strin
|
||||
}
|
||||
|
||||
args := append(vmimpl.SSHArgs(inst.debug, inst.sshkey, 22),
|
||||
"root@"+inst.cfg.Slave_Addr, "cd /data; "+command)
|
||||
"root@"+inst.cfg.Device_Addr, "cd /data; "+command)
|
||||
if inst.debug {
|
||||
Logf(0, "running command: ssh %#v", args)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user