pkg icon indicating copy to clipboard operation
pkg copied to clipboard

Cant run executable from cmd spawned by itself

Open ErAz7 opened this issue 2 years ago • 15 comments

Problem

PKG throws erroor when running windows executable from a process spawned by itself

Steps to Reproduce

lets say I have a simple nodejs app like this:

import cp from 'child_process';
cp.exec('start cmd');

so obviously it starts a new cmd every time it gets executed. Now I build it with PKG into start.exe, then I open a cmd in the folder and run start.exe. It works fine and opens a new cmd, now, if I run start.exe in this new cmd, I'll get the following error:

pkg/prelude/bootstrap.js:1523
      if (error.code !== 'MODULE_NOT_FOUND') throw error;
                                             ^

TypeError: Cannot read property 'charAt' of undefined
[90m    at Function.Module._resolveLookupPaths (internal/modules/cjs/loader.js:617:15)[39m
[90m    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:834:20)[39m
    at Function._resolveFilename (pkg/prelude/bootstrap.js:1521:44)
[90m    at Function.Module._load (internal/modules/cjs/loader.js:725:27)[39m
    at Function.runMain (pkg/prelude/bootstrap.js:1558:12)
[90m    at internal/main/run_main_module.js:17:47[39m

Apparently, it cannot find the file name because argument[0] is undefined while it's supposed to be a path

CC @leerob @jesec @igorklopov

ErAz7 avatar Nov 03 '21 22:11 ErAz7

btw this works fine when I directly run the source with node

ErAz7 avatar Nov 04 '21 13:11 ErAz7

I'm having the same issue!

SamuelCharpentier avatar Nov 30 '21 01:11 SamuelCharpentier

same issue, run itself, even with bash sub process

goshander avatar Jan 31 '22 05:01 goshander

same

CheneyWong avatar Mar 23 '22 13:03 CheneyWong

PKG modify the node native child_process lib. (prelude\bootstrap.js line 1990).

It add a var env PKG_EXECPATH wich is used in (prelude\bootstrap.js line 77).

if (process.env.PKG_EXECPATH === EXECPATH) {
  process.argv.splice(1, 1); // Remove PKG_DUMMY_ENTRYPOINT

  if (process.argv[1] && process.argv[1] !== '-') {
    // https://github.com/nodejs/node/blob/1a96d83a223ff9f05f7d942fb84440d323f7b596/lib/internal/bootstrap/node.js#L269
    process.argv[1] = path.resolve(process.argv[1]);
  }
} else {
  process.argv[1] = DEFAULT_ENTRYPOINT;
}

[, ENTRYPOINT] = process.argv;
delete process.env.PKG_EXECPATH;

When script.exe spawn itselft, PKG_EXECPATH and EXECPATH is the same.

This cause ENTRYPOINT have the wrong value.

script used to reproduce

const readline = require('readline')
const { spawn } = require('child_process')

const spawnSelf = function () {
    return new Promise((resolve, reject) => {
        try {
            const worker = spawn(process.execPath, ['child'], { env: {} })
            worker.on('error', (error) => {
                console.error(`Error ${error.stack}`)
                return reject(error)
            })
            worker.on('exit', code => {
                console.log(`Exit code ${code}`)
                return resolve()
            })
            const lineParser = readline.createInterface({ input: worker.stdout })
            const stderrParser = readline.createInterface({ input: worker.stderr })
            stderrParser.on('line', line => {
                console.error(`Stderr : ${line.toString()}`)
            })
            lineParser.on('line', data => {
                console.info(`Stdout : ${data.toString()}`)
            })
        }
        catch (error) {
            return reject(error)
        }
    })
}

const test = async function () {
    console.log('process.argv0', process.argv0)
    console.log('process.execPath', process.execPath)

    if (process.argv.includes('child')) {
        console.log('CHILD SPAWNED SUCCESS')
        process.exit(0)
    }
    await spawnSelf()
}
test()
    .catch(console.error)

Can you help us solve this issue ?

orgrimarr avatar May 30 '22 15:05 orgrimarr

PR?

robertsLando avatar May 30 '22 15:05 robertsLando

What do you mean by PR ? I don't have any code fixing this issue to push

orgrimarr avatar May 30 '22 15:05 orgrimarr

You have identified where the issue is, just try to fix it

robertsLando avatar May 30 '22 15:05 robertsLando

Of course we will work on this issue and submit a PR when we find a fix.

But it would be nice to have some help on this, it seems many people opened issue regarding this king of bug. So you should have some experience on this point.

There is some part in the code we dont understand. For exemple :

  • What is the usage of PKG_DUMMY_ENTRYPOINT
  • Why does pkg add PKG_EXECPATH
  • What is the purpose of this code (line 80 in bootstrap.js
  if (process.argv[1] && process.argv[1] !== '-') {
    // https://github.com/nodejs/node/blob/1a96d83a223ff9f05f7d942fb84440d323f7b596/lib/internal/bootstrap/node.js#L269
    process.argv[1] = path.resolve(process.argv[1]);
  }

Is there any documentation on how pkg work ? It will help us understanding and fixing this issue

orgrimarr avatar May 31 '22 08:05 orgrimarr

I started a wiki for devs some months ago, try to read this: https://github.com/vercel/pkg/wiki/Developers

robertsLando avatar May 31 '22 08:05 robertsLando

Thanks

orgrimarr avatar May 31 '22 09:05 orgrimarr

Current logic

FORK         : [process.exe, PKG_DUMMY_ENTRYPOINT, child.js,   arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH   => delete PKG_DUMMY_ENTRYPOINT
CLUSTER      : [process.exe, PKG_DUMMY_ENTRYPOINT, process.js, arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH   => delete PKG_DUMMY_ENTRYPOINT

SPAWN        : [process_2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]			                        		 => replace PKG_DUMMY_ENTRYPOINT by DEFAULT_ENTRYPOINT
SPAWN SELF   : [process.exe,   PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...], process.env.PKG_EXECPATH === EXECPATH		 => delete PKG_DUMMY_ENTRYPOINT => /!\ KO [process.exe, arg1, arg2, ...] /!\

Expected logic

FORK         : [process.exe, child.js,   arg1, arg2, ...]  
CLUSTER      : [process.exe, process.js, arg1, arg2, ...]  

SPAWN        : [process2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]
SPAWN SELF   : [process.exe,  PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]		

if args[1] === PKG_DUMMY_ENTRYPOINT => replace PKG_DUMMY_ENTRYPOINT with DEFAULT_ENTRYPOINT

Analyse

PKG includes a mecanism to detect forked process in order to fix the args. (bootstrap line 77). It use process.env.PKG_EXECPATH === EXECPATH to detect it.

When we use spawn, execFile, ... to spawn process.execPath. PKG detect that PKG_EXECPATH is equal to EXECPATH and think this is a fork.

The issue is that the arguments of a fork and a spawn are not the same.

  • fork : process.exe, PKG_DUMMY_ENTRYPOINT, child.js, arg1, arg2, ...
  • spawn : process_2.exe, PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...

This cause PKG crashed is that case.

Proposal

Improving the PKG_DUMMY_ENTRYPOINT logic is painfull, because this arg is added through a NodeJS patch in pkg-fetch.

We need to keep the current logic with PKG_EXECPATH.

We should detect the spawn self case and modify the argument of the spawn to match the arguments signature of a fork.

process.argv[1] is the entrypoint of a PKG process.

SPAWN SELF   : [process.exe,  PKG_DUMMY_ENTRYPOINT, arg1, arg2, ...]  => [process.exe, PKG_DUMMY_ENTRYPOINT, process.argv[1], arg1, arg2, ...]

Fix

bootstrap.js setOptsEnv()

Pseudo code fix. isNotCalledByFork logic to be defined

if (args[0] === ARGV0 || args[0] === ENTRYPOINT || args[0] === EXECPATH) { // Spawn self
    if(isNotCalledByFork()) { // To be defined
    if(!Array.isArray(args[1])) args[1] = []; 
    args[1] = [process.argv[1], ...args[1]];
    }
}

Workarounds

spawn(process.execPath, [process.argv[1], arg1, arg2, ...], {})
fork(process.argv[1], [arg1, arg2, ...], {})

orgrimarr avatar Jun 01 '22 10:06 orgrimarr

Too painfull to detect the right case and apply the fix just for that case. I think i will stop working on this issue.

I can live with the work around. (Adding process.argv[1] as first arg of the spawn command).

spawn(process.execPath, [process.argv[1], arg1, arg2, ...], options)
spawn(myprocess.exe, [process.argv[1], arg1, arg2, ...], options)

orgrimarr avatar Jun 02 '22 16:06 orgrimarr

This issue is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 5 days. To ignore this issue entirely you can add the no-stale label

github-actions[bot] avatar Sep 01 '22 00:09 github-actions[bot]

not tried but orgrimarr's workaround seems to work, anyways this is still not fixed

ErAz7 avatar Sep 02 '22 10:09 ErAz7

is there a solution to this problem? I facing a similar issue when working with cluster inside pkg.

lamualfa avatar Oct 26 '22 12:10 lamualfa

Any update on this issue?

jcs090218 avatar Jan 10 '23 05:01 jcs090218

same as https://github.com/vercel/pkg/issues/1866

introspection3 avatar Feb 12 '23 10:02 introspection3

this bug is alive in linux also... using the workaround for now, thanks @orgrimarr

In case someone else gets ELF errors, what did it is a modified version of the workaround:

spawn(process.execPath, [process.pkg.entrypoint , arg1, arg2, ...], {})

jaime-ez avatar Feb 27 '23 20:02 jaime-ez

This issue is stale because it has been open 90 days with no activity. Remove the stale label or comment or this will be closed in 5 days. To ignore this issue entirely you can add the no-stale label

github-actions[bot] avatar May 31 '23 00:05 github-actions[bot]

This issue is now closed due to inactivity, you can of course reopen or reference this issue if you see fit.

github-actions[bot] avatar Jun 06 '23 00:06 github-actions[bot]

Can someone reopen this issue?

jcs090218 avatar Jun 06 '23 02:06 jcs090218