Exec() read from file - c++

I am working on creating a basic shell. I'm stuck on trying to get exec() to read in from an input file. Here's what I have. I'm unsure what arguments I should be feeding execvp(). Here, stringList[0] will be something along the lines of "ls" or "cat". If stringList[0] is ls the file would contain something along the lines of ls -a -l
int fd = open(iFile, O_RDONLY);
dup2(fd, 0);
close(fd);
execvp(stringList[0], ...);
cout << "Exec error!\n";
exit(1);

It sounds like you want to read a command from a file and then execute that command. If that's your objective, you should actually be executing the shell.
Your current approach of open then dup2 doesn't cause exec to read from a file, because exec never reads from standard input. It only reads from the executable (to load the program image). What your current approach does is redirect input, so that if exec is successful, the new program will have iFile as its standard input file.
You can just do this:
execl(shell, basename(shell), iFile, (char*)0);
Example: if iFile is the string "myCommand.sh", and shell is /bin/bash, then basename(shell) gives bash, and this is similar to running this on the command line:
$ bash myCommand.sh
For shell you probably want to use the current user's default shell. You can obtain this information portably using getpwuid or getpwuid_r.

Related

Check/list all bash commands in C++?

Basically, is there a simple way to get a list of all bash commands in the PATH environment variable in C++? My current solution is to run a command beforehand that lists all the commands into a .txt, which is then read into the C++ program. I want to be able to cut out this step, if possible.
ls ${PATH//:/ } > commands.txt
If you do NOT need to use stdin in your C++ program
This is the easy solution. Just pipe the output of the ls command to your C++ program. Then, in your C++ program, read the contents of the file from stdin like you would read from a normal file. Literally use stdin wherever you need to provide a file descriptor. So, your command would look something like
ls ${PATH//:/ } | ./a.out
The | denotes a pipe in bash. It takes stdout from the first program (here ls) and redirects it to stdin of the second program (here your C++ program).
If you do need to use stdin in your C++ program
This is going to be tricky. You essentially need to make your C++ program do everything itself. The first way to this that comes to mind is
Read $PATH using getenv().
Parse $PATH by replacing all occurrences of : with (a blank space). This is easy enough to do in a loop, but you could also use std::replace.
Now that you have the directory paths from $PATH, you simply need the contents of each directory. This post will help you get the contents of a directory.
UPDATE: Another Approach
I've thought of another way to approach your problem that allows you to use IO redirection (ie. use the pipe), and also use stdin at the same time. The problem is that it is probably not portable.
The basic idea is that you read the output of ls from stdin (using the pipe operator in bash). Next, you essentially reset stdin using freopen. Something along the lines of
#include <stdio.h>
int main(void)
{
char buf[BUFSIZ];
puts("Reading from stdin...");
while(fgets(buf, sizeof(buf), stdin))
fputs(buf, stdout);
freopen("/dev/tty", "rw", stdin);
puts("Reading from stdin again...");
while(fgets(buf, sizeof(buf), stdin))
fputs(buf, stdout);
return 0;
}
The above code is from here. It reads stdin, resets stdin, and reads from stdin again. I would suggest not using this approach for anything important, or for something that needs to work on several platforms. While it is more convenient since it allows you to use IO redirection while retaining the ability to use stdin, it is not portable.

Cannot read output of processes launched under cmd.exe pipe

I hope your programming is going well.
I have a question that I hope asserts an easy answer due to my lack of knowledge.
I've used this code from this question - CreateProcess cmd.exe read/write pipes deadlock
And everything works well.
The problem is when I run other commands from the cmd.exe shell that require interactivity, for example, python or powershell, I get the initial output then nothing gets written to the pipe.
So this is what my input/output looks like:
static PCSTR commands[] = { "powershell\r\n", "dir\r\n", "help\r\n"};
ULONG n = RTL_NUMBER_OF(commands);
PCSTR* psz = commands;
do
{
if (MessageBoxW(0,0, L"force close ?", MB_YESNO) == IDYES)
{
DisconnectNamedPipe(hFile);
break;
}
if (p = new U_IRP(&obj))
{
PCSTR command = *psz++;
p->Write(command, (ULONG)strlen(command) * sizeof(CHAR));
p->Release();
}
} while (--n)
When the code runs, I get the initial powershell.exe prompt as so
PS C:\Users>
But after that nothing gets written to the pipe.
The code is using CreateProcess(... "cmd.exe" ...) and I have tried changing it from "cmd.exe" to "cmd.exe /c" and "cmd.exe /k", neither of which work.
Perhaps you would know what I need to do read/write output to interpreted such as python or powershell from a CreateProcess() induced pipe? Thanks for your help!
you exec cmd.exe and send command to it via pipe to exec powershell. then all depended from powershell implementation
on window7:
powershell use ReadConsoleW for got input. so it not use you named pipe - not read from it. and you can note that console window become interactive after you exec powershell. so powershell not accept what you write to pipe (it simply not read from it at all) but read user input from screen. however after you manually input some command to console and press enter - you can got pipe output - powershell use (mix) both - WriteFile and WriteConsoleW for output. some information output via WriteFile and some via WriteConsoleW
on windows10:
powershell use ReadFile for got input. and WriteFile for output. so it read you commands from pipe and write results to it. and all perfect worked. also you can note that console window is inactive in this case - you can not enter any text to it (unlike win7)
so with code all absolute ok. problem only in how 3-rd program read and write data. if it not read from your pipe - you nothing can do here

Will File I/O In Current Working Directory Ever Fail?

On my home Linux laptop, I like to write wrapper programs and GUI helpers for things I use frequently. However, I don't like Bash scripting very much, so I do a lot of stuff in C++. However, a lot of times, this requires me to use the system() function from the cstdlib.
This system() command is awesome, but I wanted a way to call system() and receive the stdout/stderror. The system() command only returns the return code from the command. So, in a Bash script, one can do:
myVar=$(ls -a | grep 'search string')
echo $myVar
and myVar will output whatever the stdout was for the command. So I began writing a wrapper class that will add a pipe-to-file to the end of the command, open the file, read all of the piped stdout, and return it as either one long string or as a vector of strings. The intricacies of the class are not really relevant here (I don't think anyway), but the above example would be done like this:
SystemCommand systemCommand;
systemCommand.setCommand("ls -a | grep \'search string\' ");
systemCommand.execute();
std::cout << systemCommand.outputAsString() << std::endl;
Behind the scenes, when systemCommand.execute() is called, the class ensures that the command will properly pipe all stdout/stderr to a randomly generated filename, in the current working directory. So for example, the above command would end up being
"ls -a | grep 'search string' >> 1452-24566.txt 2>&1".
The class then goes attempts to open and read from that file, using ifstream:
std::ifstream readFromFile;
readFromFile.open(_outputFilename);
if (readFromFile.is_open()) {
//Read all contents of file into class member vector
...
readFromFile.close();
//Remove temporary file
...
} else {
//Handle read failure
}
So here is my main question will std::ifstream ever fail to open a recently created file in the current working directory? If so, what would be a way to make it more robust (specifically on Linux)?
A side/secondary question: Is there a very simplified way to achieve what I'm trying to achieve without using file pipes? Perhaps some stuff available in unistd.h? Thanks for your time.
So here is my main question will std::ifstream ever fail to open a recently created file in the current working directory?
Yes.
Mount a USB thumb drive (or some other removable media)
cd to the mount
Execute your program. While it's executing, remove the drive.
Watch the IO error happen.
There's a ton of other reasons too. Filesystem corruption, hitting the file descriptor limit, etc.
If so, what would be a way to make it more robust (specifically on Linux)?
Make temporary files in /tmp, whose entire purpose is for temporary files. Or don't create a file at all, and use pipes for communication instead (Like what popen does, like harmic suggested). Even so, there are no guarantees; try to gracefully handle errors.

Sending data to stdin of another process through linux terminal

I've been trying to send data to stdin of a running process. Here is what I do:
In a terminal I've started a c++ program that simply reads a string and prints it. Code excerpt:
while (true) {
cin >> s;
cout << "I've just read " << s << endl;
}
I get the PID of the running program
I go to /proc/PID/fd/
I execute echo text > 0
Result: text appears in the terminal where the program is run. Note, not I've just read text, but simply text.
What am I doing wrong and what should I do to get this thing to print 'I've just read text'?
When you're starting your C++ program you need to make sure its input comes from a pipe but not from a terminal. You may use cat | myapp to do that. Once it's running you may use PID of your application for echo text > /proc/PID/fd/0
It could be a matter of stdout not being properly flushed -- see "Unix Buffering". Or you could be in a different shell as some commentators have suggested.
Generally, it's more reliable to handle basic interprocess communication via FIFOs or NODs -- named pipes. (Or alternatively redirect stdout and/or stderr to a file and read from that with your c++ program.)
Here's some good resources on how to use those in both the terminal and c++.
"FIFO – Named pipes: mkfifo, mknod"
"Using Pipes in Linux Processes"
"Programming with FIFO: mkfifo(), mknod()"
FD 0 is the terminal the program is running from. When you write to FD 0, you are writing to the terminal the program is running from. FD 0 is not required to be opened in read-only mode; in practice it seems to be read/write mode, so you can write to it. (I suspect this is because FDs 0, 1 and 2 all refer to the same file description)
So echo text > /proc/PID/fd/0 just echoes text to the terminal.
To pipe input to the program, you would need to write to the other end of the pipe (actually a PTY, which mostly behaves like a pair of pipes). Most likely, whatever terminal emulator you're using (xterm, konsole, gnome-terminal) will have the other end open, so you could try writing to that.

How to redirect output from reading a bash script in c++?

I know that the function:
system("myfile.sh")
exec a bash script.
Ok but now I want to redirect the output to my program to ensure the reading.
For example the script date.sh give me the date of my system, and i want to see it on my program with std::cout << OUTPUTDATE;
Is it possible?
How?
Use popen instead of system.
The function popen will give you a FILE * you can read from.
FILE *script = popen("myfile.sh", "r");
while (fgets(line, LENGTH, script)) {
/* ... */
}
pclose(script);