Executing shell commands with execvp() - c++

I want to write a program which acts like a Linux shell. I started with writing a small program to execute the "ls" command. What I can't figure out is how should I proceed in order to make my program respond to any command like the shell does. ( e.g cat, cd, dir).
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#define MAX 32
using namespace std;
int main() {
pid_t c;
char s[MAX];
int fd[2];
int n;
pipe(fd);
c = fork();
if(c == 0) {
close(fd[0]);
dup2(fd[1], 1);
execlp("ls", "ls", "-l", NULL);
return 0;
} else {
close(fd[1]);
while( (n = read(fd[0], s, MAX-1)) > 0 ) {
s[n] = '\0';
cout<<s;
}
close(fd[0]);
return 0;
}
return 0;
}
How can I make my program read what the user types in and passes it to execlp (or something similar that does the same thing)?

A shell basically does the following :
reads a line from stdin
parses that line to make a list of words
forks
then the shell (parent process) waits until the end of the child, while the child execs to execute the code of the command represented by the list of words extracted from the input line.
the shell then restarts at step 1.
Construct first a very simple shell.

If I correctly understand a problem, You can:
read array of strings with scanf()
run it as a command with execvp() (it works the same as execlp(), but You can pass all arguments as an array).
Something like:
char args[100][50];
int nargs = 0;
while( scanf( " %s ", args[nargs] ) )
nargs++;
args[nargs] = NULL;
/* fork here *
...
/* child process */
execvp( args[0], args );

Related

Trying to make a scalable pipe and execvp program using loop

This program is trying to any number of commands greater than one and use pipes, execvp, and fork to chain them together much like a shell would. In this code I have a hard coded "ls" "wc" and "less" that should come out like running "ls | wc | less" on a shell. For some reason, the pipes are not working as intended. I have a big block of comments explaining what I think the problem is on line 99 (starting with "The read end of the..." ). I know there is no error checking, any help is appreciated.
#include <iostream>
#include <string.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
#define READ 0
#define WRITE 1
//This program will do three different commands ls, wc, then less.
int main(){
pid_t pid;
int cmd=3;
//One less pipe than command is required.
int fd[cmd-1][2];
//The pipes are created in a for loop.
for(int i=0; i<(cmd-1); i++){
if(pipe(fd[i])==-1){
cout<<"Help"<<endl;
}
}
//The commands are put in c.
char* c[3];
c[0]="ls";
c[1]="wc";
c[2]="less";
//First fork
pid=fork();
if(pid==0){
//Pipe 0 is linked up.
close(fd[0][READ]);
dup2(fd[0][WRITE], 1);
close(fd[0][WRITE]);
//Remaining pipes are closed.
for(int i=1; i<(cmd-1); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
//The command is prepared and then execvp is executed.
char* temp[2];
temp[0]=c[0];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
}
//This for loop executes two times less than the number of commands.
for(int i=0; i<(cmd-2); i++){
pid=fork();
if(pid==0){
//I link up the read connection with pipe 0, I am fairly certain that
//this part is working. You can put a cout after this pipe and it will
//print that of command 1.
close(fd[i][WRITE]);
dup2(fd[i][READ], 0);
close(fd[i][READ]);
//This is the linking of pipe 1.
close(fd[i+1][READ]);
dup2(fd[i+1][WRITE], 1);
close(fd[i+1][WRITE]);
//This closes the remaining pipes, in this case there are none.
for(int j=0; j<(cmd-1); j++){
if(j==i || j==(i+1)){
continue;
}
close(fd[j][READ]);
close(fd[j][WRITE]);
}
//The command is prepared and executed
char* temp[2];
temp[0]=c[i+1];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
}
}
pid=fork();
if(pid==0){
//The read end of the final pipe is linked here.
//THIS IS WERE THE PROBLEM IS! For some reason after dup2, I can no longer
//use cin. Inbetween the linking of pipe 0 and pipe 1 (line 66), I can
//use cin to make sure that the first execvp works and put its output in the
//pipe. I also know that the second execvp works as intended. I just need to
//know why dup2 messes up my program here.
close(fd[cmd-2][WRITE]);
dup2(fd[cmd-2][READ], 0);
close(fd[cmd-2][READ]);
//closes the remaining pipes.
for(int i=0; i<(cmd-2); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
//Preps next command.
char* temp[2];
temp[0]=c[cmd];
temp[1]=NULL;
char* x=temp[0];
execvp(x, temp);
//}
//closes all pipes.
for(int i=0; i<(cmd-1); i++){
close(fd[i][READ]);
close(fd[i][WRITE]);
}
return 0;
}
Your code has multiple problems
e.g. you've not allocated memory to commands and your code doesn't seem to be properly enclosed within brackets
I've modified your code as follows :
#include <iostream>
#include <string.h>
#include <string>
#include <vector>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
//This program will do three different commands ls, wc, then less.
int main(){
pid_t pid = 0;
int cmd=3, i;
//One less pipe than command is required.
int fd[cmd-1][2];
//The pipes are created in a for loop.
for(int i=0; i<(cmd-1); i++){
if(pipe(fd[i])==-1){
cout<<"Help"<<endl;
}
}
//The commands are put in c.
char c[3][8] = {{'l', 's', '\0'}, {'w', 'c', '\0'}, {'l','e','s','s', '\0'}}, *temp[2];
for(i = 0; i < cmd-1; i ++){
pid = fork();
if(pid == 0){
if(i != 0){
// read from previous fd
close(fd[i-1][1]);
dup2(fd[i-1][0], STDIN_FILENO);
close(fd[i-1][0]);
}
// write to current fd
close(fd[i][0]);
dup2(fd[i][1], STDOUT_FILENO);
close(fd[i][1]);
temp[0] = c[i];
temp[1] = NULL;
execvp(c[i], temp);
exit(0);
}
else{
if(i != 0){
// close unnecessary fds in parent
close(fd[i-1][0]);
close(fd[i-1][1]);
}
}
}
// the last command i.e. less here
if(i > 0){
close(fd[i-1][1]);
dup2(fd[i-1][0], STDIN_FILENO);
close(fd[i-1][0]);
}
temp[0] = c[i];
temp[1] = NULL;
execvp(c[i], temp);
return 0;
}
Let me know if it works for you!

Why does using process substitution here cause a hang?

I have a program which needs to launch other programs, possibly substituting their stdio with files and pipes. While it appears to "work" in that the sub-process does get it's I/O from the source pipe, unfortunately, it also causes a hang. The sub-process is seemingly never getting an EOF.
Here is a minimal reproduction of the code, why does this hang after printing "Hello World\n"?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
switch (pid_t pid = fork()) {
case 0: {
// in child
// replace the child's stdin with whatever filename is given as argv[1]
freopen(argv[1], "r+b", stdin);
// construct an argv array for to exec, no need for anything except
// argv[0] since we want it to use stdin
char path[] = "/bin/cat";
char *args[] = {path, NULL};
// run it!
execv(args[0], args);
abort(); // we should never get here!
}
case -1:
// error
return -1;
default: {
// in parent, just wait for the sub-process to terminate
int status;
const auto r = waitpid(pid, &status, __WALL);
if (r == -1) {
perror("waitpid");
return -1;
}
break;
}
}
}
# runs printf creating a pipe, which is then passed as the argv of my test program
./test >(printf "Hello\n")
./test <(printf "Hello\n")
Switch to >(...) to <(...) to read from printf rather than write to it.
freopen(argv[1], "rb", stdin);
Don't use r+. You're only reading from the file, so make it r.

Find CPU usage using 'top' in Linux and parsing result [duplicate]

Is there any way to store output of system command into char array, since system command is returning only int.
There's no way to retrieve the output of system(3). Well, you could redirect the output of whatever command is executed to a file and then open and read that file, but a more sane approach is to use popen(3).
popen(3) replaces system(3) and it allows you to read the output of a command (or, depending on the flags you pass it, you can write to the input of a command).
Here's an example that executes ls(1) and prints the result:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *ls_cmd = popen("ls -l", "r");
if (ls_cmd == NULL) {
fprintf(stderr, "popen(3) error");
exit(EXIT_FAILURE);
}
static char buff[1024];
size_t n;
while ((n = fread(buff, 1, sizeof(buff)-1, ls_cmd)) > 0) {
buff[n] = '\0';
printf("%s", buff);
}
if (pclose(ls_cmd) < 0)
perror("pclose(3) error");
return 0;
}

Unix command line failing to run program after compiling with no error message

I'm trying to run a C++ program I've been writing from my school's Unix Command-Line based server. The program is supposed to use commands like pipe() and fork() to calculate an integer in the child process and send it to the parent process through a pipe. The problem I've come across is when I try to run the program after compiling it, nothing happens at all except for a '0' is inserted before the prompt. I don't completely understand forking and piping so I'll post the entire program in case the problem is in my use of those commands. There are probably errors because I haven't been able to successfully run it yet. Here is my code:
#include <cstdlib>
#include <iostream>
#include <string>
#include <array>
#include <cmath>
#include <unistd.h>
using namespace std;
// Return bool for whether an int is prime or not
bool primeChecker(int num)
{
bool prime = true;
for (int i = 2; i <= num / 2; ++i)
{
if (num%i == 0)
{
prime = false;
break;
}
}
return prime;
}
int main(int argc, char *argv[])
{
int *array;
array = new int[argc - 1]; // dynamically allocated array (size is number of parameters)
int fd[2];
int count = 0; // counts number of primes already found
int num = 1; // sent to primeChecker
int k = 1; // index for argv
int addRes = 0;
// Creates a pair of file descriptors (int that represents a file), pointing to a pipe inode,
// and places them in the array pointed to. fd[0] is for reading, fd[1] is for writing
pipe(fd);
while (k < argc)
{
if (primeChecker(num)) // if the current number is prime,
{
count++; // increment the prime number count
if (count == (stoi(argv[k]))) // if the count reaches one of the arguments...
{
array[k - 1] = num; // store prime number
k++; // increment the array of arguments
}
}
num++;
}
pid_t pid;
pid = fork();
if (pid < 0) // Error occurred
{
cout << "Fork failed.";
return 0;
}
else if(pid == 0) // Child process
{
for (int i = 0; i < (argc-1); i++)
{
// Close read descriptor (not used)
close(fd[0]);
// Write data
write(fd[1], &addRes, sizeof(addRes)); /* write(fd, writebuffer, max write lvl) */
// Close write descriptor
close(fd[1]);
}
}
else // Parent process
{
// Wait for child to finish
wait(0);
// Close write descriptor (not used)
close(fd[1]);
// Read data
read(fd[0], &addRes, sizeof(addRes));
cout << addRes;
// Close read descriptor
close(fd[0]);
}
return 0;
}
Here is what I'm seeing in the command window (including the prompt) when I try to compile and run my program:
~/cs3270j/Prog2$ g++ -o prog2.exe prog2.cpp
~/cs3270j/Prog2$ ./prog2.exe
0~/cs3270j/Prog2$
and nothing happens. I've tried different naming variations as well as running it from 'a.out' with no success.
tl;dr after compiling and attempting to execute my program, the Unix command prompt simply adds a 0 to the beginning of the prompt and does nothing else.
Any help that anybody could give me would be very much appreciated as I can't find any information whatsoever about a '0' appearing before the prompt.
Your program is doing exactly what you're telling it to do! You feed addRes into the pipe, and then print it out. addRes is initialized to 0 and never changed. In your child, you want to pass num instead. Also, you may want to print out a new line as well ('\n').
You never write anything to the pipe; writing is once per each command line argument, and ./prog2.exe does not supply any, so the loop never executes
If you passed one argument, you would write addRes; you never change addRes, so you'd get 0 in the parent
If you passed multiple arguments, you'd write one addRes then close the channel. This is not too bad since you never read more than one addRes anyway.
You print out your addRes (which is unchanged from its initialisation int addRes = 0) without a newline, which makes the next prompt stick right next to it (using cout << addRes << endl would print out a newline, making it prettier)

Store output of system command into local char array in c

Is there any way to store output of system command into char array, since system command is returning only int.
There's no way to retrieve the output of system(3). Well, you could redirect the output of whatever command is executed to a file and then open and read that file, but a more sane approach is to use popen(3).
popen(3) replaces system(3) and it allows you to read the output of a command (or, depending on the flags you pass it, you can write to the input of a command).
Here's an example that executes ls(1) and prints the result:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *ls_cmd = popen("ls -l", "r");
if (ls_cmd == NULL) {
fprintf(stderr, "popen(3) error");
exit(EXIT_FAILURE);
}
static char buff[1024];
size_t n;
while ((n = fread(buff, 1, sizeof(buff)-1, ls_cmd)) > 0) {
buff[n] = '\0';
printf("%s", buff);
}
if (pclose(ls_cmd) < 0)
perror("pclose(3) error");
return 0;
}