Shell script for input output redirection - c

Hi I am writing a shell script for input output redirection. The code is as follows
int parse(void)
{
char *p; /* pointer to current word */
char *msg; /* error message */
nwds = 0;
p = strtok(line," \t");
while (p != NULL) {
if (nwds == NWORDS) {
msg = "Error: too many words.\n";
write(2 ,msg, strlen(msg));
return 0;
}
if (strlen(p) >= MAXWORDLEN) {
msg = "Error: Word too long.\n";
write(2, msg ,strlen(msg));
return 0;
}
if(*p == '<') /* check if input symbol is present*/
isInput = true;
if(*p == '>') /* check if Output symbol is present*/
isOutput = true;
if(isInput) {
p = strtok(NULL," \t"); /* get pointer to next word, if any */
input = p;
}
if(isOutput) {
p = strtok(NULL," \t"); /* get pointer to next word, if any */
output = p;
}
words[nwds] = p; /* save pointer to the word */
nwds++; /* increase the word count */
p = strtok(NULL," \t"); /* get pointer to next word, if any */
}
return 1;
}
The above code takes commands from inputs and validates the number of commands and word length. It also validates the input and output redirection characters and stores the files names in input and output character pointers
int execute(void)
{
int i, j;
int status;
char *msg;
pid_t child_pid;
int in;
int out;
if (execok() == 0) { /* is it executable? */
status = fork(); /* yes; create a new process */
if (status == -1) { /* verify fork succeeded */
perror("fork");
exit(1);
}
if (status == 0) { /* in the child process... */
words[nwds] = NULL; /* mark end of argument array */
if(input != NULL && output != NULL)
{
in = open (input, O_RDONLY);
out = open (output, O_TRUNC | O_CREAT | O_WRONLY, 0666);
if ((in <= 0) || (out <= 0))
{
fprintf (stderr, "Couldn't open a file\n");
exit (errno);
}
dup2(in, 0);
dup2(out, 1);
close(in);
close out);
}
status = execve(path,words,environ); /* try to execute it */
perror("execve"); /* we only get here if */
_exit(0); /* execve failed... */
}
/*------------------------------------------------*/
/* The parent process (the shell) continues here. */
/*------------------------------------------------*/
child_pid = wait(&status); /* wait for process to end */
if (v_opt) { /* display status? */
char emsg[100];
if (WIFEXITED(status))
sprintf(emsg,"Child process %u exited with status %d.\n",
child_pid, WEXITSTATUS(status));
else
sprintf(emsg,"Child process %u did not exit normally.\n",
child_pid);
write(1,emsg,strlen(emsg));
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "Error: '";
write(2,msg,strlen(msg));
write(2,words[0],strlen(words[0]));
msg = "' cannot be executed.\n";
write(2,msg,strlen(msg));
}
}
This is the execute function which performs redirection.
I am new to shell scripting, I gained some knowledge by going through the web and coded this program. This program is not redirecting the input and output. Could you please help me with this

Related

Implementing >> in a Linux shell using C

I am trying to create a Linux shell in C which would perform basic Linux shell operation. I have got > and < part working. But I am stuck in >> operation where a file is supposed to open in append mode. Below is what I have done for >> operation so far. Could somebody help me pointing out where I am doing it wrong. Thanks in advance.
extern char **environ;
char words[MAXWORDS][MAXWORDLEN+1]; /* words on the command line */
int nwds; /* number of words in words array */
int ignore[MAXWORDS]; /* ignore[i] != 0 --> ignore words[i] */
char line[MAXLINELEN+1]; /* current input line */
char *eveargs[MAXWORDS+1]; /* pointers to execve arguments */
char *ofn;
for (i=1; i<nwds; i++) { /* number of words in words array */
if (ignore[i]) /* skip those words in input redir */
continue;
if (strcmp(words[i],">>")) /* loop through words array. skip if not double greater-than symbol */
continue;
if (i == nwds-1 || ignore[i+1] != 0) { /* verify filename */
write(1,NOFILENAME,strlen(NOFILENAME)); /* error message */
fail = 1;
break;
}
if (ofn != NULL) { /* ofn is an outputfilename */
write(1,TOOMANYREDIRECTS,strlen(TOOMANYREDIRECTS)); /* error message */
fail = 1;
break;
}
ignore[i] = 1;
ignore[i+1] = 1;
ofn = words[i+1];
i++;
}
/*-----------------------------------------------*/
/* Setup >> file descriptor per redirection. */
/*-----------------------------------------------*/
if (ofn != NULL) {
int fd = open(ofn,O_CREAT|O_WRONLY|O_APPEND,0644);
if (fd < 0) {
write(2,CANNOTAPPEND,strlen(CANNOTAPPEND)); /* error message */
exit(1);
}
close(1);
dup(fd);
close(fd);
}
execve(path,eveargs,environ);
write(2,CANNOTEXEC,strlen(CANNOTEXEC)); /* error message */
exit(1);
/*-----------------------------------------------*/
/* Setup output file descriptor per redirection. */
/*-----------------------------------------------*/
if (ofn != NULL) {
int fd = open(ofn, O_CREAT|O_WRONLY|O_TRUNC,0644);
if (fd < 0) {
write(2,CANNOTCREATE,strlen(CANNOTCREATE));
exit(1);
}
close(1);
dup(fd);
close(fd);
}
execve(path,eveargs,environ);
write(2,CANNOTEXEC,strlen(CANNOTEXEC));
exit(1);

C Shell: Redirection and piping working, but not a combination of input and output redirection with 1 or more pipes

My C shell can successfully handle redirection (e.g. ls -al > output.txt, ./pre < input1.txt, etc.) and multiple pipes (i.e. cmd1 | cmd 2 | cmd 3). However, my code is not working when I try to do input and output redirection together with a pipe, such as ./pre < input.txt | ./sort > output.txt. No output file is ever made, although the ./pre does successfully execute.
pre is an executable that prints names with GPAs over 3.0
sort is an executable that alphabetizes a list of names
input.txt is a file with names and GPAs (Name 3.1...).
CODE:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef READ
#define READ 0
#endif
#ifndef WRITE
#define WRITE 1
#endif
void clearArgumentContainer (int argumentContainer[]);
int main() {
/* professor-supplied variables for commands and command parsing */
char *iPath, *oPath, *argv[20], buf[80], n, *p;
int m, status, inword, continu;
int start[20];
/* flags for redirection (note: C does not have type bool; using integer value 0 or 1) */
int inputRedirectFlag, outputRedirectFlag;
/* variables for piping */
int count, pipes;
pid_t pid;
/* pipes */
int l_pipe[2], r_pipe[2];
/* required container for handling arguments */
int argumentContainer[20] = { 0 };
while (1) {
inword = m = continu = count = pipes = pid = 0;
p = buf;
/* redirection flags reset */
inputRedirectFlag = outputRedirectFlag = 0;
/* shell prompt */
printf("\nshhh> ");
/* command parsing */
while ((n = getchar()) != '\n' || continu)
{
if (n == ' ') {
if (inword)
{
inword = 0;
*p++ = 0;
}
}
else if (n == '\n')
continu = 0;
else if (n == '\\' && !inword)
continu = 1;
else {
if (!inword)
{
inword = 1;
argv[m++] = p;
*p++ = n;
}
else
*p++ = n;
}
} /* end of command parsing */
*p++ = 0;
argv[m] = 0;
/* user wishes to terminate program */
if (strcmp(argv[0], "exit") == 0)
exit(0);
/* manage redirection */
while (argv[count] != 0) {
if (strcmp(argv[count], "|") == 0) {
argv[count] = 0;
argumentContainer[pipes + 1] = count + 1;
++pipes;
}
else if (strcmp(argv[count], "<") == 0) {
iPath = strdup(argv[count + 1]); /* copy string argument (file string) */
argv[count] = 0;
argv[count + 1] = 0;
inputRedirectFlag = 1;
}
else if (strcmp(argv[count], ">") == 0) {
oPath = strdup(argv[count + 1]); /* copy string argument (file string) */
argv[count] = 0;
argv[count + 1] = 0;
outputRedirectFlag = 1;
}
else {
argumentContainer[count] = count;
}
++count;
} /* end of redirection management */
/* execute commands [<= in for-loop; n pipes require n+1 processes] */
for (int index = 0; index <= pipes; ++index) {
if (pipes > 0 && index != pipes) { /* if user has entered multiple commands with '|' */
pipe(r_pipe); /* no pipe(l_pipe); r_pipe becomes next child's l_pipe */
}
/* switch-statement for command execution */
switch (pid = fork()) {
/* fork() error */
case -1: perror("fork failed");
break;
case 0: /* child process manages redirection and executes */
if ((index == 0) && (inputRedirectFlag == 1)) {
int inputFileDescriptor = open(iPath, O_RDONLY , 0400);
if (inputFileDescriptor == -1) {
perror("input file failed to open\n");
return(EXIT_FAILURE);
}
close(READ);
dup(inputFileDescriptor);
close(inputFileDescriptor);
} /* end of input redirection management */
if ((index == pipes) && (outputRedirectFlag == 1)) {
int outputFileDescriptor = open(oPath, O_WRONLY | O_CREAT, 0755);
if (outputFileDescriptor < 0) {
perror("output file failed to open\n");
return(EXIT_FAILURE);
}
close(WRITE);
dup(outputFileDescriptor);
close(outputFileDescriptor);
} /* end of output redirection management */
/* manage pipes if (a) first child process, (b) middle child process, or (c) third/final child process */
if (pipes > 0) {
if (index == 0){ /* first child process */
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[WRITE]);
close(r_pipe[READ]);
}
else if (index < pipes) { /* middle child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[READ]);
close(r_pipe[WRITE]);
}
else { /* third/final child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
}
}
/* execute command */
execvp(argv[argumentContainer[index]], &argv[argumentContainer[index]]);
/* if execvp() fails */
perror("execution of command failed\n");
break;
default: /* parent process manages the pipes for child process(es) */
if (index > 0) {
close(l_pipe[READ]);
close(l_pipe[WRITE]);
}
l_pipe[READ] = r_pipe[READ];
l_pipe[WRITE] = r_pipe[WRITE];
/* parent waits for child process to complete */
wait(&status);
break;
} /* end of switch-statement for command execution */
} /* end of loop for all pipes */
// clear all executed commands
for (int i = 0; i < 20; ++i) {
argv[i] = 0;
}
}
}
void clearArgumentContainer (int argumentContainer[]){
// clear argument container
for (int i = 0; i < 20; ++i) {
argumentContainer[i] = 0;
}
}
Here is the input file I'm using:
Tim 3.5
Todd 2.1
Beth 3.9
Jason 3.5
Zander 3.3
Alex 3.5
Tyler 3.5
Lauren 3.6
Jack 2.3
Amir 3.4
Beth 3.2
pre executable will list only those names with GPAs higher than 3.0
sort executable will sort list of names in alphabetical order
Here is my final working code. It does not implement built-ins like cd, but I plan to implement those built-ins soon! Program does meet the requirements for the assignment.
My major change occurred in the redirection handling. I had to remove a line of code in two places:
argv[count + 1] = 0;
From the redirection for "<" and ">."
I also added code to handle the connections of the pipes, whether my process was the first child, the last, or one in between.
Code:
/***********************************************************************************************
***********************************************************************************************
Student: Douglas Adolph
Course: Operating Systems
Project #: 2
Program emulates shell, and can do the following:
1. Can execute a command with the accompanying arguments.
2. Recognize multiple pipe requests and handle them.
3. Recognize redirection requests and handle them.
4. Type "exit" to quit the shhh shell.
Notes:
Shell built-ins (cd, echo, etc.) not yet implemented
REFERENCED:
1. http://www.thinkplexx.com/learn/article/unix/command
2. http://man7.org/linux/man-pages/man2/open.2.html
3. https://stackoverflow.com/questions/19846272/redirecting-i-o-implementation-of-a-shell-in-c
**********************************************************************************************
*********************************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef READ
#define READ 0
#endif
#ifndef WRITE
#define WRITE 1
#endif
void clearArgIndexContainer (int argLocation[]);
int main() {
/* variables for command parsing and storage*/
char n, *parser, buf[80], *argv[20];
int m, status, inword, continu;
/* variables and flags for redirection (note: C does not have type bool; using integer value 0 or 1) */
char *in_path, *out_path;
int inputRedirectFlag, outputRedirectFlag;
/* variables for piping */
int count, pipes;
pid_t pid;
/* left and right pipes */
int l_pipe[2], r_pipe[2];
/* container for recording argument locations in argv[] */
int argLocation[20] = { 0 };
while (1) {
/* reset parsing and piping variable values */
m = inword = continu = count = pipes = pid = 0;
/* begin parsing at beginning of buffer */
parser = buf;
/* reset redirection flags */
inputRedirectFlag = outputRedirectFlag = 0;
/* print shell prompt */
printf("\nshhh> ");
/* parse commands */
while ((n = getchar()) != '\n' || continu)
{
if (n == ' ') {
if (inword)
{
inword = 0;
*parser++ = 0;
}
}
else if (n == '\n')
continu = 0;
else if (n == '\\' && !inword)
continu = 1;
else {
if (!inword)
{
inword = 1;
argv[m++] = parser;
*parser++ = n;
}
else
*parser++ = n;
}
} /* end of command parsing */
/* append terminating character to end of parser buffer and argv buffer */
*parser++ = 0;
argv[m] = 0;
/* user wishes to terminate program */
if (strcmp(argv[0], "exit") == 0)
exit(0);
/* manage redirection */
while (argv[count] != 0) {
if (strcmp(argv[count], "|") == 0) {
argv[count] = 0;
argLocation[pipes + 1] = count + 1;
++pipes;
}
else if (strcmp(argv[count], "<") == 0) {
in_path = strdup(argv[count + 1]);
argv[count] = 0;
inputRedirectFlag = 1;
}
else if (strcmp(argv[count], ">") == 0) {
out_path = strdup(argv[count + 1]);
argv[count] = 0;
outputRedirectFlag = 1;
}
else {
argLocation[count] = count;
}
++count;
} /* end of redirection management */
/* execute commands [<= in for-loop; n pipes require n+1 processes] */
for (int index = 0; index <= pipes; ++index) {
if (pipes > 0 && index != pipes) { /* if user has entered multiple commands with '|' */
pipe(r_pipe); /* no pipe(l_pipe); r_pipe becomes next child's l_pipe */
}
/* switch-statement for command execution */
switch (pid = fork()) {
case -1: perror("fork failed"); /* fork() error */
break;
case 0: /* child process manages redirection and executes */
if ((index == 0) && (inputRedirectFlag == 1)) {
int inputFileDescriptor = open(in_path, O_RDONLY , 0400);
if (inputFileDescriptor == -1) {
perror("input file failed to open\n");
return(EXIT_FAILURE);
}
close(READ);
dup(inputFileDescriptor);
close(inputFileDescriptor);
} /* end of input redirection management */
if ((index == pipes) && (outputRedirectFlag == 1)) {
//printf("DEBUG: here we should be about to create our output file\n");
int outputFileDescriptor = creat(out_path, 0700);
if (outputFileDescriptor < 0) {
perror("output file failed to open\n");
return(EXIT_FAILURE);
}
close(WRITE);
dup(outputFileDescriptor);
close(outputFileDescriptor);
} /* end of output redirection management */
/* manage pipes if (a) first child process, (b) in-between child process, or (c) final child process */
if (pipes > 0) {
if (index == 0){ /* first child process */
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[WRITE]);
close(r_pipe[READ]);
}
else if (index < pipes) { /* in-between child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[READ]);
close(r_pipe[WRITE]);
}
else { /* final child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
}
}
/* execute command */
execvp(argv[argLocation[index]], &argv[argLocation[index]]);
/* if execvp() fails */
perror("execution of command failed\n");
break;
default: /* parent process manages the pipes for child process(es) */
if (index > 0) {
close(l_pipe[READ]);
close(l_pipe[WRITE]);
}
l_pipe[READ] = r_pipe[READ];
l_pipe[WRITE] = r_pipe[WRITE];
/* parent waits for child process to complete */
wait(&status);
break;
} /* end of switch-statement for command execution */
} /* end of loop for all pipes */
// clear all executed commands
for (int i = 0; i < 20; ++i) {
argv[i] = 0;
}
}
}
void clearArgIndexContainer (int argLocation[]){
// clear argument container
for (int i = 0; i < 20; ++i) {
argLocation[i] = 0;
}
}
This is where I removed the lines making argv[count + 1] = 0; :
/* manage redirection */
while (argv[count] != 0) {
if (strcmp(argv[count], "|") == 0) {
argv[count] = 0;
argLocation[pipes + 1] = count + 1;
++pipes;
}
else if (strcmp(argv[count], "<") == 0) {
in_path = strdup(argv[count + 1]);
argv[count] = 0;
inputRedirectFlag = 1;
}
else if (strcmp(argv[count], ">") == 0) {
out_path = strdup(argv[count + 1]);
argv[count] = 0;
outputRedirectFlag = 1;
}
else {
argLocation[count] = count;
}
This was my major addition to handle to pipes:
/* manage pipes if (a) first child process, (b) in-between child process, or (c) final child process */
if (pipes > 0) {
if (index == 0){ /* first child process */
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[WRITE]);
close(r_pipe[READ]);
}
else if (index < pipes) { /* in-between child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
close(WRITE);
dup(r_pipe[WRITE]);
close(r_pipe[READ]);
close(r_pipe[WRITE]);
}
else { /* final child process */
close(READ);
dup(l_pipe[READ]);
close(l_pipe[READ]);
close(l_pipe[WRITE]);
}
}
I still have more work to do, namely implementing the built-ins, but also putting more of this into separate functions to clean up the code and make it more readable. I was also given some good advice on some expressions that might be better written, and I'll soon tackle that.

background process in shell

I want to implement my own shell. I'm stucked on implementing background process . Actually ,I write some piece of code but I'm not sure if it is work or not .
my code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
#define HISTORY_SIZE 5 /*keep track of 5 most recent commands*/
int cmd_count; /*global to keep track of most recent commands entered*/
char history[HISTORY_SIZE][MAX_LINE]; /* global so it can be accessed in interrupt handler. */
void viewHistory()
{
int i;
if (cmd_count < 1)
printf("No command history to show. \n");
else {
printf("\n\n");
for (i = (cmd_count >= HISTORY_SIZE) ? cmd_count - HISTORY_SIZE:0;
i < cmd_count; i++)
printf("%d: %s\n",i+1,history[i%HISTORY_SIZE]);
}
//printf("SystemsIIShell->");
}
int setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
int temp;
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}else{
inputBuffer[length]='\0';
if(inputBuffer[0]=='r'){
if(inputBuffer[1]=='r'){
if(cmd_count==0){
printf("No recent command can be found in the history. \n");
return 0;
}
strcpy(inputBuffer,history[(cmd_count)% HISTORY_SIZE]);
}else{
temp = atoi(&inputBuffer[1]);
if(temp < 1 || temp > cmd_count || temp <= cmd_count -HISTORY_SIZE){
printf("Command number cannot be found. \n");
return 0;
}
strcpy(inputBuffer,history[(temp-1)%HISTORY_SIZE]);
}
length = strlen(inputBuffer);
}
cmd_count++;
strcpy(history[(cmd_count-1)%HISTORY_SIZE], inputBuffer);
for (i = 0; i < length; i++) {
if (inputBuffer[i] == '&') {
inputBuffer[i] = '\0';
*background = 1;
--length;
break;
}
}
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("SystemsIIShell->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
pid_t child; /* process id for child */
int status; /* status for execvp */
child = fork(); /* create a child process*/
if(child < 0){ /* if the child process didn't return 0, the fork is failed */
printf("Fork failed! \n");
}else if(child==0){ /* child process */
if(inputBuffer[0]=='history' || inputBuffer[0] =='h'){
viewHistory();
return 0;
}
status = execvp(args[0],args);
if(status !=0){
printf("%s: command not found. \n", args[0]);
}
}else{ /* parent process */
if(background == 0)
waitpid(child,&background,0);
}
/* the steps are:
(1) fork a child process using fork()
(2) the child process will invoke execvp()
(3) if background == 0, the parent will wait,
otherwise returns to the setup() function. */
}return 0;
}
I don't add whole code but the other things are true. I call execv and it works. When I write on the console :
output terminal :
$ gedit ------->it works correctly because it is a foreground
$ gedit & -----> it opens a gedit file which name is "&"
$ firefox ---> it works correctly
$ firefox & ---> it opens a firefox window which url is www.&.com
How can fix it ?any suggestions ?
editing part : https://github.com/iqbalhasnan/CSE2431-System-II/blob/master/lab2/lab2.c --> I use this code as a reference
you're not implementing background processes, you're trying to start a background process using the syntax of the already-implemented-shell on your computer (but honestly, it's pretty hard to tell what's going on with that indentation. That's really bad. Can you make it readable, please?). That '&' character is recognised by your shell, not by execvp. Have a look at this similar looking question which was the first hit in a google search for your problem.

Process started by execvp() exits with some of commands

I use this code to run some of shell commands, but it exits after ls command.:
where is my wrong?
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#define MAX_LINE 80 /* The maximum length command */
void setup(char inputBuffer[], char *args[],int *background)
{
int length, i, start, ct;
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO,inputBuffer,MAX_LINE);
start = -1;
if (length == 0)
exit(0); /* ^d was entered, end of user command stream */
if ( (length < 0) && (errno != EINTR) ) {
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
printf(">>%s<<",inputBuffer);
for (i=0;i<length;i++){ /* examine every character in the inputBuffer */
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
default : /* some other character */
if (start == -1)
start = i;
if (inputBuffer[i] == '&'){
*background = 1;
inputBuffer[i-1] = '\0';
}
} /* end of switch */
} /* end of for */
args[ct] = NULL; /* just in case the input line was > 80 */
for (i = 0; i <= ct; i++)
printf("args %d = %s\n",i,args[i]);
} /* end of setup routine */
int main(void)
{
char inputBuffer[MAX_LINE]; /*buffer to hold command entered */
int background; /* equals 1 if a command is followed by '&' */
char *args[MAX_LINE/2 + 1]; /*command line arguments */
int should_run = 1; /* flag to determine when to exit program */
while (should_run) {
//background=0;
printf("Msh>");
fflush(stdout);
setup(inputBuffer, args, &background);
execvp(args[0], args);
}
return 0;
}
As Kerrek SB already said, execvp does not return.
To add a little more info: the execv-family of functions replaces your process (running program) with another. This, in cooperation with fork is what happens inside a system() call.
To put it more bluntly:
If you want to run a system command from your C program, and carry on based on "return" value, you should use the system() call. See example.
If you want to spawn a child process, which should run another executable, you should fork, and inside the child process use execv. See the following example.
Remember that execvp does not return.

Using pipe system call in a mini shell

Although my program works correctly in all cases, it doesn't use a pipe to connect the output of the first of two commands to the second when they're separated by a pipe symbol. I wrote the output of the first command to a file, then redirected the standard input of the second command to the file when the process to run that command was run. I need to use a pipe system call to create the pipe and obtain the file descriptors
for the pipe, and then run the two processes at the same time. It is a homework question and I have done 99% of the work but somehow am not able to get the pipe system call working... what I've been trying is that for an input like: Command 1 | Command 2
inside the child process for command 2 I close FD[0] then dup FD[1] and for command 1 close FD[1] then dup FD[1] and close FD[0].
I am hell confused with the file descriptors when using pipe.... I have to use a pipe
Any sort of help is appreciated. Execute function is where I am forking the processes.
Here's my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <regex.h>
/* Global Variables */
extern char **environ; /* Environmental Variables */
char *pathList[10]; /* List of paths from the $PATH */
int pathCount; /* Count of the # of paths in $PATH */
char *pathSet; /* Variable through which $PATH is retrieved */
int hasPipe = 0;
int cmdNo = 0;
/* This function takes the 'finalPath', the full path to executable,argList[],the
full command-line input arguments and argCount, the number of arguments from
command-line as input. It the creates a child process, in turn invokes the
execve() that finally executes the executable in 'finalPath' with the arguments
in 'argText' all stored into the args[] appropriately. Child process also handles
input and output file re-direction.
*/
void execute(char *finalPath, char *argList[], int argCount)
{
int k,fd,ofound,pos,i; /* flags and temporary variables */
pid_t pid; /* process ID */
int status, which;
char msg[100];
char *args[4]; /* argument list for execve() */
int spCase = 0;
ofound = 0;
pos=0;
pid = fork(); /* Creating a new process using fork() */
if (pid == -1) /* Checking for errors in process creation */
{
write(1,"Fork failed.\n",12);
exit(1);
}
/**************************
Checking for parent process
***************************/
if (pid != 0)
{
which = wait(&status);
if (which == -1)
{
write(1,"Wait failed.\n",12);
exit(1);
}
if (status & 0xff)
{ /* Case of abnormal termination */
sprintf(msg,"ERROR: <dShell> # process %d terminated abnormally for reason %d\n",which, status & 0xff);
write(1,msg,strlen(msg));
}
else
{ /* Case of normal termination */
sprintf(msg,"process %d terminated normally with status %d\n",which, (status >> 8) & 0xff);
write(1,msg,strlen(msg));
}
}
/*************************
Checking for child process
**************************/
if (pid == 0)
{
char argText[50];
argText[0] = '\0';
int std_fd;
if(cmdNo==0 && hasPipe)
{
close(1);
std_fd = open("temp.out", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
dup(std_fd);
}
else if(cmdNo==1 && hasPipe)
{
close(0);
std_fd = open("temp.out", O_RDONLY);
dup(std_fd);
}
/* Finding the first re-direction operator */
for( i = 0; i < argCount ; ++i)
{
if( ofound != 1 && ofound != 2)
{
if( strcmp(argList[i],"<") == 0 )
{
fd = open(argList[i+1],O_RDONLY);
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
ofound = 1;
strcpy(argText,"\0");
close(0);
dup(fd);
close(fd);
}
else if(strcmp(argList[i],">") == 0)
{
fd = open(argList[i+1],O_CREAT | O_WRONLY, 0777);
pos = i;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If input re-direction operator is found check for an output re-direction along with it */
if(ofound == 1)
{
for( k = 0; k < argCount && ofound != 2; ++k)
{
if( strcmp(argList[k],">") == 0 )
{
fd = open(argList[k+1],O_CREAT | O_WRONLY , 0777);
spCase = 1;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[k+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If the re-direction operators are not found */
if( ofound == 0 )
{
for(i = 1; i < argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText, " ");
}
spCase = 2;
}
/* Case when both arguments and output re-direction operators are found */
if (spCase == 0)
{
if(pos == 0)
{
for( i = 3; i<argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
if(pos == argCount - 2)
{
for( i = 1; i<argCount - 2; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
}
argText[strlen(argText)-1] = '\0'; /*because I added an extra space so trimming that*/
/* Running the execve */
args[0] = finalPath;
if(strlen(argText) == 0) /* checking if argText is populated */
{
args[1] = NULL;
}
else
{
args[1] = argText;
args[2] = NULL;
}
/* Execute command,if it returns that means it failed and need to display error and exit */
execve(args[0], args, environ);
sprintf(msg, "ERROR! execve() failed");
write(1, msg, strlen(msg));
}
}
/*******************************************************************************
This function checks if the path is accessible and continues to execute the
command. If the path does not exist of is not accessible, variable 'retFlag'
is used to return 0 to the calling function.
********************************************************************************/
int checkPath(char *exepath, char *argList[], int argCount, int flag)
{
char *finalPath;
int retFlag = flag;
if(access(exepath,X_OK) == 0)
{
finalPath = exepath;
retFlag = 1;
execute(finalPath,argList,argCount);
return retFlag;
}
else
return retFlag;
}
/**********************************************************************************
This function checks if the first argument is a path and if so calls checkPath().
Else it gets the paths set to the $PATH variable, tokenizes it, pads it with the
first token of input command and calls checkPath(). If the correct path is established,
the variable 'found' is used to kick out of the for loop.
************************************************************************************/
void setPath(char *argList[], int argCount)
{
char *exepath;
char com[50];
char emsg[80];
char *command;
int i,found = 0;
/* Seperating the command if redirection is used */
if( strcmp(argList[0],"<") == 0 || strcmp(argList[0],">") == 0 )
{
command = argList[2];
}
else
command = argList[0];
/* In case of no redirection, storing the commands and arguments into a array */
if(strcmp(command,"#") == 0) /* Checking for comment statements */
{
write(1,"ERROR: No command(s) found. Only comment present/n",48);
}
else
{
if(strstr(command,"/")) /* Checking if the entire path is given as a part of the command */
{
exepath = command;
found = checkPath(exepath,argList,argCount,0);
}
else /* building the path and storing it in 'com' */
{
for(i = 0; i< pathCount && found != 1; i++)
{
sprintf(com,"%s%s%s",pathList[i],"/",command);
exepath = com;
found = checkPath(exepath,argList,argCount,0);
}
}
if(found == 0)
{
sprintf(emsg,"%s%s",command,":COMMAND DOES NOT EXIST");
write(1,emsg,sizeof(emsg));
write(1,"\n",1);
}
}
}
/* Tokenizes commands into words */
void tokens(char *cmdStr)
{
char cmd[100];
strcpy(cmd,cmdStr);
char *result;
char delims[] = " , ";
char *argList[20];
int argCount = 0;
/*Tokenize the individual command into strings */
result = strtok(cmd,delims);
while( result != NULL )
{
argList[argCount] = result;
result = strtok( NULL, delims );
++argCount;
}
setPath(argList,argCount);
}
/* Tokenizes multiple commands into single commands */
void tokenize(char *inputStr)
{
int i,cmdCount = 0;
char *cmdResult;
char *cmdStr[100];
char delimiters[] = "|";
cmdResult = strtok(inputStr, delimiters);
while(cmdResult != NULL)
{
cmdStr[cmdCount]=cmdResult;
cmdResult = strtok(NULL, delimiters);
cmdCount++;
}
if( cmdCount > 1 )
hasPipe = 1;
else
hasPipe = 0;
for( i=0; i<cmdCount ; i++)
{
cmdNo = i%cmdCount;
tokens(cmdStr[i]);
}
}
int main(int argc, char *argv[])
{
char prompt[8]; /* String that stores the personalized prompt */
char *path; /* Temporary variable used for tokenization*/
char ch; /* Temporary variable used in read() */
int chCount; /* # of characters read from the prompt */
int entry; /* return variable of read() */
int flag; /* Flag to go read the next command when newline is found */
regex_t reIgnore;
char pattern[20]="^\\s*$|^#.*";
/* Tokenizing the paths asociated with the $PATH and storing them in a array declared globally */
pathCount = 0;
pathSet = getenv("PATH");
if ( !pathSet)
{
write(1, "ERROR: PATH environment does not exist.\n", 40);
exit(1);
}
path = strtok(pathSet,":");
while(path != NULL)
{
pathList[pathCount] = path;
path = strtok(NULL,":");
++pathCount;
}
/* Checks for blanks and tabs in Step 2 */
if ( regcomp(&reIgnore, pattern, REG_EXTENDED) )
{
write(1, "Error. \n",9);
exit(2);
}
sprintf(prompt,"<dShell> # "); /* Storing the personalized shell prompt into 'prompt' */
/* Reading the input from command line and passing it to tokenize() */
while(1)
{
char inputStr[100]; /* String into which inputs are read into */
chCount = 0;
flag = 0;
hasPipe = 1;
write(1,prompt,strlen(prompt)); /* Printing out the personalized shell prompt */
/* This will read a character 1 by 1 until it reaches the end of file */
entry = read(0,&ch,1);
if(!entry)
exit(0);
/* Reading the input and storing it in inputStr as long as newline is not encountered */
while( entry != 0 && flag == 0 )
{
/* A newline has been found so a new command will need to be executed */
/* The inputStr till this point is sent to tokenize() */
if( ch == '\n' )
{
inputStr[chCount] = '\0';
flag = 1;
if(chCount > 0) {
if(strcmp(inputStr,"exit") == 0)
exit(3);
else
tokenize(inputStr);
}
}
inputStr[chCount] = ch;
chCount++;
if(flag == 0)
entry = read( 0, &ch, 1 );
}
}
}
See the man page for pipe(2). It has this example:
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
assert(argc == 2);
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}

Resources