exec family with a file input - c++

Hey guys I am trying to write a shell with C++ and I am having trouble with the function of using input file with the exec commands. For example, the bc shell in Linux is able to do “bc < text.txt” which calculate the lines in the text in a batch like fashion. I am trying to do likewise with my shell. Something along the lines of:
char* input = “input.txt”;
execlp(input, bc, …..) // I don’t really know how to call the execlp command and all the doc and search have been kind of cryptic for someone just starting out.
Is this even possible with the exec commands? Or will I have to read in line by line and run the exec commands in a for loop??

You can open the file and then dup2() the file descriptor to standard input, or you can close standard input and then open the file (which works because standard input is descriptor 0 and open() returns the lowest numbered available descriptor).
const char *input = "input.txt";
int fd = open(input, O_RDONLY);
if (fd < 0)
throw "could not open file";
if (dup2(fd, 0) != 0) // Testing that the file descriptor is 0
throw "could not dup2";
close(fd); // You don't want two copies of the file descriptor
execvp(command[0], &command[0]);
fprintf(stderr, "failed to execvp %s\n", command[0]);
exit(1);
You would probably want cleverer error handling than the throw, not least because this is the child process and it is the parent that needs to know. But the throw sites mark points where errors are handled.
Note the close().

the redirect is being performed by the shell -- it's not an argument to bc. You can invoke bash (the equivalent of bash -c "bc < text.txt")
For example, you can use execvp with a file argument of "bash" and argument list
"bash"
"-c"
"bc < text.txt"

Related

Input redirection using dup2()

So I am trying to implement the following command line statement in c++ by using dup2() and execvp(): wc < inputFile.txt then return to my command line. So basically I am forking a process and executing that command in the child process.
However my code the following error: wc: stdin: read: Bad file descriptor
Here is my code:
int file_desc = open(fileName.c_str(), O_WRONLY | O_APPEND);
int stdin = dup(0);
dup2(file_desc,0);
execvp (args2[0], args2); // now execute
dup2(stdin, 0);
So my thought process was that I needed to redirect the standard in (aka index 0 of the file descriptor table) to the file descriptor of the file since at index is always stdin and that's where input is read from. Then after I execute, I replace it back with the original standard in. So I am confused about what I am doing wrong.
The file_desc is opened only for writing (O_WRONLY) - try opening it for reading (O_RDONLY).
You might also want to:
dup2() between fork() and exec() instead of saving and restoring stdin - less system calls and saves a race in multi-threaded apps.
close file_desc in the parent process
close file_desc in the child process after the dup2 (and before the exec)

How do I get the content of llvm::MemoryBuffer when reading STDIN?

I am using llvm::MemoryBuffer::getFileOrSTDIN("-") and, according to the specification, it should Open the specified file as a MemoryBuffer, or open stdin if the Filename is "-".
Now, in the following context:
auto Source = llvm::MemoryBuffer::getFileOrSTDIN(File);
if (std::error_code err = Source.getError()) {
llvm::errs() << err.message();
} else{
someFunction(std::move(*Source), File, makeOutputWriter(Format, llvm::outs()),
IdentifiersOnly, DumpAST);
}
it blocks on the first line (when File == "-"); as expected as the STDIN never closes.
When a special *char appears in STDIN, let's say <END_CHAR>, I know that I am finished reading for a given task. How could I close the STDIN in this situations and move on to someFunction ?
Thanks,
You can always close the stdin file descriptor using close, i.e. close(0). If you check llvm::MemoryBuffer's source, you'll see that getFileOrSTDIN() basically boils down to a call to llvm::MemoryBuffer::getMemoryBufferForStream() with the first argument (the file descriptor) set to 0.
Also, see this SO answer.
The special character to close the standard input is ctrl-d (in *nix at least) on the command line (have a look here).

How to use standard input '<' in execl?

I want to achieve the equivalent of "./myfile < input.txt" using execl():
execl("path/myfile", ",myfile", "< input.txt");
execl("/home/user/Desktop/Fuzzer/clear/easy_fuzzer/buf", "/home/user/Desktop/Fuzzer/clear/easy_fuzzer/buf", "buf < input", NULL);
execlp("/home/user/Desktop/Fuzzer/clear/easy_fuzzer/buf", "/home/user/Desktop/Fuzzer/clear/easy_fuzzer/buf", "input");
but the command fails...
I want 'input.txt' and '<' command through myfile using execl — how do I do it?
Your code has to do the I/O redirection before you run the execl() code.
If you want to achieve the effect of a shell running:
/user/Desktop/Fuzzer/clear/easy_fuzzer/buf < input
then you will need to write something like this in the child code:
const char *filename = "input"; // or "input.txt" — the question uses both
int fd = open(file, O_RDONLY);
if (fd < 0)
err_syserr("failed to open file %s for reading\n", filename);
if (dup2(fd, STDIN_FILENO) < 0)
err_syserr("failed to redirect %s to standard input\n", filename);
close(fd); // In theory, it could fail, but there isn't much you can do about it
const char *cmdpath = "/home/user/Desktop/Fuzzer/clear/easy_fuzzer/buf";
execl(cmdpath, "buf", (char *)NULL);
err_syserr("failed to execute program %s\n", cmdpath);
This should normally all be in the code executed by the child.
You can find the code for the err_syserr() function in stderr.c and stderr.h from https://github.com/jleffler/soq/tree/master/src/libsoq. One line error handling makes it less onerous than writing out multiple lines. Note that there's no reason to check the return value from any of the exec*() functions. If the function returns, it failed. If it succeeds, there's a different process running in place of the current process.
If you like doing things the long-winded way, you can investigate whether your system supports posix_spawn() and its colleagues. You can do all sorts of things by setting up appropriate sequences of attributes. For my money, it is far simpler and clearer to write the code as shown above.
I would probably not use execl() — I'd probably use execv() (or perhaps execvp()) because it allows the argument list to be fixed at run-time instead of mandating that it is fixed at compile time. The code passes buf as the value for argv[0] to the executed program. If you want the full path name as argv[0] you can do that.
Note that if the file name part of the first argument to execlp() (or execvp(), or any other path-searching exec*() function) contains any / at all, then there is no path-based search performed, so it is not appropriate to use them if the command name is an absolute path name as in the example.

Exec() read from file

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.

Returning output from bash script to calling C++ function

I am writing a baby program for practice. What I am trying to accomplish is basically a simple little GUI which displays services (for Linux); with buttons to start, stop, enable, and disable services (Much like the msconfig application "Services" tab in Windows). I am using C++ with Qt Creator on Fedora 21.
I want to create the GUI with C++, and populating the GUI with the list of services by calling bash scripts, and calling bash scripts on button clicks to do the appropriate action (enable, disable, etc.)
But when the C++ GUI calls the bash script (using system("path/to/script.sh")) the return value is only for exit success. How do I receive the output of the script itself, so that I can in turn use it to display on the GUI?
For conceptual example: if I were trying to display the output of (systemctl --type service | cut -d " " -f 1) into a GUI I have created in C++, how would I go about doing that? Is this even the correct way to do what I am trying to accomplish? If not,
What is the right way? and
Is there still a way to do it using my current method?
I have looked for a solution to this problem but I can't find information on how to return values from Bash to C++, only how to call Bash scripts from C++.
We're going to take advantage of the popen function, here.
std::string exec(char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
This function takes a command as an argument, and returns the output as a string.
NOTE: this will not capture stderr! A quick and easy workaround is to redirect stderr to stdout, with 2>&1 at the end of your command.
Here is documentation on popen. Happy coding :)
You have to run the commands using popen instead of system and then loop through the returned file pointer.
Here is a simple example for the command ls -l
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *process;
char buff[1024];
process = popen("ls -l", "r");
if (process != NULL) {
while (!feof(process)) {
fgets(buff, sizeof(buff), process);
printf("%s", buff);
}
pclose(process);
}
return 0;
}
The long approach - which gives you complete control of stdin, stdout, and stderr of the child process, at the cost of fairly significant complexity - involves using fork and execve directly.
Before forking, set up your endpoints for communication - pipe works well, or socketpair. I'll assume you've invoked something like below:
int childStdin[2], childStdout[2], childStderr[2];
pipe(childStdin);
pipe(childStdout);
pipe(childStderr);
After fork, in child process before execve:
dup2(childStdin[0], 0); // childStdin read end to fd 0 (stdin)
dup2(childStdout[1], 1); // childStdout write end to fd 1 (stdout)
dup2(childStderr[1], 2); // childStderr write end to fd 2 (stderr)
.. then close all of childStdin, childStdout, and childStderr.
After fork, in parent process:
close(childStdin[0]); // parent cannot read from stdin
close(childStdout[1]); // parent cannot write to stdout/stderr
close(childStderr[1]);
Now, your parent process has complete control of the std i/o of the child process - and must safely multiplex childStdin[1], childStdout[0], and childStderr[0], while also monitoring for SIGCLD and eventually using a wait-series call to check the process termination code. pselect is particularly good for dealing with SIGCLD while dealing with std i/o asynchronously. See also select or poll of course.
If you want to merge the child's stdout and stderr, just dup2(childStdout[1], 2) and get rid of childStderr entirely.
The man pages should fill in the blanks from here. So that's the hard way, should you need it.