Skip to content
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

Track errors that may happen in forked process on Posix #5431

Merged
merged 5 commits into from Jun 13, 2017

Conversation

FreeSlave
Copy link
Contributor

This commit changes behavior of spawnProcess on Posix.
Previously spawnProcess just reported child errors via perror. But the true D way is to throw exception. The problem is that it's not easy to transfer error from forked process to parent. This commit uses pipes to solve this problem.
This commit also adds new overload for ProcessException.newFromErrno that allows to create exception object from arbitrary error number, not just from the current value of errno.

To check that it works:

touch emptyfile
chmod +x emptyfile

Then use spawnProcess on emptyfile. The old phobos will just report error to stderr. The patched one will throw an exception with message saying that exec format is invalid.

@FreeSlave
Copy link
Contributor Author

I'll try to improve coverage, though it's not easy. Code of fork does not get into coverage because it's different process. Anyway, I provided unittest with empty executable file.

@CyberShadow
Copy link
Member

CC @schveiguy @kyllingstad

std/process.d Outdated
string errorMsg;
readExecResult = read(forkPipe[0], &error, error.sizeof);

switch(status)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not final switch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because noerror is not error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then add it with assert(false) as the case body.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, space after switch (CC @wilzbach)

std/process.d Outdated
sigaction_t ignoreAction;
memset(&ignoreAction, 0, sigaction_t.sizeof);
ignoreAction.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &ignoreAction, null);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

man sigaction says:

During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

It looks like this will cause SIGPIPE to be ignored in the child process too, then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right.
Actually I'm not sure if ignoring pipe errors is needed at all. Should we care about the case when parent process dies before forked child proceeds to execve?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we care about the case when parent process dies before forked child proceeds to execve?

I can't think of a reasonable situation where it would make a difference, so I don't think so. If the parent process is gone, the child process will be disowned, in which case its exit code doesn't matter. Furthermore, unless the termination of the parent process is somehow linked with the spawnProcess call, it would be a random event, in which case it wouldn't matter whether the parent process was terminated before the fork call, or after it but before it received the child status. And if we're going to worry about either process being randomly terminated, then that also raises the question of how to handle the child process being killed before it communicates its status... not something we should worry about, I think.

@FreeSlave
Copy link
Contributor Author

FreeSlave commented May 30, 2017

I also added unittest for the case when user does not have search permission on working directory.
The error is ensured by chdir documentation

EACCES
Search permission is denied for one of the components of path

This improves coverage a little by including chdir error case.
The last one is getrlimit error, but I have no idea how to test it.

std/process.d Outdated
@@ -570,7 +559,7 @@ private Pid spawnProcessImpl(in char[][] args,
string errorMsg;
readExecResult = read(forkPipe[0], &error, error.sizeof);

switch(status)
switch (status)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So... final switch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I probably misunderstood you first. Do you want assert(0) on noerror case instead of default case?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. If almost all enum members are present in the switch and it makes sense to account for them explicitly, then use final switch with an assert(false) case body for cases which logically cannot be expected to occur for that switch expression in that point in the program.

@@ -1081,6 +1081,17 @@ version (Posix) @system unittest
std.file.write(directory, "foo");
scope(exit) remove(directory);
assertThrown!ProcessException(spawnProcess([prog.path], null, Config.none, directory));

// can't run in directory if user does not have search permission on this directory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Traversal (+x), not search (+r)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure here. What does "search permission" mean then in chdir documentation?
If it's read permission, then there should be no such test, since it relies on non-standard behavior.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, you're right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From chmod docs:

S_IXUSR (00100)

execute/search by owner ("search" applies for directories, and means that entries within the directory can be accessed)

@FreeSlave
Copy link
Contributor Author

I'm not sure what codecov complains about.
Can we get this merged?

@kyllingstad
Copy link
Member

Auto-merge toggled on

@kyllingstad
Copy link
Member

Thanks for this improvement!

@kyllingstad kyllingstad merged commit 9a91e25 into dlang:master Jun 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants