-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexecute.js
122 lines (110 loc) · 3.76 KB
/
execute.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use strict'
import { execSync } from 'child_process'
import { ensureTrue, ensureWellFormedUser, fail, pass, validateOptions } from './assert.js'
import { warn } from './log.js'
import { trim } from './_common.js'
import { info } from 'console'
/* WARN: '&>/dev/null' might suppress error codes and wrongly return 0 (wrongly: ok) */
export const guard = (cmd, config = {}) => {
ensureTrue(
typeof config !== 'boolean',
'forgot to change a mute param `true|false` to config object?'
)
validateOptions(config, ['mute', 'noPass', 'timeout', 'errMsg'])
const mute = config.mute === true
const noPass = config.noPass === true
const errMsg = !!config.errMsg ? config.errMsg + '\n' : ''
const timeout = config.timeout ? config.timeout : 120 * 1000
try {
// Synchronous execution of the command
const stdout = execSync(cmd, {
timeout,
stdio: mute ? ['ignore', 'pipe', 'ignore'] : ['pipe', 'pipe', 'inherit'],
encoding: 'utf8',
})
const trimmedResult = trim(stdout.toString(), '\n')
if (!noPass) pass(cmd)
if (!mute && trimmedResult !== '') { info(trimmedResult) }
return trimmedResult
} catch (error) {
fail(
'guard() failed',
`status: ${error.status}`,
`message: ${errMsg}${error.message}`,
`stderr: ${error.stderr?.toString() || 'no stderr output'}`,
`stdout: ${error.stdout?.toString() || 'no stdout output'}`
)
}
}
/* Userguard function unchanged */
export const userguard = (user, userCmd, config = {}) => {
ensureTrue(
config !== true,
'forgot to change a mute true to config object'
)
validateOptions(config, ['mute', 'timeout', 'useSudo'])
const useSudo = config.useSudo === true
ensureWellFormedUser(user)
// user will have (most of) its typical env varibles
// NOTE: user will have its home dir as current path
// escape quotes in the command, since it gets put into outer quotes
const escapedUserCmd = userCmd.replace(/"/g, '\\"')
let cmd
if (useSudo) {
warn('using `sudo`')
// note: might work better without `-i` (on env variables)
cmd = `sudo -Hu "${user}" -- sh -c "${escapedUserCmd}"`
} else {
// single quotes against „premature“ variable substitution
// stackoverflow.com/a/50119758
cmd = `runuser -l '${user}' -c '${escapedUserCmd}'`
}
return guard(cmd, config)
}
/* like guard, except, returns true or false (thus not “enforcing”)
NOTE: by default returns error code (not result, like [user]guard())
NOTE: on error code: 0 is 'falsy' but means everything is ok,
truthy other value if unsuccessful
██
WARNING: '&>/dev/null' might suppress error codes and wrongly return 0 (everything ok)
*/
export const check = (cmd, config = {}) => {
ensureTrue(
config !== true,
'forgot to change a mute true to config object'
)
validateOptions(config, ['mute', 'timeout', 'getResult'])
const mute = config && config.mute ? !!config.mute : false
const timeout = config && config.timeout ? config.timeout : 120 * 1000
const getResult = config && config.getResult === true
try {
const stdout = execSync(cmd, {
stdio: mute ? [] : 'pipe',
encoding: 'utf8',
timeout
})
const trimmedResult = trim(stdout.toString(), '\n')
if (!mute) {
pass(cmd)
info('status (pass):', 0)
if (trimmedResult !== '') info(trimmedResult)
}
if (getResult) return trimmedResult
return 0
} catch (error) {
if (error.code === 'ETIMEDOUT') warn('check: cause was TIMEOUT!')
const trimmedResult = trim(error.stdout?.toString() || '', '\n')
if (!mute) {
warn('result: ', trimmedResult)
warn('cmd: ', cmd)
warn('status: ', error.status)
}
if (getResult) return trimmedResult
return error.status
}
}
export default {
guard,
userguard,
check
}