Command panics with process cannot access the file already being used - rust

I'm trying to spawn a CLI in my Rust code via Comand::new. CLI file is extracting from binary files to exe file and then run with Command::new. But It gives 'ERROR: Os { code: 32, kind: Other, message: "The process cannot access the file because it is being used by another process." }' error.
let taskmgr_pid = get_pid_by_name("Taskmgr.exe");
let process_hide = asset::Asset::get("cli.exe").unwrap();
let file_path = "C:\\filepathhere\\cli.exe";
let mut file = File::create(file_path.to_string()).expect("Couldn't create file");
file.write_all(&process_hide);
let res = Command::new(file_path)
.arg(taskmgr_pid.to_string())
.output()
.expect("ERROR");
println!("PID: {}", taskmgr_pid);
println!("{:?}", res);

It's because you didn't close file prior to executing the command. The easiest way to resolve the issue, is to simply drop(file); prior to Command::new().
let mut file = File::create(file_path).expect("unable to create file");
file.write_all(&process_hide).expect("unable to write");
drop(file);
let res = Command::new(file_path)
.arg(taskmgr_pid.to_string())
.output()
.expect("ERROR");

Related

Edit local yaml file in rust

I'm trying to edit yaml file using serde_yaml but using this i'm only able to edit in stdout but cannot write back to local file(/home/home/.kube/config)
let kubeconfig = "/home/home/.kube/config"
let contents = fs::read_to_string(kubeconfig)
.expect("Something went wrong reading the file");
let mut value: serde_yaml::Value = serde_yaml::from_str(&contents).unwrap();
*value.get_mut("current-context").unwrap() = "new_user".into();
// Below lines shows the edited file in stdout
serde_yaml::to_writer(std::io::stdout(), &value).unwrap();
I did tired as below an other method but had no luck.
let writer = serde_yaml::to_writer(std::io::stdout(), &value).unwrap();
println!("{:?}",writer); // shows ()
serde_yaml::to_writer(writer);
How do i write this edit back to /home/home/.kube/config ?
It seems that you are trying to use to_writer as to_string:
let writer = serde_yaml::to_string(&value).unwrap();
println!("{:?}", writer);
Then you can save the string how you usually would.
Or, alternatively, you could write directly to a file:
let mut file = File::create("/home/home/.kube/config").unwrap();
serde_yaml.to_writer(&mut file, &value);

How to write a String to file? [duplicate]

This question already has answers here:
What's the de-facto way of reading and writing files in Rust 1.x?
(3 answers)
Closed 6 months ago.
I am very new to Rust, and have the question, how to write a string to a file in Rust. There are plenty of tutorials and documentations out there, how to write an &str type to file, but no tutorial how to write a String type to files. I wrote this code by myself and it compiles, but I always get an "Bad file descriptor (os error 9)". I'm on Linux(Manjaro).
I'm very thankfully for every help I will get.
//Import
use std::fs::File;
use std::io::*;
//Mainfunction
fn main() {
//Programm startet hier | program starts here
println!("Program started...");
// Lesen des Files, createn des Buffers | reading file createing buffer
let mut testfile = File::open("/home/julian/.rust_test/test_0.txt").unwrap();
let mut teststring = String::from("lol");
//Vom File auf den Buffer schreiben | writing from file to buffer
testfile.read_to_string(&mut teststring).unwrap();
//Buffer ausgeben | print to buffer
println!("teststring: {}", teststring);
// Neue Variable deklarieren | declare new variable
let msg = String::from("Writetest tralalalal.");
// msg an ursprünglichen String anhängen | append msg to string
teststring.push_str(&msg);
println!("teststring: {}", teststring);
// Neuen String nach File schreiben | write new sting to file
let eg = testfile.write_all(&teststring.as_bytes());
match eg {
Ok(()) => println!("OK"),
Err(e) => println!("{}",e)
}
println!("Fertig")
}
Your issue is that the file you opened is opened in read-only mode.
As #Herohtar correctly pointed out, from the documentation of File::open():
Attempts to open a file in read-only mode.
What you are trying to do requires read & write mode. There is no pre-made function for that, so you need to build your own using OpenOptions:
//Import
use std::fs::OpenOptions;
use std::io::*;
//Mainfunction
fn main() {
//Programm startet hier | program starts here
println!("Program started...");
// Lesen des Files, createn des Buffers | reading file createing buffer
let mut testfile = OpenOptions::new()
.read(true)
.write(true)
.open("test_0.txt")
.unwrap();
let mut teststring = String::from("lol");
//Vom File auf den Buffer schreiben | writing from file to buffer
testfile.read_to_string(&mut teststring).unwrap();
//Buffer ausgeben | print to buffer
println!("teststring: {}", teststring);
// Neue Variable deklarieren | declare new variable
let msg = String::from("Writetest tralalalal.");
// msg an ursprünglichen String anhängen | append msg to string
teststring.push_str(&msg);
println!("teststring: {}", teststring);
// Neuen String nach File schreiben | write new sting to file
let eg = testfile.write_all(teststring.as_bytes());
match eg {
Ok(()) => println!("OK"),
Err(e) => println!("{:?}", e),
}
println!("Fertig")
}
The rest of your code is pretty much fine.
The only nitpick I have is that testfile.write_all(&teststring.as_bytes()) doesn't make much sense, because as_bytes() already returns a reference, so I removed the & from it.

Is it possible to access current file name?

Is it possible to access current file name in Rust by
// main.rs
fn main() {
println!("filename: {}", FILE_NAME);
}
?
(This program should print filename: main.rs)
You can use the std::file macro to get the current source filename at the compile time.
let this_file = file!();
If you want to remove the path from the returned filename, you can construct a Path with it and call the file_name method.
let filename_only = Path::new(this_file).file_name().and_then(|s| s.to_str()).unwrap();
Playground

How can I embed the Aeron driver JAR in a Rust program and start it later?

I have a Rust program that uses Aeron through the aeron-rs crate.
Every time I want to run my program, I need to manually start the driver first. In fact, the crate explicitly states that it works simply as a wrapper around the running driver.
I would like that the driver launches upon starting my program.
I know that Aeron provides an embedded driver, but I'm clueless about how to possibly integrate it.
So far, I've put the embedded driver JAR in my src folder
my-project/
src/
aeron-all-1.32.0-SNAPSHOT.jar
I used the include_bytes! macro to try to include the JAR in the build
fn main() {
include_bytes!("aeron-all-1.32.0-SNAPSHOT.jar");
}
I don't see the jar in the build folder.
Then, the following code should run the driver upon startup:
if cfg!(target_os = "windows") {
Command::new("cmd")
.args(&[
"/C",
"%JAVA_HOME%\\bin\\java \
-cp aeron-all-1.32.0-SNAPSHOT.jar \
%JVM_OPTS% io.aeron.driver.MediaDriver %*",
])
.output()
.expect("Failed to execute external process.")
} else {
Command::new("sh")
.arg("-c")
.arg(
"${JAVA_HOME}/bin/java \
-cp aeron-all-1.32.0-SNAPSHOT.jar \
${JVM_OPTS} io.aeron.driver.MediaDriver \"$#\"",
)
.output()
.expect("Failed to execute external process.")
};
Is this the right way to run the jar file?
I had to extract the bytes into a temp JAR file and I am able to run such JAR
fn main() -> std::io::Result<()> {
let driver_path = extract_driver();
if cfg!(target_os = "windows") {
let mut command = String::from("%JAVA_HOME%\\bin\\java -cp ");
command.push_str(driver_path.as_str());
command.push_str("%JVM_OPTS% io.aeron.driver.MediaDriver %*");
Command::new("cmd")
.args(&["/C", command.as_str()])
.spawn()
.expect("Error spawning Aeron driver process")
} else {
let mut command = String::from("${JAVA_HOME}/bin/java -cp ");
command.push_str(driver_path.as_str());
command.push_str("${JVM_OPTS} io.aeron.driver.MediaDriver \"$#\"");
Command::new("sh")
.arg("-c")
.arg(command.as_str())
.spawn()
.expect("Error spawning Aeron driver process")
};
}
fn extract_driver() -> String {
let bytes = include_bytes!("aeron-all-1.32.0-SNAPSHOT.jar");
let mut driver_path = temp_dir();
driver_path.push("aeron-driver.jar");
let mut file = File::create(driver_path.to_owned()).expect("Error extracting Aeron driver jar");
file.write_all(bytes).unwrap();
String::from(driver_path.to_str().unwrap())
}

How do I stop the program and restart with sudo without the user typing the word sudo?

use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut source_list = OpenOptions::new()
.write(true)
.append(true)
.open("/usr/local/etc/apt/sources.list")
.unwrap();
if let Err(e) = writeln!(source_list, "{}", "deb ".to_owned() + "https://www.google.com/" + " ./") {
eprintln!("Couldn't write to file: {}", e);
}
}
When running this code without sudo it outputs:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', src/libcore/result.rs:999:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
and when running this code with sudo it asks for my password and then writes to the file successfully so my question is how do I get it to ask for me my password then write to the file successfully without the need of sudo (Basically how do I make it run sudo for the user so they don't have to)?
Basically how do I make it run sudo for the user so they don't have to
You can't.
The normal way of dealing with this problem is to try, and, when it fails, analyze the error and politely ask the user to launch the program with the relevant rights:
let source_list = openoptions::new()
.write(true)
.append(true)
.open("/usr/local/etc/apt/sources.list");
match source_list {
Err(ioerr) => {
match ioerr.kind() {
ErrorKind::PermissionDenied => {
e.println("permission denied. Maybe sudo?");
}
_ => {
e.println("I failed :(");
}
}
return;
}
Ok(source_list) => {
// do things
}
}
Yes you could, but if you should is another question...
When detecting the lack of privileges you could restart your program with sudo with std::process::Command and unix is extensions exec by executing a command with the same cmdline arguments. env::args is an iterator to all command line arguments where the first one is the program name
use std::process::Command;
use std::os::unix::process::CommandExt;
[...]
// when restart as root is needed
let cmdlineargs:Vec<String>=env::args().collect();
let _output = Command::new("sudo")
.args(&cmdlineargs)
.exec(); // Bye bye never returns
There are a ton of problems with the code above, for instance you should avoid having the program to restart itself in an indefinite loop and hiding privilege escalations from users is often considered bad design.
Edit: Updated after suggestions in comments replaced spawn with exec

Resources