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

pkgxdev / pkgx / 28956929277

08 Jul 2026 04:01PM UTC coverage: 92.699% (+0.5%) from 92.163%
28956929277

Pull #1276

github

web-flow
Merge 9ce995554 into a4d957590
Pull Request #1276: v1: bump libpkgx

630 of 667 branches covered (94.45%)

1384 of 1493 relevant lines covered (92.7%)

21.05 hits per line

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

91.47
/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
7
  /// but otherwise the user has set stuff so we should use it
8
  for (const [key, value] of Object.entries(Deno.env.toObject())) {
9✔
9
    env[key] ??= value
1,044✔
10
  }
1,044✔
11

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

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

18
  const errno = (() => {
9✔
19
    let tries = 10;
9✔
20
    while (true) {
9✔
21
      const errno = _internals.execve(
9✔
22
        Deno.UnsafePointer.of(path),
9✔
23
        Deno.UnsafePointer.of(argv),
9✔
24
        Deno.UnsafePointer.of(envp)
9✔
25
      );
26
      if (!tries--) return errno;
×
27
      // 11 = EAGAIN = Try again
28
      if (errno == 11) continue;
×
29
      return errno;
9✔
30
    }
9✔
31
  })();
9✔
32

33
  switch (errno) {
9✔
34
    case 2:   //ENOENT:
9✔
35
    case 22:  //EINVAL:  (on Linux we're getting this for file is not executable since Deno 2 :/)
9✔
36
    case 316: //FIXME ALERT! ALERT! BUG! SOMETHING IS WRONG WITH OUR USE OR ERRNO AND THIS SOMETIMES RESULTS, USUALLY ON MACOS :/
9✔
37
      // yes: strange behavior from execve here indeed
38
      if (parse_Path(args[0])?.exists()) {
4✔
39
        throw new Deno.errors.PermissionDenied()
2✔
40
      } else {
2✔
41
        throw new Deno.errors.NotFound()
2✔
42
      }
2✔
43
    case 13:
9✔
44
      throw new Deno.errors.PermissionDenied()
1✔
45
    case 63: //ENAMETOOLONG:
×
46
    case 7:  //E2BIG:
×
47
    case 14: //EFAULT:
×
48
    case 5:  //EIO:
×
49
    case 62: //ELOOP:
×
50
    case 8:  //ENOEXEC:
×
51
    case 12: //ENOMEM:
×
52
    case 20: //ENOTDIR:
×
53
    case 26: //ETXTBSY:
9!
54
      throw new PkgxError(`execve (${errno})`)
×
55
  }
9✔
56

57
  throw new ProgrammerError(`execve (${errno})`)
4✔
58
}
9✔
59

60
export function parse_Path(input: string) {
3✔
61
  const p = Path.abs(input)
4✔
62
  if (p) return p
4✔
63
  try {
2✔
64
    return _internals.getcwd().join(input)
2✔
65
  } catch {
4✔
66
    return
1✔
67
  }
1✔
68
}
4✔
69

70
function find_in_PATH(cmd: string[], PATH: string | undefined, HOME: Path | undefined) {
9✔
71
  if (cmd[0].startsWith("/")) {
9✔
72
    return
3✔
73
  }
3✔
74
  if (cmd[0].includes("/")) {
9✔
75
    cmd[0] = _internals.getcwd().join(cmd[0]).string
1✔
76
    return
1✔
77
  }
1✔
78

79
  PATH ??= "/usr/bin:/bin"  // see manpage for execvp(3)
5✔
80

81
  for (const part of PATH.split(':')) {
9✔
82
    const path = (() => {
76✔
83
      if (part == '.') return _internals.getcwd()
76✔
84
      if (part == '~') return HOME
76✔
85
      if (part.startsWith('~/')) return HOME?.join(part.slice(2))
76✔
86
      //FIXME: not handled: ~user/...
87
      return new Path(part)
73✔
88
    })()?.join(cmd[0])
76✔
89
    if (path?.isExecutableFile()) {
76✔
90
      cmd[0] = path.string
1✔
91
      return
1✔
92
    }
1✔
93
  }
76✔
94
}
9✔
95

96

97
////// COPY PASTA
98
// actually minor mods to add trailing null
99
// https://github.com/aapoalas/libclang_deno/blob/main/lib/utils.ts
100

101
const ENCODER = new TextEncoder();
3✔
102

103
export class CStringArray extends Uint8Array {
3✔
104
  constructor(strings: string[]) {
3✔
105
    let stringsLength = 0;
18✔
106
    for (const string of strings) {
18✔
107
      // maximum bytes for a utf8 string is 4×length
1,100✔
108
      stringsLength += string.length * 4 + 1;
1,100✔
109
    }
1,100✔
110
    super(8 * (strings.length + 1) + stringsLength);
18✔
111
    const pointerBuffer = new BigUint64Array(this.buffer, 0, strings.length + 1);
18✔
112
    const stringsBuffer = new Uint8Array(this.buffer).subarray(
18✔
113
      (strings.length + 1) * 8,
18✔
114
    );
115
    const basePointer = BigInt(
18✔
116
      Deno.UnsafePointer.value(Deno.UnsafePointer.of(stringsBuffer)),
18✔
117
    );
118
    let index = 0;
18✔
119
    let offset = 0;
18✔
120
    for (const string of strings) {
18✔
121
      const start = offset;
1,100✔
122
      const result = ENCODER.encodeInto(
1,100✔
123
        string,
1,100✔
124
        stringsBuffer.subarray(start),
1,100✔
125
      );
126
      offset = start + result.written + 1; // Leave null byte
1,100✔
127
      pointerBuffer[index++] = basePointer + BigInt(start);
1,100✔
128
    }
1,100✔
129
  }
18✔
130
}
3✔
131

132
export const cstr = (string: string): Uint8Array =>
3✔
133
  ENCODER.encode(`${string}\0`);
3✔
134

135
function execve(arg0: Deno.PointerValue, args: Deno.PointerValue, env: Deno.PointerValue) {
6✔
136
  const filename = host().platform == 'darwin' ? '/usr/lib/libSystem.dylib' : 'libc.so.6'
6!
137

138
  const libc = Deno.dlopen(
6✔
139
    filename, {
6✔
140
      execve: {
6✔
141
        parameters: ["pointer", "pointer", "pointer"],
6✔
142
        result: "i32"
6✔
143
      },
6✔
144
      errno: {
6✔
145
        type: "i32"
6✔
146
      }
6✔
147
    }
6✔
148
  )
149

150
  try {
6✔
151
    libc.symbols.execve(arg0, args, env)
6✔
152
    return libc.symbols.errno
6✔
153
  } finally {
6✔
154
    libc.close()
6✔
155
  }
6✔
156
}
6✔
157

158
export const _internals = {
3✔
159
  execve,
3✔
160
  getcwd: Path.cwd
3✔
161
}
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