-
Notifications
You must be signed in to change notification settings - Fork 30.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
process: add execve #56496
base: main
Are you sure you want to change the base?
process: add execve #56496
Conversation
Review requested:
|
What does it do on non-Unix systems? |
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #56496 +/- ##
==========================================
+ Coverage 88.53% 89.18% +0.64%
==========================================
Files 657 662 +5
Lines 190761 191868 +1107
Branches 36616 36908 +292
==========================================
+ Hits 168899 171111 +2212
+ Misses 15048 13631 -1417
- Partials 6814 7126 +312
|
On Windows:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/node_process_methods.cc
Outdated
node::Utf8Value pathname_string(env->isolate(), args[0].As<String>()); | ||
|
||
argv = new char*[2]; | ||
argv[0] = strdup(*pathname_string); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we need check not null also in here, maybe(?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot. Added.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should use std::memcpy or a similar modern C++ function other than this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
@ljharb I was totally wrong. Despite of naming and similar signatures, the function I'll disable this function on Windows. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is to be added, I'd absolutely recommend referring to it by a standard name such as execve()
, or otherwise something that makes it clear that it spawns a new process.
This requires integration with the permissions API or is otherwise an immediate security hole.
Overall I'd recommend not adding this though, unless there's a concrete reason to believe that it fills a significant gap that the existing child_process
API doesn't cover.
doc/api/process.md
Outdated
resources from the current process are preserved, except for the standard input, | ||
standard output and standard error file descriptor. | ||
|
||
All other resources are discarded by system when the processes are swapped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if this isn't the a desirable behavior in a given situation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please expand this? What do you mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there's any point in adding execve(), it's the customizability that the method brings with it. Leaving file descriptors open and/or redirecting them intentionally can be part of that -- just like you could in a bash script do exec bash 0<&4 1>&4
to create a shell that reads and writes to e.g. a pre-opened socket or something along those lines.
(With a similar reasoning, you could also allow users to actually specify argv[0]
with a value they control -- it doesn't need to equal the filename that's being executed)
Why would we want to add a function that can't work on all tier 1 platforms? |
Well, we already have a number of such apis... |
Gotcha, i wasn't aware of that. |
I don't consider it to be ideal. I would have preferred a pattern like |
+1 on @addaleax's alternative name suggestion. I'm fine with adding this so long as the cleanup logic/expectations are clearly documented. |
Thanks for involuntary hint. :) |
63d5444
to
037edbc
Compare
As requested, I've renamed it to
I just added integration with the permission API. It will require
In the shell scripting context, there is no way to create a new process which would replace the current one. Think about using Node.js to build a complex command to run. After it, Node.js was not used anymore but since there was no way to swap the process, you would have to spawn a new process, manage it's stdin/stdout/stderr and so forth. That's why I added |
I added two tests which check that:
I've added this to the documentation to reflect that. Does it look good now? |
I mean, to be clear, this is still a one-line operation (managing stdio literally just comes down to setting |
I kinda agree. Is your objection a full block or just an "intent"? |
@ShogunPanda Just fyi, have you seen https://www.npmjs.com/package/foreground-child? That also works on Windows 🙂 My "request changes" marker only refers to the C++ memory management here, i.e. this comment specifically (and the integration with the permissions API, of course). I don't intend to block this feature, I've given my opinion but it's pretty clear overall that the Node.js project has a different stance from my own when it comes to 'what should go into core' 🙂 |
src/node_process_methods.cc
Outdated
execve(*executable, argv.data(), envp.data()); | ||
int error_code = errno; | ||
|
||
THROW_ERR_PROCESS_EXECVE_FAILED(env, error_code); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that you're running the RunAtExit(...)
here, and given that the expectation is for this process to be replaced, I wonder if it would be more appropriate to crash the process here instead of just throwing (a throw can be caught and ignored and I'm not sure we want that here)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
I update that. Can you check if I used the proper methods to print the errors before aborting or was it there a better way?
// so that the new process will inherit it. | ||
if (persist_standard_stream(0) < 0 || persist_standard_stream(1) < 0 || | ||
persist_standard_stream(2) < 0) { | ||
env->ThrowErrnoException(errno, "fcntl"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
env->ThrowErrnoException(errno, "fcntl"); | |
env->ThrowErrnoException(errno, "fcntl"); | |
return; |
std::string info = FormatErrorMessage( | ||
isolate, context, error_message.c_str(), message, true); | ||
FPrintF(stderr, "%s\n", info); | ||
ABORT(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this abort instead of throwing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addaleax ... see #56496 (comment)
I think this is something we can reasonably debate a bit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm yeah ... I'd argue that this further limits the already fairly limited usefulness of this feature, but I understand where it's coming from 🙂 Just seems odd that this hard crashes in this case, you can't always know for sure that execve()
will succeed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this one is rather tricky. I'm not quite sure what this should do really and absolutely open to alternatives. One suggestion is to mark this new API as experimental, stick with the abort for now, and see if a reasonable alternative emerges before we graduate it but I'm not really certain what the ideal approach here is.
This PR adds a new
process.execve
method (absolutely willing to change the name if you want), which is a wrapper for theexecve
UNIX function.The function will never return and will swap the current process with a new one.
All memory and system resources are automatically collected from
execve
, except for std{in,out,err}.The primary use of this function is in shell scripts to allow to setup proper logics and then spawn another command.