Okay so I have this assignment that requires me to build a parallel shell tool called PELL. It provides users of pell with the ability to execute commands, execute multistep processes connected by pipes, and execute independent processes.
Input: Lines of text containing conc or pipe commands:
conc cmd1 args1 , cmd2 args2 , ...
conc causes PELL to execute the commands concurrently (i.e., parallel).
There is no communication between the commands, these simply happen
concurrently (in parallel).
pipe cmd1 args1 , cmd2 args2
pipe causes PELL to create a pipe and fork a child for each cmdi.
The pipe is the output for step 1 and the input for step 2. Also,
cmd1 can have stdin redirected from a file. cmd2 can have stdout
redirected to a file.
Results: For each command read from stdin:
Prints the tokens
Prints the command information
Depending on the command:
concCmd:
- prints the parent PID, child PID, and the command
Example:
33009 33011: ls -l /bin > lsOne.txt
33009 33012: ls -l /usr/bin > lsTwo.txt
33009 33013: ls -l /etc > lsThree.txt
pipeCmd, for each child:
- prints step, parent PID, child Pid, and the command
Example:
1 33043 33045: ls -l Data
2 33043 33046: sort -k5 -n
Here is the Header file for the code:
#define TRUE 1
#define FALSE 0
#define MAX_COMMANDS 5
#define MAX_TOKEN_SZ 100
#define MAX_PATH 500
#define MAX_TOKENS 30
#define MAX_ARGS 6
// Cmd - represents a command, its list of arguments and
// subscripts into the token array for redirection of stdin and/or stdout
typedef struct Cmd
{
int iBeginIdx; // Beginning subscript in tokenM for first arg
// (this will be 0 when there aren't any arguments)
int iEndIdx; // Ending subscript in tokenM for last arg. If
// there is redirection, this subscript would be
// before that. (this is -1 for no arguments)
char szCmdNm[MAX_TOKEN_SZ+1]; // command name (e.g., ls)
int iStdinRedirectIdx; // Subscript in tokenM for the stdin redirect; 0 for no redirect
int iStdoutRedirectIdx; // Subscript in tokenM for the stdout redirect; 0 for no redirect
}Cmd;
typedef char Token [MAX_TOKEN_SZ+1];
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
int pipeCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
And the included Driver File:
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include "cs3423p8.h"
#define MAX_BUFFER_SZ 500
void processCommands(FILE *pfileCommand);
void prtCmdList(Cmd cmdM[], int iCmdCnt);
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt);
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim);
int main(int argc, char *argv[])
{
// We don't expect a command argument
if (argc > 1)
errExit("Usage: pell < inputFile");
processCommands(stdin);
return 0;
}
/******************** processCommands **************************************
void processCommands(FILE *pfileCommand)
Purpose:
Reads the Command file to process commands. There are several types of
records (see the program header for more information).
Parameters:
I FILE *pfileCommand command stream input
Notes:
This calls:
split
getCmdList
concCmd
pipeCmd
**************************************************************************/
void processCommands(FILE *pfileCommand)
{
// variables for command processing
char szInputBuffer[MAX_BUFFER_SZ+1]; // input buffer for a single text line
// variables for tokenizing
int iTokenCnt;
Token tokenM[MAX_TOKENS];
// variables to understand the commands in the input text
int iCmdCnt;
Cmd cmdM[MAX_COMMANDS];
// misc
int rc;
// get command data until EOF
while (fgets(szInputBuffer, MAX_BUFFER_SZ, pfileCommand) != NULL)
{
// if the line is just a line feed, ignore it
if (szInputBuffer[0] == '\n')
continue;
// see if the command is a comment
if (szInputBuffer[0]== '*')
{
printf("%s", szInputBuffer);
continue; // it was just a comment
}
printf(">>> %s", szInputBuffer);
// split the line based on spaces
iTokenCnt = split(tokenM, MAX_TOKENS, szInputBuffer, ' ');
// print the tokens
int i;
printf("%3s %s\n", "Seq", "Token");
for (i = 0; i < iTokenCnt; i++)
printf(" %3d '%s'\n", i, tokenM[i]);
if (iTokenCnt <= 0)
errExit("Command was blank");
// get the command list for this command
memset(cmdM, 0, sizeof(cmdM));
iCmdCnt = getCmdList(cmdM, tokenM, iTokenCnt);
prtCmdList(cmdM, iCmdCnt);
// process the particular command
if (strcmp(tokenM[0], "conc")==0)
{ // conc command
rc = concCmd(cmdM, iCmdCnt, tokenM, iTokenCnt);
if (rc != 0)
printf("*** concCmd returned %d\n", rc);
}
else if (strcmp(tokenM[0], "pipe")==0)
{ // pipe command
rc = pipeCmd(cmdM, iCmdCnt, tokenM, iTokenCnt);
if (rc != 0)
printf("*** pipeCmd returned %d\n", rc);
}
else
errExit("Invalid command: '%s'", tokenM[0]);
}
printf("\n"); // good place for a breakpoint
}
/******************** split **************************************
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim)
Purpose:
Tokenizes the input text by splitting it on the specified
delimiter.
Parameters:
O Token tokenM[] array of tokens for the input test
I int iMaxToken the maximum number of characters in a token (not
including zero byte
I char szInput[] input text to be tokenized
I char cDelim delimiter character (e.g., ' ')
Returns:
Count of number of entries in tokenM.
Notes:
- Linefeed and '\0' are also used as delimiters for the last token
- If we encounter two or more adjacnet delimiters, we ignore them.
- If a token is larger than the specified max token size, we
truncate the string.
**************************************************************************/
int split(Token tokenM[], int iMaxToken, char szInput[], char cDelim)
{
int i; // used to traverse the input text string
int iTokenBeg = 0; // subscript where the token begins
int iTokenEnd = -1; // subscript to the delimiter following the token
int iTokenIdx = 0; // where to place next token in tokenM
int iTokenSize; // size (in bytes) of the token without zero byte
// We will actually include touching the '\0' or '\n' since that will
// mark the end of the last token.
int iLen = strlen(szInput);
for (i = 0; i <= iLen; i +=1)
{
// Is it end of line, line feed or the delim?
if (szInput[i]== '\0' || szInput[i] == '\n' || szInput[i] == cDelim)
{
if (iTokenBeg == i)
{ // only a delimiter, nothing in token so ignore it
iTokenBeg = i+1;
continue;
}
// see if the token is too long
if ( (i-iTokenBeg) > iMaxToken)
iTokenSize = iMaxToken; // truncate it
else
iTokenSize = i-iTokenBeg;
memcpy(&tokenM[iTokenIdx][0], &szInput[iTokenBeg], iTokenSize);
tokenM[iTokenIdx][iTokenSize] = '\0'; // terminate it
iTokenIdx++;
iTokenBeg = i+1;
}
}
return iTokenIdx;
}
/******************** gettCmdList **************************************
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt)
Purpose:
Parse through the token array to determine the commands. It
saves the beginning and ending subscripts for each command's arguments.
It also determines whether the command has a redirected stdin
and/or stdout.
Parameters:
O Cmd cmdM[] array of commands
I Token tokenM[] array of tokens for the input test
I int iTokenCnt number of entries in tokenM
Returns:
Count of number of entries in cmdM.
Notes:
- commands are separated by commas
**************************************************************************/
int getCmdList(Cmd cmdM[], Token tokenM[], int iTokenCnt)
{
int i; // subscript to current token
char cChar; // current character in input text
int iCmdCnt = 0; // count of number of entries in cmdM
// Iterate through the array of tokens. We actually
// go to one item beyond the end so that we can process
// the last token normally. (We pretend there is an
// ending token after the last token.)
for (i = 1; i <= iTokenCnt; i += 1)
{
Cmd *pCmd = &(cmdM[iCmdCnt]);
if (i == iTokenCnt)
cChar = ','; // pretend an ending delim
else
cChar = tokenM[i][0];
switch(cChar)
{
case ',': // delimiter between commands
if (pCmd->iBeginIdx == 0)
errExit("no command, cmd arg: %d\n", i);
// If we haven't yet marked the end of the command's
// arguments, assume it is right before the comma.
// Note that redirection also set the iEndIdx.
if (pCmd->iEndIdx == 0)
pCmd->iEndIdx = i-1;
// Check for no command arguments
if (pCmd->iBeginIdx > pCmd->iEndIdx)
{ // no args
pCmd->iBeginIdx = 0;
pCmd->iEndIdx = -1;
}
iCmdCnt += 1;
break;
case '<': // stdin redirection
if (i+1 >= iTokenCnt) // need another arg
errExit("redirect requires additional arg, cmd arg: %d\n", i);
pCmd->iStdinRedirectIdx = i+1;
// If we haven't yet marked the end of the command's
// arguments, assume it is right before the <.
if (pCmd->iEndIdx == 0)
pCmd->iEndIdx = i-1;
break;
case '>':
if (i+1 >= iTokenCnt) // need another arg
errExit("redirect requires additional arg, cmd arg: %d\n", i);
pCmd->iStdoutRedirectIdx = i+1;
// If we haven't yet marked the end of the command's
// arguments, assume it is right before the >.
if (pCmd->iEndIdx == 0)
pCmd->iEndIdx = i-1;
break;
default:
// check if at the beginning of the command
if (pCmd->iBeginIdx == 0)
{ // not comma, <, > if iBeginIdx is 0, we need to record
// where the arguments might begin
strcpy(pCmd->szCmdNm, tokenM[i]);
pCmd->iBeginIdx = i+1;
}
}
}
return iCmdCnt;
}
/******************** prtCmdList **************************************
void prtCmdList(Cmd cmdM[], int iCmdCnt)
Purpose:
Prints information for each command in the list of commands.
Parameters:
I Cmd cmdM[] array of commands
I int iCmdCnt count of number of entries in cmdM.
**************************************************************************/
void prtCmdList(Cmd cmdM[], int iCmdCnt)
{
int i;
printf("%-20s %5s %-5s %-5s %-6s\n"
, "Command", "Begin", "End", "stdin", "stdout");
for (i = 0; i < iCmdCnt; i +=1)
{
printf("%-20s %5d %5d %5d %6d\n"
, cmdM[i].szCmdNm, cmdM[i].iBeginIdx, cmdM[i].iEndIdx
, cmdM[i].iStdinRedirectIdx, cmdM[i].iStdoutRedirectIdx);
}
}
Could use some help constructing the pseudocode for the program. I have an idea what the main function will look like, but need help setting up the code for the concCMD Command and pipeCMD Command. I'm not very proficient with c. Any help would be greatly appreciated.
Here is the code I have written so far and I'm looking for way to make it work, and how to write the pipe command. I'm getting an ambiguous output redirection error.
//
// cs3423p8.c
//
//
// Created by Cesar Benavides on 12/4/17.
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include "cs3423p8.h"
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
//int pipeCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt);
//prints parents processID Child's ProcessID, the Command and Command arguments
//fork each of the children, Max commands is 5. If any of the commands is redirect or output
//do redirection after forking but before execing
//redirect stdout for each child, and execvp to the particular command for child
int concCmd (Cmd cmdM[], int iCmdCnt, Token tokenM[], int iTokenCnt)
{
int j;
int i;
int count = 0;
long lForkPid;
long lWaitPid;
int iExitStatus = 0;
char *execArgv[25];
int fdin, fdout;
//make children
lForkPid = fork();
//children made
for(i = 0; i < iCmdCnt; i++)
{
//count =0;
switch(lForkPid)
{
case -1:
errExit("fork failed: %s", strerror(errno));
break;
case 0://child
execArgv[0] = cmdM[i].szCmdNm;
//count = 0;
for(j = cmdM[i].iBeginIdx; j <= cmdM[i].iEndIdx; j++)
{
execArgv[count + 1] = tokenM[j];
count++;
}
execArgv[count] = NULL;
if (cmdM[i].iStdinRedirectIdx !=0)
{
printf("stdin redirect \n \n ");
fdin = open(tokenM[cmdM[i].iStdinRedirectIdx], O_RDONLY);
dup2(fdin, STDIN_FILENO);
close(fdin);
fprintf(stderr,"sTDIN REDIRECT Child Process: PID=%ld, PPID=%ld\n" , (long) getpid(), (long) getppid());
// execvp(cmdM[i].szCmdNm, execArgv);
//errExit("Child process failed to exec: %s", strerror(errno));
}
if (cmdM[i].iStdoutRedirectIdx !=0)
{
printf("stdout redirect \n\n");
fdout = open(tokenM[cmdM[i].iStdoutRedirectIdx], O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
dup2(fdout, STDOUT_FILENO);
close(fdout);
fprintf(stderr, "sTDOUT REDIRECT Child Process: PID=%ld, PPID=%ld\n", (long) getpid(), (long) getppid());
}
execvp(cmdM[i].szCmdNm, execArgv);
//errExit("Child process failed to exec: %s", strerror(errno));
// exit(0);
default://parent
lWaitPid = wait(&iExitStatus);
// if(lWaitPid == -1)
// errExit("wait error: %s", strerror(errno));
}
}
return 0;
}