• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pkgxdev / pkgx / 7298730073

22 Dec 2023 10:14AM UTC coverage: 96.0% (-1.4%) from 97.388%
7298730073

push

github

mxcl
Escape `pkgx +pkg`; Fixes #902

369 of 393 branches covered (0.0%)

2 of 2 new or added lines in 1 file covered. (100.0%)

20 existing lines in 4 files now uncovered.

1407 of 1457 relevant lines covered (96.57%)

69.65 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.96
/src/utils/execve.ts
1
import { ProgrammerError } from "./error.ts"
3✔
2
import { utils, Path, PkgxError } from "pkgx"
3✔
3
const { host } = utils
3✔
4

5
export default function({cmd: args, env}: {cmd: string[], env: Record<string, string>}): never {
3✔
6
  /// we need to forward any other env, some vars like HOME are super important
12✔
7
  /// but otherwise the user has set stuff so we should use it
12✔
8
  for (const [key, value] of Object.entries(Deno.env.toObject())) {
12✔
9
    env[key] ??= value
1,074✔
10
  }
1,074✔
11

12
  find_in_PATH(args, env.PATH, Path.abs(env.HOME))
12✔
13

14
  const path = cstr(args[0])
12✔
15
  const argv = new CStringArray(args)
12✔
16
  const envp = new CStringArray(Object.entries(env).map(([key, value]) => `${key}=${value}`))
12✔
17

18
  const errno = _internals.execve(
12✔
19
    Deno.UnsafePointer.of(path),
12✔
20
    Deno.UnsafePointer.of(argv),
12✔
21
    Deno.UnsafePointer.of(envp))
12✔
22

23
  switch (errno) {
12✔
24
    case 2:  //ENOENT:
12✔
25
      // yes: strange behavior from execve here indeed
16✔
26
      if (parse_Path(args[0])?.exists()) {
16✔
27
        throw new Deno.errors.PermissionDenied()
18✔
28
      } else {
18✔
29
        throw new Deno.errors.NotFound()
18✔
30
      }
18✔
31
    case 13:
12✔
32
    case 316:  //FIXME ALERT! ALERT! BUG! SOMETHING IS WRONG WITH OUR USE OR ERRNO AND THIS SOMETIMES RESULTS :/
12✔
33
      throw new Deno.errors.PermissionDenied()
13✔
UNCOV
34
    case 63: //ENAMETOOLONG:
×
UNCOV
35
    case 7:  //E2BIG:
×
UNCOV
36
    case 14: //EFAULT:
×
UNCOV
37
    case 5:  //EIO:
×
UNCOV
38
    case 62: //ELOOP:
×
UNCOV
39
    case 8:  //ENOEXEC:
×
UNCOV
40
    case 12: //ENOMEM:
×
UNCOV
41
    case 20: //ENOTDIR:
×
UNCOV
42
    case 26: //ETXTBSY:
×
UNCOV
43
      throw new PkgxError(`execve (${errno})`)
×
44
  }
12✔
45

46
  throw new ProgrammerError(`execve (${errno})`)
16✔
47
}
12✔
48

49
export function parse_Path(input: string) {
3✔
50
  const p = Path.abs(input)
7✔
51
  if (p) return p
7✔
52
  try {
9✔
53
    return _internals.getcwd().join(input)
9✔
54
  } catch {
7✔
55
    return
8✔
56
  }
8✔
57
}
7✔
58

59
function find_in_PATH(cmd: string[], PATH: string | undefined, HOME: Path | undefined) {
12✔
60
  if (cmd[0].startsWith("/")) {
12✔
61
    return
15✔
62
  }
15✔
63
  if (cmd[0].includes("/")) {
12✔
64
    cmd[0] = _internals.getcwd().join(cmd[0]).string
13✔
65
    return
13✔
66
  }
13✔
67

68
  PATH ??= "/usr/bin:/bin"  // see manpage for execvp(3)
17✔
69

70
  for (const part of PATH.split(':')) {
12✔
71
    const path = (() => {
88✔
72
      if (part == '.') return _internals.getcwd()
164✔
73
      if (part == '~') return HOME
164✔
74
      if (part.startsWith('~/')) return HOME?.join(part.slice(2))
164✔
75
      //FIXME: not handled: ~user/...
237✔
76
      return new Path(part)
237✔
77
    })()?.join(cmd[0])
88✔
78
    if (path?.isExecutableFile()) {
88✔
79
      cmd[0] = path.string
89✔
80
      return
89✔
81
    }
89✔
82
  }
88✔
83
}
12✔
84

85

86
////// COPY PASTA
1✔
87
// actually minor mods to add trailing null
1✔
88
// https://github.com/aapoalas/libclang_deno/blob/main/lib/utils.ts
1✔
89

90
const ENCODER = new TextEncoder();
3✔
91

92
export class CStringArray extends Uint8Array {
3✔
93
  constructor(strings: string[]) {
3✔
94
    let stringsLength = 0;
21✔
95
    for (const string of strings) {
21✔
96
      // maximum bytes for a utf8 string is 4×length
1,142✔
97
      stringsLength += string.length * 4 + 1;
1,142✔
98
    }
1,142✔
99
    super(8 * (strings.length + 1) + stringsLength);
21✔
100
    const pointerBuffer = new BigUint64Array(this.buffer, 0, strings.length + 1);
21✔
101
    const stringsBuffer = new Uint8Array(this.buffer).subarray(
21✔
102
      (strings.length + 1) * 8,
21✔
103
    );
104
    const basePointer = BigInt(
21✔
105
      Deno.UnsafePointer.value(Deno.UnsafePointer.of(stringsBuffer)),
21✔
106
    );
107
    let index = 0;
21✔
108
    let offset = 0;
21✔
109
    for (const string of strings) {
21✔
110
      const start = offset;
1,142✔
111
      const result = ENCODER.encodeInto(
1,142✔
112
        string,
1,142✔
113
        stringsBuffer.subarray(start),
1,142✔
114
      );
115
      offset = start + result.written + 1; // Leave null byte
1,142✔
116
      pointerBuffer[index++] = basePointer + BigInt(start);
1,142✔
117
    }
1,142✔
118
  }
21✔
119
}
3✔
120

121
export const cstr = (string: string): Uint8Array =>
3✔
122
  ENCODER.encode(`${string}\0`);
3✔
123

124
function execve(arg0: Deno.PointerValue, args: Deno.PointerValue, env: Deno.PointerValue) {
9✔
125
  const filename = host().platform == 'darwin' ? '/usr/lib/libSystem.dylib' : 'libc.so.6'
×
126

127
  const libc = Deno.dlopen(
9✔
128
    filename, {
9✔
129
      execve: {
9✔
130
        parameters: ["pointer", "pointer", "pointer"],
45✔
131
        result: "i32"
9✔
132
      },
9✔
133
      errno: {
9✔
134
        type: "i32"
9✔
135
      }
9✔
136
    }
9✔
137
  )
138

139
  try {
9✔
140
    libc.symbols.execve(arg0, args, env)
9✔
141
    return libc.symbols.errno
9✔
142
  } finally {
9✔
143
    libc.close()
9✔
144
  }
9✔
145
}
9✔
146

147
export const _internals = {
3✔
148
  execve,
3✔
149
  getcwd: Path.cwd
3✔
150
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc