How to pipe to a process using vala/glib - glib

I'm trying to pipe output from echo into a command using GLib's spawn_command_line_sync method. The problem I've run into is echo is interpreting the entire command as the argument.
To better explain, I run this in my code:
string command = "echo \"" + some_var + "\" | command";
Process.spawn_command_line_sync (command.escape (),
out r, out e, out s);
I would expect the variable to be echoed to the pipe and the command run with the data piped, however when I check on the result it's just echoing everything after echo like this:
"some_var's value" | command
I think I could just use the Posix class to run the command but I like having the result, error and status values to listen to that the spawn_command_line_sync method provides.

The problem is that you are providing shell syntax to what is essentially the kernel’s exec() syscall. The shell pipe operator redirects the stdout of one process to the stdin of the next. To implement that using Vala, you need to get the file descriptor for the stdin of the command process which you’re running, and write some_var to it manually.

You are combining two subprocesses into one. Instead echo and command should be treated separately and have a pipe set up between them. For some reason many examples on Stack Overflow and other sites use the Process.spawn_* functions, but using GSubprocess is an easier syntax.
This example pipes the output of find . to sort and then prints the output to the console. The example is a bit longer because it is a fully working example and makes use of a GMainContext for asynchronous calls. GMainContext is used by GMainLoop, GApplication and GtkApplication:
void main () {
var mainloop = new MainLoop ();
SourceFunc quit = ()=> {
mainloop.quit ();
return Source.REMOVE;
};
read_piped_commands.begin ("find .", "sort", quit);
mainloop.run ();
}
async void read_piped_commands (string first_command, string second_command, SourceFunc quit) {
var output = splice_subprocesses (first_command, second_command);
try {
string? line = null;
do {
line = yield output.read_line_async ();
print (#"$(line ?? "")\n");
}
while (line != null);
} catch (Error error) {
print (#"Error: $(error.message)\n");
}
quit ();
}
DataInputStream splice_subprocesses (string first_command, string second_command) {
InputStream end_pipe = null;
try {
var first = new Subprocess.newv (first_command.split (" "), STDOUT_PIPE);
var second = new Subprocess.newv (second_command.split (" "), STDIN_PIPE | STDOUT_PIPE);
second.get_stdin_pipe ().splice (first.get_stdout_pipe (), CLOSE_TARGET);
end_pipe = second.get_stdout_pipe ();
} catch (Error error) {
print (#"Error: $(error.message)\n");
}
return new DataInputStream (end_pipe);
}
It is the splice_subprocesses function that answers your question. It takes the STDOUT from the first command as an InputStream and splices it with the OutputStream (STDIN) for the second command.
The read_piped_commands function takes the output from the end of the pipe. This is an InputStream that has been wrapped in a DataInputStream to give access to the read_line_async convenience method.

Here's the full, working implementation:
try {
string[] command = {"command", "-options", "-etc"};
string[] env = Environ.get ();
Pid child_pid;
string some_string = "This is what gets piped to stdin"
int stdin;
int stdout;
int stderr;
Process.spawn_async_with_pipes ("/",
command,
env,
SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
null,
out child_pid,
out stdin,
out stdout,
out stderr);
FileStream input = FileStream.fdopen (stdin, "w");
input.write (some_string.data);
/* Make sure we close the process using it's pid */
ChildWatch.add (child_pid, (pid, status) => {
Process.close_pid (pid);
});
} catch (SpawnError e) {
/* Do something w the Error */
}
I guess playing with the FileStream is what really made it hard to figure this out. Turned out to be pretty straightforward.

Based on previous answers probably an interesting case is to use program arguments to have a general app to pipe any input on it:
pipe.vala:
void main (string[] args) {
try {
string command = args[1];
var subproc = new Subprocess(STDIN_PIPE | STDOUT_PIPE, command);
var data = args[2].data;
var input = new MemoryInputStream.from_data(data, GLib.free);
subproc.get_stdin_pipe ().splice (input, CLOSE_TARGET);
var end_pipe = subproc.get_stdout_pipe ();
var output = new DataInputStream (end_pipe);
string? line = null;
do {
line = output.read_line();
print (#"$(line ?? "")\n");
} while (line != null);
} catch (Error error) {
print (#"Error: $(error.message)\n");
}
}
build:
$ valac --pkg gio-2.0 pipe.vala
and run:
$ ./pipe sort "cc
ab
aa
b
"
Output:
aa
ab
b
cc

Related

.NET Core execute OS Command in Container problem

i want to execute OS Command both windows and docker container (linux). When i control on windows every command which i entered work well because i can see output. When i build Dockerfile and run my app with container, send a few command i didn't take output what i expected.
My function is bellow.
public string RunCommand(string arguments)
{
var argsPrepend = "-c ";
var shellName = "/bin/sh";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
shellName = #"C:\Windows\System32\cmd.exe";
argsPrepend = "/c ";
}
try
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = shellName,
Arguments = argsPrepend + arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return output;
}
catch (Exception ex)
{
return ex.ToString();
}
}
When i send pwd and ls command it works well.
swagger-pwd
swagger-ls
When i want to execute "ls -la /" or "cat /etc/passwd" i didn't get right output.
swagger-ls-la
swagger-cat-etc-passwd
How can i execute OS Command over Docker Container ?
What's the problem which i make or i face ?
1 - If you can access the directories you are running your file in and not other root directories, then there is the problem of not being able to read the file. You may need to check if the user you are running the file from has access.
2 - You may need to take a look at the "SecurityContext" configurations. I don't know about container structures. Maybe it's hardening in itself as a precaution. You may need to give permission yourself.
Link : https://techcommunity.microsoft.com/t5/azure-developer-community-blog/hardening-an-asp-net-container-running-on-kubernetes/ba-p/2542224
Best Regards.
Thanks for your helping. After gave my all 2 days's time :) i solved at the end. After i changed function likes bellow, everything works well.
public string RunCommand(string arguments)
{
var shellName = "/bin/bash";
var argsPrepend = "-c ";
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
shellName = #"C:\Windows\System32\cmd.exe";
argsPrepend = "/c ";
}
try
{
Process process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = shellName,
Arguments = argsPrepend + "\"" + arguments + "\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
return output;
}
catch (Exception ex)
{
return ex.ToString();
}
}

Capture output of fexecve on memfd_create fd

This code is supposed to create a memfd (anonymous file), copy shellcode as a Vec<u8>, then
finally execute using fexecve().
// A method that takes a u8 vector and copies it to a memfd_create file, then executes using fexecve()
use std::ffi::{CStr, CString};
use nix::sys::memfd::{memfd_create, MemFdCreateFlag};
use nix::unistd::fexecve;
use nix::unistd::write;
fn fileless_exec(code: Vec<u8>) {
// Name using CStr
let name = CStr::from_bytes_with_nul(b"memfd\0").unwrap();
// Create a new memfd file.
let fd = memfd_create(&name, MemFdCreateFlag::MFD_CLOEXEC).unwrap();
// Write to the file
let _nbytes = write(fd, &code);
// args for fexecve
let arg1 = CStr::from_bytes_with_nul(b"memfd\0").unwrap();
// enviroment variables
let env = CString::new("").unwrap();
// fexecve
let _ = match fexecve(fd, &[&arg1], &[&env]) {
Ok(_) => {
println!("Success!");
},
Err(e) => {
println!("Error: {}", e);
}
};
}
fn main() {
// Read the file `hello_world` into a vector of bytes.
let code = std::fs::read("/tmp/hello_world").unwrap();
fileless_exec(code);
}
(hello_world is just a simple C hello world example).
The binary executes and writes to stdout normally. How would I capture the output as, say, a String in Rust? I've seen this example do it in C which is ultimately what I'm trying to achieve here.
The whole point here is to execute a file using its fd and capture its output. The input could be coming from anywhere (not always from disk as with the hello_world executable): from a web endpoint, other processes, etc.
I'm aware this code isn't that "Rust"-y.
So following some very bad practices I was able to make this:
// A method that takes a u8 vector and copies it to a memfd_create file.
use std::ffi::{CStr, CString};
use nix::sys::memfd::{memfd_create, MemFdCreateFlag};
use nix::unistd::{read, write, fexecve, dup2, close, fork};
fn fileless_exec(code: Vec<u8>, fd_name: &[u8], stdout: &mut String) {
// Name using CStr
let name = CStr::from_bytes_with_nul(fd_name).unwrap();
// Create a new memfd file.
let fd = memfd_create(&name, MemFdCreateFlag::MFD_CLOEXEC).unwrap();
// Write to the file
let _nbytes = write(fd, &code);
// args for fexecve
let arg1 = CStr::from_bytes_with_nul(fd_name).unwrap();
// enviroment variables
let env = CString::new("").unwrap();
// to capture the output we need to use a pipe
let pipe = nix::unistd::pipe().unwrap();
unsafe {
let mut output = [0u8; 1024];
// fork and exec
let pid = fork().unwrap();
if pid.is_child() {
// dup the read end of the pipe to stdout
dup2(pipe.1, nix::libc::STDOUT_FILENO).unwrap();
// close the write end of the pipe
close(pipe.0).unwrap();
// close the read end of the pipe
close(pipe.1).unwrap();
// fexecve
fexecve(fd, &[&arg1], &[&env]).unwrap();
} else {
// close the read end of the pipe
close(pipe.1).unwrap();
// write to the pipe
let _nbytes = read(pipe.0, &mut output);
// close the write end of the pipe
close(pipe.0).unwrap();
// convert output to a string
*stdout = String::from_utf8(output.to_vec()).unwrap();
}
}
}
fn main() {
// Read the file `/bin/ls` into a vector of bytes.
let code = std::fs::read("/bin/ls").unwrap();
let mut output = String::new();
fileless_exec(code, b"anonymous\0", &mut output);
print!("File output: {}", output);
}
This works for now... thanks for answers
Somewhat old question, but I couldn't find a better answer anywhere. If you want to do this, there is now a crate memfd-exec to do exactly this!
For example (from the docs) we can download an execute a program without ever writing it to disk:
use memfd_exec::{MemFdExecutable, Stdio};
use reqwest::blocking::get;
const URL: &str = "https://novafacing.github.io/assets/qemu-x86_64";
let resp = get(URL).unwrap();
// The `MemFdExecutable` struct is at near feature-parity with `std::process::Command`,
// so you can use it in the same way. The only difference is that you must provide the
// executable contents as a `Vec<u8>` as well as telling it the argv[0] to use.
let qemu = MemFdExecutable::new("qemu-x86_64", resp.bytes().unwrap().to_vec())
// We'll just get the version here, but you can do anything you want with the
// args.
.arg("-version")
// We'll capture the stdout of the process, so we need to set up a pipe.
.stdout(Stdio::piped())
// Spawn the process as a forked child
.spawn()
.unwrap();
// Get the output and status code of the process (this will block until the process
// exits)
let output = qemu.wait_with_output().unwrap();
assert!(output.status.into_raw() == 0);
// Print out the version we got!
println!("{}", String::from_utf8_lossy(&output.stdout));
I am the author of memfd-exec.

Reading a file in dart and split the string has different results in console that in vscode

I'm new in dart, I'm trying to read information from a txt file and use the data to create objects from a class (in this case about pokemon), but when I run my program in the terminal it doesn't prints the correct information, and when I run the program in vscode (whit the dart extension, the "run" button) it prints in the debug console the correct information. What is the problem?
When I run the program in vscode I get in my print method (printP) this (which is what I want)
vscode:
Print method:
1+: Bulbasaur GRASS | POISON
but when I run the program in the terminal I get this.
Terminal:
Print method:
| POISONsaur
Here is the dart code.
main.dart
import 'dart:io';
import 'pokemon.dart';
void main() {
var file = new File("/home/ariel/Documents/script/pokemon.txt");
String str = file.readAsStringSync();
var pokes = str.split("[");
pokes = pokes.sublist(1, pokes.length);
getPokemon(pokes[0]).printP();
}
Pokemon getPokemon(String str) {
Pokemon p = new Pokemon();
print("string: " + str);
var aux = str.split("\n");
print(aux.length);
for (var i in aux) {
print("line: " + i);
}
p.number = int.parse(aux[0].split("]")[0]);
p.name = aux[1].split("=")[1];
p.type1 = aux[3].split("=")[1];
p.type2 = aux[4].split("=")[1];
return p;
}
pokemon.dart
class Pokemon {
String _name, _type1, _type2;
int _number;
Pokemon() {
this._name = "";
this._number = 0;
this._type1 = "";
this._type2 = "";
}
void printP() {
print("Print method:");
print("${this._number}+: ${this._name} ${this._type1} | ${this._type2}");
}
void set number(int n) {
this._number = n;
}
void set name(String nm) {
this._name = nm;
}
void set type1(String t) {
this._type1 = t;
}
void set type2(String t) {
this._type2 = t;
}
}
And here is the txt file
pokemon.txt
[1]
Name=Bulbasaur
InternalName=BULBASAUR
Type1=GRASS
Type2=POISON
BaseStats=45,49,49,45,65,65
GenderRate=FemaleOneEighth
GrowthRate=Parabolic
BaseEXP=64
EffortPoints=0,0,0,0,1,0
Rareness=45
Happiness=70
Abilities=OVERGROW
HiddenAbility=CHLOROPHYLL
Moves=1,TACKLE,3,GROWL,7,LEECHSEED,9,VINEWHIP,13,POISONPOWDER,13,SLEEPPOWDER,15,TAKEDOWN,19,RAZORLEAF,21,SWEETSCENT,25,GROWTH,27,DOUBLEEDGE,31,WORRYSEED,33,SYNTHESIS,37,SEEDBOMB
EggMoves=AMNESIA,CHARM,CURSE,ENDURE,GIGADRAIN,GRASSWHISTLE,INGRAIN,LEAFSTORM,MAGICALLEAF,NATUREPOWER,PETALDANCE,POWERWHIP,SKULLBASH,SLUDGE
Compatibility=Monster,Grass
StepsToHatch=5355
Height=0.7
Weight=6.9
Color=Green
Habitat=Grassland
Kind=Seed
Pokedex=Almacena energía en el bulbo de su espalda para alimentarse durante épocas de escasez de recursos o para atacar liberándola de golpe.
BattlerPlayerY=0
BattlerEnemyY=25
BattlerAltitude=0
Evolutions=IVYSAUR,Level,16
Your code are dependent on the newline format of your txt file. I will recommend you are using the LineSplitter class from dart:convert to split your lines.
The problem is that Windows newlines contains both '\n' and '\r' but you are only removing the '\n' part. '\r' are essential meaning the terminal should set the cursor back to the beginning of the line.
You can read this like a typewriter where you first move the head back and set move the paper to the next line. And can read a lot more about is topic here: https://en.wikipedia.org/wiki/Newline
The purpose of the LineSplitter class is to abstract all of this logic and get some behavior which will work on all platforms.
So import dart:convert and change this line:
var aux = str.split("\n");
Into:
var aux = LineSplitter.split(str).toList();

Apache Beam TextIO.Read with line number

Is it possible to get access to line numbers with the lines read into the PCollection from TextIO.Read? For context here, I'm processing a CSV file and need access to the line number for a given line.
If not possible through TextIO.Read it seems like it should be possible using some kind of custom Read or transform, but I'm having trouble figuring out where to begin.
You can use FileIO to read the file manually, where you can determine the line number when you read from the ReadableFile.
A simple solution can look as follows:
p
.apply(FileIO.match().filepattern("/file.csv"))
.apply(FileIO.readMatches())
.apply(FlatMapElements
.into(strings())
.via((FileIO.ReadableFile f) -> {
List<String> result = new ArrayList<>();
try (BufferedReader br = new BufferedReader(Channels.newReader(f.open(), "UTF-8"))) {
int lineNr = 1;
String line = br.readLine();
while (line != null) {
result.add(lineNr + "," + line);
line = br.readLine();
lineNr++;
}
} catch (IOException e) {
throw new RuntimeException("Error while reading", e);
}
return result;
}));
The solution above just prepends the line number to each input line.

Printing multiple poscripts in one job

I need to write a Java method in order to send postscript files to a printer in one job. In other words, I need to reproduce the effect of the following Unix command:
lp -d printer file1.ps file2.ps file3.ps
First I thought I could just concatenate the PS files (using classes like ConcatInputStream and PrintJobWatcher). But the resulting merged PS file is not always valid.
If it helps, here is my current code (I have been asked to do it in Groovy):
/**
* Prints the {#code files} {#code copyCount} times using
* {#code printService}.
* <p>
* Exceptions may be thrown.
* #param printService Print service
* #param files Groovy array of {#code File} objects
* #param copyCount Number of copies to print
*/
private static void printJob(
PrintService printService,
def files,
int copyCount) {
// No multiple copy support for PS file, must do it manually
copyCount.times { i ->
InputStream inputStream = null
try {
log.debug("Create stream for copy #${i}")
inputStream = new ConcatInputStream()
for (def file in files) {
if (file != null) {
log.debug("Add '${file.absolutePath}' to the stream")
((ConcatInputStream)inputStream).addInputStream(
new FileInputStream(file))
}
}
log.debug("Create document")
Doc doc = new SimpleDoc(
inputStream, DocFlavor.INPUT_STREAM.AUTOSENSE, null)
log.debug("Create print job")
DocPrintJob docPrintJob = printService.createPrintJob()
log.debug("Create watcher")
PrintJobWatcher watcher = new PrintJobWatcher(docPrintJob)
log.debug("Print copy #${i}")
docPrintJob.print(doc, null)
log.debug("Wait for completion")
watcher.waitForDone()
} finally {
if (inputStream) log.debug("Close the stream")
inputStream?.close()
}
}
}
I’m not allowed to convert the PS into PDF.
I read here that I could insert false 0 startjob pop between the PS files. But then would there be only one job?
I may be confusing the concept of "jobs"...
I didn’t find a post on the topic (sending multiple PS files to the printer in one job). The solution may be so obvious that it blinded me, that why I posted this question.
My next attempt will be to execute lp from the class, even if it looks dirty I know I can make it work that way... If you know a simpler way, please tell me.
Edit:
Executing lp (as below) works well:
/**
* Prints the {#code files} {#code copyCount} times using an executable.
* <p>
* Exceptions may be thrown.
* #param config ConfigObject containing closures for building the
* command line to the printing executable, and to analyze the
* return code. Example of config file:
*
* print {
* commandClosure = { printerName, files ->
* [
* 'lp',
* '-d', printerName,
* files.collect{ it.absolutePath }
* ].flatten()
* }
* errorClosure = { returnCode, stdout, stderr -> returnCode != 0 }
* warnClosure = { returnCode, stdout, stderr ->
* !stderr?.isAllWhitespace() }
* }
*
* #param printerName Printer name
* #param files Groovy array of {#code File} objects
* #param copyCount Number of copies to print
*/
private static void printJob(
ConfigObject config,
String printerName,
def files,
int copyCount) {
files.removeAll([null])
Integer copyCount = job.copyCountString.toInteger()
copyCount.times { i ->
def command = config.print.commandClosure(printerName, files)
log.debug("Command: `" + command.join(' ') + "`")
def proc = command.execute()
proc.waitFor()
def returnCode = proc.exitValue()
def stdout = proc.in.text
def stderr = proc.err.text
def debugString = "`" + command.join(' ') +
"`\nReturn code: " + returnCode +
"\nSTDOUT:\n" + stdout + "\nSTDERR:\n" + stderr
if (config.print.errorClosure(returnCode, stdout, stderr)) {
log.error("Error while calling ${debugString}")
throw new PrintException("Error while calling ${debugString}")
} else if (config.print.warnClosure(returnCode, stdout, stderr)) {
log.warn("Warnings while calling ${debugString}")
} else {
log.debug("Command successful ${debugString}")
}
}
}
Even if I would prefer not to use an external executable... This issue is not anymore critical for me. I will accept an answer if it does not require the call to an external executable.
Actually, can't you just loop through the files inside your loop for the number of copies?
ie:
private static void printJob( PrintService printService, def files, int copyCount) {
// No multiple copy support for PS file, must do it manually
copyCount.times { i ->
log.debug( "Printing Copy $i" )
files.each { file ->
log.debug( "Printing $file" )
file.withInputStream { fis ->
Doc doc = new SimpleDoc( fis, DocFlavor.INPUT_STREAM.AUTOSENSE, null )
DocPrintJob docPrintJob = printService.createPrintJob()
PrintJobWatcher watcher = new PrintJobWatcher( docPrintJob )
docPrintJob.print( doc, null )
watcher.waitForDone()
}
}
}
}
(untested)
edit
As an update to your workaround above, rather than:
def proc = command.execute()
proc.waitFor()
def returnCode = proc.exitValue()
def stdout = proc.in.text
def stderr = proc.err.text
You're probably better with:
def proc = command.execute()
def out = new StringWriter()
def err = new StringWriter()
ps.consumeProcessOutput( out, err )
ps.waitFor()
def returnCode = proc.exitValue()
def stdout = out.toString()
def stderr = err.toString()
As this won't block if the process writes a lot of information :-)
One of the issues could be related to the Document Structure Convention (DSC) comments. These comments provide metadata about the document contained in the file. A tool like ghostscript should be able to process the resulting concatenated file because it ignores DSC comments entirely and just processes the postscript. But tools that expect to work on DSC-conforming files will be confused when the first file ends (it's marked by an End comment) and there's more data in the file.
One thing that might work is to strip all comments from the files, so there's no misleading DSC information. (DSC comments will always be a full line starting with %%, so an RE substitution should do it. s/^%[^$]*$//g)

Resources