Exec nodejs and get output on error - node.js

I am trying to fire a command via exec in my go binary to get JSON output of the other script. The other script is a nodejs task checking for html problems.
If I fire the nodeJS task on cli everything works fine and I get JSON output, but if I fire the command inside of go I only get:
exit status 1
I am not total sure if the problem is a nodejs or a go problem, but even if nodejs founds HTML Problems I want to be able to analyze the JSON Response in my go script.
Here is my source code:
out, err := exec.Command("/usr/bin/testcafe", "'chromium:headless --no-sandbox'", "--reporter json", "/data/checks.js").Output()
status := http.StatusOK
message := ""
if err != nil {
status = http.StatusNotImplemented
message = err.Error() + string(string(out[:]))
fmt.Println(message)
}

As mentioned above if you ever need to access Stderr from an Command Exit in Golang use:
message += string(err.(*exec.ExitError).Stderr[:])
In my case the nodejs tool gave an exit code based on the amounts of problems. Solved this it runs perfectly now =).

I made a function that I use to do shell command execution:
https://gist.github.com/leninhasda/8f2b5cdc22677a8f2bdf2e896eb0b561
stdOut, stdErr, err := ShellExec("echo", "hello", "world")
if err != nil {
// error executing command
}
if stdErr != "" {
// shell command returned error message
}
if stdOut != "" {
// shell command returned output message
// hello world
}

Related

"Error: Command failed: ..." when I execute commands with exec / execSync

const {execSync , exec} = require('child_process')
const res = execSync("help")
console.log(res.toString())
For every command I try to execute (in this case 'help') it throws the error "Error: Command Failed .... not found". What I do not understand?
Dive in NodeJs child process module
I decided to look at NodeJS source code at here to understand the problem. execSync method use spawnSync to run the command, and in the end, it calls checkExecSyncError. In the latter, you can see that if the status code is not 0, it will throw the error "Command failed ...".
Then I try to run const res = spawnSync("help");, and res.status gives me 1. So, the command help actually yields an "invisible" error although we can see the expected output on the terminal.
Note: Exit status 1 is the catch-all for general error, AFAIK. Exit status 0 means success. Further reading
Confirm in the terminal
I go to my terminal to confirm this, I ran help then echo %ERRORLEVEL%. (thank Tim Gilbert) and I received 1. So it totally makes sense for me.
Even the command throws an error, we can still get the output in this case. You can refer to my code below:
const { execSync } = require('child_process')
console.log(process.env.ComSpec);// verify what terminal is used
try {
// success command
const resNpmVersion = execSync("npm -v");
console.log("success", resNpmVersion.toString());
// failed command, the result is printed in the catch block
const resHelp = execSync("HELP");
} catch (error) {
console.log(error.message);
console.log("error", error.stdout.toString());
}
Lesson learned: Check the exit status of the command :)
(I did my test on Windows 10)

How to restart itself in Go daemon process?

I use go-daemon library to fork process and run it in background. And I need to restart the daemon process after update performed from within http handler.
The handler code is
func httpUpdate(w http.ResponseWriter, req *http.Request) {
if !isPost(req.Method) {
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
return
}
if checkAuth(req) != 200 {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
log.Println("INFO: Update request, checking for update...")
var err = doUpdate(UPDATE_URL, nil, false)
if !isError(err) {
log.Println("INFO: Update successful, exit")
var system = RealSystem{}
system.Run(fmt.Sprintf("(sleep 0.3s && %s start &> /test/extra.log)&disown", appFilename()))
system.Exit(0)
return
}
w.WriteHeader(http.StatusNoContent)
}
doUpdate() returns nil if successfully replaced the executable file. RealSystem is just wrapper for exec.Command and os.Exit(). appFilename() is the executable file name. The command to start app is /path/to/app start.
I see that new process starts, but executing Context::Reborn() fails with EOF error. Looks like some intrinsic pipes used as implementation details fail with EOF (may be...).
What would be the reason? Or may be there is a better way of doing that?
For now everything happens inside docker container in the "context" of e2e test if it matters. I spent hours trying to make it work but with no success.
I assume you mean restarting the currently running Go binary. You can use a syscall for unix-based systems, and use an exec.Command for Windows.
func RestartSelf() error {
self, err := osext.Executable()
if err != nil {
return err
}
args := os.Args
env := os.Environ()
// Windows does not support exec syscall.
if runtime.GOOS == "windows" {
cmd := exec.Command(self, args[1:]...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
cmd.Env = env
err := cmd.Run()
if err == nil {
os.Exit(0)
}
return err
}
return syscall.Exec(self, args, env)
}
The issue is specific to the library. Spawn new self instance from within child process is not a problem for the system, but for that library.
To achieve this it's necessary to execute something like that.
Note the _GO_DAEMON=0 variable set to zero. This makes library follow parent control flow.
var cmd = exec.Command("bash", "-c", fmt.Sprintf("sleep 0.5s; _GO_DAEMON=0 %s start", appFilename()))
var err = cmd.Start()
Also it was necessary to make small changes to the original library. Here is the fork.

child_process not returning correct exit code from batch file

I'm trying to get the correct exit code from a batchfile using nodejs on Windows.
When run from the command line I see the expected error code:
ml.bat profile bootstrap
ERROR: [".../deploy/lib/RoxyHttp.rb:362:in block in request'", ".../deploy/lib/RoxyHttp.rb:352:inloop'", ".../deploy/lib/RoxyHttp.rb:352:in request'", ".../deploy/lib/MLClient.rb:110:ingo'", ".../deploy/lib/server_config.rb:1963:in get_sid'", ".../deploy/lib/server_config.rb:2056:inexecute_query_7'",".../deploy/lib/server_config.rb:510:in execute_query'",".../deploy/lib/server_config.rb:709:inbootstrap'", "deploy/lib/ml.rb:168:in `'"
I verify that there exit code was non-zero:
echo %errorlevel%
12
Below is the correspoinding code I use in NodeJS - which always gives an exit code of 0:
var stdout = "";
let proc = child_process.spawn("ml.bat", ["profile", "bootstrap"], {shell:true});
proc.stdout.on('data', d => stdout += d.toString() + '\n';);
proc.on('close', exitcode => {
console.log(stdout);
if (exitcode !== 0) {
console.log('ERROR in command');
process.exit();
}
});
I have tried using several variations (exec, execSync, spawnSync) and options (shell, "cmd.exe /c") but I can never get a non-zero code using node. The program does not print any output on stderr.
Any idea how to get back the right error code in NodeJS?
To solve the problem, I added the following last line to ml.bat:
exit /b %errorlevel%
Now it behaves identically on both windows and linux.
If you want Node to return the child process error code, you need to pass it to the exit function....
process.exit(exitcode);
As Tom mentions in his comment, "code" is undefined, so that needs to be fixed.
See Node Docs: process_process_exitcode
I suggest that you use child_process.spawnSync which is a blocking call and returns ONLY after the process is complete. In addition, the object returned includes an error property that contains the code.
But in all cases - spawn or spawnSync, etc, I believe the result object always includes an error object if the sub-process fails for any reason. So checking for the existence of this object is probably enough.

child_process exec returning a mysterious error

I have the following code (copied from the node docs apart from the command itself) :
var util = require('util'),
exec = require('child_process').exec,
child,
command = 'libreoffice --headless -convert-to pdf mysourcefile.doc -outdir /tmp';
child = exec(command,
function (error, stdout, stderr) {
if (error !== null) {
console.log(error);
return;
}
);
The command appears to be executing fine (output file is there) but error is always "Error: Command failed:" and err is not defined (the docs say err.code will give more information).
What am I doing wrong / overlooking?
It should be error.code.
The docs mix the use of error and err; it refers to the Error object provided to the callback.
like i say . years after. i got the same error. just find what can be the error - checkout (https://stackoverflow.com/a/21137820/1211174). if you are on windows there is a chance that you have someauto run on cmd. and then this autorun failed. and you get both output. sterr and stdout

Where do writes to stdout go when launched from a cygwin shell, no redirection

I have an application, let's call it myapp.exe, which is dual-mode console/GUI, built as /SUBSYSTEM:WINDOWS (There's a tiny 3KB shim myapp.com to cause cmd.exe to wait to display the new prompt.)
If I launch from a command prompt:
myapp -> cmd.exe runs myapp.com which runs myapp.exe. stdout is initially a detached console, by using AttachConsole and freopen("CONOUT$", "w", stdout) my output appears in the command box. OK
myapp.exe -> cmd.exe displays the prompt too early (known problem), otherwise same as previous. Not a normal usage scenario.
myapp > log -> stdout is a file, normal use of std::cout ends up in the file. OK
If I launch from Windows explorer:
myapp.com -> console is created, stdout is console, output goes into console. Same result as using /SUBSYSTEM:CONSOLE for the entire program, except that I've added a pause when myapp.com is the only process in the console. Not a normal usage scenario.
myapp.exe -> stdout is a NULL handle, I detect this and hook std::cout to a GUI. OK
If I launch from Matlab shell:
system('myapp') or system('myapp.com') or system('myapp.exe') -> For all three variations, stdout is piped to MatLab. OK
If I launch from a cygwin bash shell:
./myapp.com -> Just like launch from cmd.exe, the output appears in the command box. OK
./myapp -> (bash finds ./myapp.exe). This is the broken case. stdout is a non-NULL handle but output goes nowhere. This is the normal situation for running the program from bash and needs to be fixed!
./myapp > log -> Just like launch from cmd.exe with file redirection. OK
./myapp | cat -> Similar to file redirection, except output ends up on the console window. OK
Does anybody know what cygwin sets as stdout when launching a /SUBSYSTEM:WINDOWS process and how I can bind std::cout to it? Or at least tell me how to find out what kind of handle I'm getting back from GetStdHandle(STD_OUTPUT_HANDLE)?
My program is written with Visual C++ 2010, without /clr, in case that matters in any way. OS is Windows 7 64-bit.
EDIT: Additional information requested.
CYGWIN environment variable is empty (or non-existent).
GetFileType() returns FILE_TYPE_UNKNOWN. GetLastError() returns 6 (ERROR_INVALID_HANDLE). It doesn't matter whether I check before or after calling AttachConsole().
However, if I simply ignore the invalid handle and freopen("CONOUT$", "w", stdout) then everything works great. I was just missing a way to distinguish between (busted) console output and file redirection, and GetFileType() provided that.
EDIT: Final code:
bool is_console(HANDLE h)
{
if (!h) return false;
::AttachConsole(ATTACH_PARENT_PROCESS);
if (FILE_TYPE_UNKNOWN == ::GetFileType(h) && ERROR_INVALID_HANDLE == GetLastError()) {
/* workaround cygwin brokenness */
h = ::CreateFile(_T("CONOUT$"), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (h) {
::CloseHandle(h);
return true;
}
}
CONSOLE_FONT_INFO cfi;
return ::GetCurrentConsoleFont(h, FALSE, &cfi) != 0;
}
bool init( void )
{
HANDLE out = ::GetStdHandle(STD_OUTPUT_HANDLE);
if (out) {
/* stdout exists, might be console, file, or pipe */
if (is_console(out)) {
#pragma warning(push)
#pragma warning(disable: 4996)
freopen("CONOUT$", "w", stdout);
#pragma warning(pop)
}
//std::stringstream msg;
//DWORD result = ::GetFileType(out);
//DWORD lasterror = ::GetLastError();
//msg << result << std::ends;
//::MessageBoxA(NULL, msg.str().c_str(), "GetFileType", MB_OK);
//if (result == FILE_TYPE_UNKNOWN) {
// msg.str(std::string());
// msg << lasterror << std::ends;
// ::MessageBoxA(NULL, msg.str().c_str(), "GetLastError", MB_OK);
//}
return true;
}
else {
/* no text-mode stdout, launch GUI (actual code removed) */
}
}
The GetFileType() function allows to distinguish between some types of handles, in particular consoles, pipes, files, and broken handles.

Resources