-
Notifications
You must be signed in to change notification settings - Fork 34
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
Redirect and forward to file in a Windows service #99
Comments
Hmm, I'm not aware of any Windows-specific limitations here, and I'd expect that expression to just work on Windows. Could you provide a small program that reproduces the error you're seeing, so that I can try it on my own box? |
I'm having some trouble with the "small" qualifier because services require a fair amount of boiler-plate code, unfortunately. That said, I did what I could: https://github.com/qrnch-jan/svcexample It's a service that you can register, run and unregister. The only thing it does when run is run a powershell script and then self-terminates. The idea is that the output of the script should be logged to a file (using duct). I put this together very quickly, but I did a test run and it did trigger the problem. Also: The resulting binary should not require vcredist when built with the msvc toolchain. |
Here's the sort of thing I was looking for: const PYTHON_PROGRAM: &str = r#"
import sys
print("something for stdout")
print("something for stderr", file=sys.stderr)
"#;
fn main() {
let logf = std::fs::File::create("logfile.txt").unwrap();
duct::cmd!("python", "-c", PYTHON_PROGRAM)
.stderr_to_stdout()
.unchecked()
.stdout_file(logf)
.run()
.unwrap();
} When I run that on my Windows box, I don't get any errors, and I see both "something for stderr" and "something for stdout" in the log file. That makes me guess that whatever error you're running into is something wrong with your command, not with the way duct is running it. |
The code you posted does not appear to be a Windows service. I can run duct with the redirection outside of a service without any issues, but I get the error when run within a service. At this point I'm not sure if you're saying that from duct's perspective there are no differences between a Windows service and a regular program, or if you're lucky enough not to have to had to work with Windows services before. :) I have to look at this through a debugger to make sure I haven't made any mistakes. |
Ah, thank you for repeating that. You're totally right, I'd missed that detail above, because I have no experience with Windows Services :) Duct definitely doesn't have any special logic built-in to deal with them. I wonder if there could be an issue related to the service not having permission to open the log file? If it was a straight up error, presumably that would happen on an earlier line, but maybe the open succeeds but returns a weird handle for some reason? |
I believe I have run into the same issue. I am using Maybe this is easier to reproduce this way: #![windows_subsystem = "windows"]
const PYTHON_PROGRAM: &str = r#"
import sys
print("something for stdout")
print("something for stderr", file=sys.stderr)
"#;
fn main() {
let res = duct::cmd!("python", "-c", PYTHON_PROGRAM)
.stderr_to_stdout()
.run()
.unwrap();
} |
Hitting the same issue without service just by adding |
I found a workaround, need to add |
Well it's sort of both a duct and Windows issue. duct assumes that the process allocates the standard input/output handles, but there's nothing actually requiring a process to do that. In a Windows service, for instance, there's no console and there's no (obvious) way to redirect the input or output, so Microsoft chose to make services not allocate stdin/stdout/stderr by default. It's perfectly valid for duct to document this as a "known issues": It simply will not work without the standard std i/o handles. That said, there are other ways this could be handled, based on setting up its own stdin/stdout/stderr handles. For instance, duct could detect that it's running in a "no std i/o" environment, and then create the stdout/stderr handles itself and redirect output to them to files. Or it could redirect its own stdout/stderr to stdin running in a client process which could be a console running on the desktop. I have some old C code somewhere to do all of this, and I've been meaning to see if there's a neat way to port and integrate these things into duct, but I unfortunately never found the time. With regards to the "windows subsystem": The idea when you run the "window subsystem" is that there's no std CRT, so you have to rely on win32 for everything. When using std i/o redirections, it's fair to assume the Rust code assumes there's an initialized CRT it can rely on. |
I'd like to try to reproduce this myself, to see exactly where in Duct the error is occuring. Could anyone point me to a GitHub repo I could clone and run some command in to repro? I have a Windows box I can use but no experience writing Windows services. However, this turns out, it sounds like a good candidate for an entry in https://github.com/oconnor663/duct.py/blob/master/gotchas.md. |
My project is probably too large to recommend, but essentially I have |
Setting up a Windows service is kind of non-trivial, unfortunately. And once you do, it can be a pain to inspect it (ironically my suggestion to allow duct to redirect to a file in no std i/o environments was partially inspired by this). The repo I posted earlier (see #99 (comment)) solves the "service binary" part, however you still need a way to interact with it. The way I do it is using Visual Studio (Community Edition is free for non-commercial use). This is a rough guide to get to the point where you can run and debug the
IIRC you can compile the
Yeah, it definitely belongs there. I'll try to find some time to write something during the holidays. |
@qrnch-jan that worked! Thank you so much for walking me through it. I think the cause of the error is that duct is unnecessarily dup'ing stdin. That's no big deal most of the time, but with a Windows service I think stdin doesn't exist, and the dup fails. The fix is pretty straightforward, and in fact there was a |
Changes since 0.13.6: - Avoid unnecessarily dup'ing standard file descriptors. This unbreaks some use cases where those descriptors might not exist at all, like Windows services. See #99.
I've gone ahead and shipped this fix as v0.13.7, since it makes the code better anyway. But folks here, please let me know if you get a chance to test it. |
I want to do the shell equivalent of
mycommand 2>&1 > /tmp/output.log
with duct, and I accomplish this with:This works great, except that when I run it in a Windows service, I get an "invalid handle" error. I figure this stems from Windows services not having standard input or outputs.
However, I wonder if it's a fundamental limitation or if it can work? If I run
std::process::Command
and use the.output()
method I can capture all the stdout and stderr and write them to a file, but I really would like to "preserve order" of the output, as is done when redirecting stderr to stdout.The text was updated successfully, but these errors were encountered: