C 函数从另一个函数接收文件描述符,但在输入文件描述符的dup2上失败
我正在创建一个自定义shell,并尝试让多个管道工作,以及在管道之前进行IO重定向。例如:C 函数从另一个函数接收文件描述符,但在输入文件描述符的dup2上失败,c,fork,file-descriptor,execvp,C,Fork,File Descriptor,Execvp,我正在创建一个自定义shell,并尝试让多个管道工作,以及在管道之前进行IO重定向。例如:prog
prog
。我的方法是执行管道左侧的命令(prog
),并将管道的写入端传递给另一个派生子函数的函数,在写入端文件描述符上使用dup2
,并适当地使用execvp
。它将对管道的右侧执行相同的操作,但它将以管道的读取端作为输入。请记住,我用来执行VP的函数能够自己处理io重定向
我已经调试并验证了我为管道创建的文件描述符列表是正确的,并且也适当地传递给了子级。但是,当被调用函数的分叉子函数尝试dup2(inputFdForStdin,STDIN\u FILENO)
和STDOUT
的相应dup2
函数时,我得到一个错误,即文件描述符不好。如何正确地将管道读写端的文件描述符传递给另一个派生子函数的函数
runPipedCommands是一个父函数,它将管道的左右命令“块”分隔开来,并使用适当的文件描述符将每一侧传递给runSimple
。我不认为我用于execvp
的命令参数结构是相关的,因此我不会共享该代码
// this function only runs iff there is a pipe within a command string
// input: a list of command structs, each of which contain a list of
// strings of each command and its arguments (for execvp)
int runPipedCommands(char *args)
{
// this code just counts the number of pipes in commands
int numPipes = 0;
int j = 0;
int status;
int i = 0;
pid_t pid;
char *cmdToken;
char *cmdSavePter;
int childStdin = STDIN_FILENO;
int childStdout = STDOUT_FILENO;
int cmdsRun = 0;
struct command *commands;
while (args[i])
{
if (args[i++] == '|')
numPipes++;
}
printf("number of pipes: %d\n\n", numPipes);
int pipefds[2 * numPipes];
// create the pipes
for (i = 0; i < (numPipes); i++)
{
if (pipe(pipefds + i * 2) < 0)
{
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
pid = fork();
if (pid == 0)
{
cmdToken = strtok_r(args, "|", &cmdSavePter);
while (cmdToken)
{
fprintf(stderr, "\ncmdToken: %s\n", cmdToken);
fprintf(stderr, "pipefds: %d, %d\n\n", pipefds[0], pipefds[1]);
commands = parseW(cmdToken);
cmdsRun += 1;
//if not last command
if (cmdsRun <= numPipes)
{
childStdout = pipefds[j + 1];
}
//if not first command and not max pipes reached
if (j != 0)
{
childStdin = pipefds[j - 2];
}
// close dup2ed fds
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
runSimple(commands, childStdin, childStdout);
cmdToken = strtok_r(NULL, "|", &cmdSavePter);
j += 2;
}
}
else if (pid < 0)
{
perror("error");
exit(EXIT_FAILURE);
}
/**Parent closes the pipes and wait for child*/
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
for (i = 0; i < numPipes + 1; i++)
wait(&status);
}
void runSimple(struct command *args, int fdin, int fdout)
{
fprintf(stderr, "in rumSimple\n");
pid_t pid;
int i = 0;
int fd; // store file descriptors
pid = fork(); // store child pid
if (pid < 0) // error
{
printf("An error occured while forking child for runSimple.\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
fprintf(stderr, "runSimple forked child: fdin: %d, fdout: %d\n", fdin, fdout);
// setting fds below are for the scenario
// where there is redirection before a pipe
// set fd to input
if (dup2(fdin, STDIN_FILENO) < 0)
{
perror("runSimple child dup2 stdin:");
exit(EXIT_FAILURE);
}
// set fd to output
if (dup2(fdout, STDOUT_FILENO) < 0)
{
perror("runSimple child dup2 stdout");
exit(EXIT_FAILURE);
}
i = 0;
// loop and deal with special operators
while (args[i].cmd)
{
// printf("command: %s\n", args[i].cmd[0]);
// deal with &>
if (args[i].cmd && !strcmp(args[i].cmd[0], "&>"))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDOUT_FILENO) < 0)
{
perror("runSimple: failed dup2 for stdout on &>");
exit(EXIT_FAILURE);
}
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
{
perror("runSimple: failed dup2 for stderr on &>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with 2>
if (args[i].cmd && !strcmp(args[i].cmd[0], "2>"))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("runSimple: error opening file for 2>");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDERR_FILENO) < 0)
{
perror("runSimple: failed dup2 on stderr redirecting on 2>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with > and 1>
if (args[i].cmd && (!strcmp(args[i].cmd[0], ">") || !strcmp(args[i].cmd[0], "1>")))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("runSimple: error opening argument for >");
exit(EXIT_FAILURE);
}
// make child's stdout into file
if (dup2(fd, STDOUT_FILENO) < 0)
{
perror("runSimple: failure dup2 on redirecting STDOUT to fd on > or 1>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with <, making sure to check if args[i] exists first
if (args[i].cmd && !strcmp(args[i].cmd[0], "<"))
{
fd = open(args[i + 1].cmd[0], O_RDONLY);
if (fd < 0)
{
perror("runSimple: error opening argument for <");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDIN_FILENO) < 0)
{
perror("runSimple: error dup2 on redirecting stdin to fd on <");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
i++;
}
// exec input with appropriate fd
execvp(args[0].cmd[0], args[0].cmd);
perror("runSimple:");
exit(EXIT_FAILURE);
}
int status;
// parent should wait for child to finish if & not specified
waitpid(pid, &status, 0);
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
// store jump mark
sigjmp_buf mark;
// command struct to store
// individual cmds and their
// arguments
struct command
{
char **cmd; // list of arguments
int numArgs; // number of total arguments in line
};
// on sigint kill stdin buffer and show prompt
void sigintHandler(int sig_num)
{
// Reset handler to catch SIGINT next time.
signal(SIGINT, sigintHandler);
// jump back to main to avoid fgets
siglongjmp(mark, 1);
}
// wait for and read from stdin
char *get_args()
{
char *args = calloc(sizeof(char), 2048);
char *result = fgets(args, 2047, stdin);
// terminate on end of file (ctrl-d)
if (!result)
{
printf("exit\n");
exit(EXIT_FAILURE);
}
char *pter = strrchr(args, '\n');
if (pter)
*pter = 0;
return args;
}
// parse whiteSpace in command, return array of command structs
struct command *parseW(char *cmd)
{
char *tempCmd = strndup(cmd, strlen(cmd)); // duplicate input command to keep it unchanged
char *savePter; // savePter for strtok_r
int i = 0, size = 0, numCmds = 0; // i fo
char *token = strtok_r(tempCmd, " \t", &savePter); // get the first whitespace token from cmd
while (token) // loop through and count number of actual args
{
// if not operator, add strlen
if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
{
}
else // is special operator
{
numCmds += 2;
}
i++;
token = strtok_r(NULL, " \t", &savePter);
}
numCmds++; // increment bc of initial cmd
// initializing commands list
struct command *commands = calloc(numCmds + 1, sizeof(struct command));
strcpy(tempCmd, cmd);
savePter = NULL;
i = 0;
token = strtok_r(tempCmd, " \t", &savePter);
// loop through and allocate space for each cmd and its arguments
while (token)
{
// error check invalid pipes
if (!strcmp(token, "|") && i == 0 && size == 0)
{
printf("parseW: invalid pipe parsed\n");
return NULL;
}
// if not operator, add strlen
if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
{
size += strlen(token);
}
else // is special operator
{
if (size == 0)
{
printf("parseW: invalid syntax, unexpected operator or token '%s'\n", token);
return NULL;
}
else
{
// add all prev commands size
commands[i].numArgs = numCmds;
commands[i++].cmd = calloc(size + 1, sizeof(char *));
// add special operator
commands[i].numArgs = numCmds;
commands[i++].cmd = calloc(strlen(token) + 1, sizeof(char *));
size = 0;
}
}
token = strtok_r(NULL, " \t", &savePter);
}
if (!size) // if last token parsed was a pipe, it's invalid
{
printf("parseW: invalid pipe parsed\n");
return NULL;
}
commands[i].numArgs = numCmds;
commands[i].cmd = calloc(size + 1, sizeof(char *)); // add final command allocation
strcpy(tempCmd, cmd);
savePter = NULL;
i = 0; // cmd index
int z = 0; // cmd argument index
token = strtok_r(tempCmd, " \t", &savePter);
// loop through and set each cmd and their args
while (token)
{
// if not operator, add token
if (strcmp(token, "|") && strcmp(token, ">") && strcmp(token, "<") &&
strcmp(token, "1>") && strcmp(token, "2>") && strcmp(token, "&>"))
{
commands[i].cmd[z++] = token;
}
else // is special operator, reset arg index, inc cmd index
{
z = 0;
commands[++i].cmd[0] = token;
i++;
}
token = strtok_r(NULL, " \t", &savePter);
}
return commands;
}
// runs non pipe/redirection commands
void runSimple(struct command *args, int fdin, int fdout)
{
fprintf(stderr, "in rumSimple\n");
pid_t pid;
int i = 0;
int fd; // store file descriptors
pid = fork(); // store child pid
if (pid < 0) // error
{
printf("An error occured while forking child for runSimple.\n");
exit(EXIT_FAILURE);
}
else if (pid == 0) // child
{
fprintf(stderr, "runSimple forked child: fdin: %d, fdout: %d\n", fdin, fdout);
// setting fds below are for the scenario
// where there is redirection before a pipe
// set fd to input
if (dup2(fdin, STDIN_FILENO) < 0)
{
perror("runSimple child dup2 stdin:");
exit(EXIT_FAILURE);
}
// set fd to output
if (dup2(fdout, STDOUT_FILENO) < 0)
{
perror("runSimple child dup2 stdout");
exit(EXIT_FAILURE);
}
i = 0;
// loop and deal with special operators
while (args[i].cmd)
{
// printf("command: %s\n", args[i].cmd[0]);
// deal with &>
if (args[i].cmd && !strcmp(args[i].cmd[0], "&>"))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("open");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDOUT_FILENO) < 0)
{
perror("runSimple: failed dup2 for stdout on &>");
exit(EXIT_FAILURE);
}
if (dup2(STDOUT_FILENO, STDERR_FILENO) < 0)
{
perror("runSimple: failed dup2 for stderr on &>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with 2>
if (args[i].cmd && !strcmp(args[i].cmd[0], "2>"))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("runSimple: error opening file for 2>");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDERR_FILENO) < 0)
{
perror("runSimple: failed dup2 on stderr redirecting on 2>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with > and 1>
if (args[i].cmd && (!strcmp(args[i].cmd[0], ">") || !strcmp(args[i].cmd[0], "1>")))
{
fd = creat(args[i + 1].cmd[0], 0644);
if (fd < 0)
{
perror("runSimple: error opening argument for >");
exit(EXIT_FAILURE);
}
// make child's stdout into file
if (dup2(fd, STDOUT_FILENO) < 0)
{
perror("runSimple: failure dup2 on redirecting STDOUT to fd on > or 1>");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
// deal with <, making sure to check if args[i] exists first
if (args[i].cmd && !strcmp(args[i].cmd[0], "<"))
{
fd = open(args[i + 1].cmd[0], O_RDONLY);
if (fd < 0)
{
perror("runSimple: error opening argument for <");
exit(EXIT_FAILURE);
}
if (dup2(fd, STDIN_FILENO) < 0)
{
perror("runSimple: error dup2 on redirecting stdin to fd on <");
exit(EXIT_FAILURE);
}
close(fd);
// args[i] = NULL;
}
i++;
}
// exec input with appropriate fd
execvp(args[0].cmd[0], args[0].cmd);
perror("runSimple:");
exit(EXIT_FAILURE);
}
int status;
// parent should wait for child to finish if & not specified
waitpid(pid, &status, 0);
}
// this function only runs iff there is a pipe within a command string
// input: a list of command structs, each of which contain a list of
// strings of each command and its arguments (for execvp)
int runPipedCommands(char *args)
{
// this code just counds the number of pipes in commands
int numPipes = 0;
int j = 0;
int status;
int i = 0;
pid_t pid;
char *cmdToken;
char *cmdSavePter;
int childStdin = STDIN_FILENO;
int childStdout = STDOUT_FILENO;
int cmdsRun = 0;
struct command *commands;
while (args[i])
{
if (args[i++] == '|')
numPipes++;
}
printf("number of pipes: %d\n\n", numPipes);
int pipefds[2 * numPipes];
// create the pipes
for (i = 0; i < (numPipes); i++)
{
if (pipe(pipefds + i * 2) < 0)
{
perror("couldn't pipe");
exit(EXIT_FAILURE);
}
}
pid = fork();
if (pid == 0)
{
cmdToken = strtok_r(args, "|", &cmdSavePter);
while (cmdToken)
{
fprintf(stderr, "\ncmdToken: %s\n", cmdToken);
fprintf(stderr, "pipefds: %d, %d\n\n", pipefds[0], pipefds[1]);
commands = parseW(cmdToken);
cmdsRun += 1;
//if not last command
if (cmdsRun <= numPipes)
{
childStdout = pipefds[j + 1];
}
//if not first command and not max pipes reached
if (j != 0)
{
childStdin = pipefds[j - 2];
}
// close dup2ed fds
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
runSimple(commands, childStdin, childStdout);
cmdToken = strtok_r(NULL, "|", &cmdSavePter);
j += 2;
}
}
else if (pid < 0)
{
perror("error");
exit(EXIT_FAILURE);
}
/**Parent closes the pipes and wait for children*/
for (i = 0; i < 2 * numPipes; i++)
{
close(pipefds[i]);
}
for (i = 0; i < numPipes + 1; i++)
wait(&status);
}
/*
Main method has an infinite while loop that does:
1) check if input is from terminal, if so go to 2
2) print myshell prompt
3) wait for user stdin by using get_args() function
4) while loop to loop through individual semicolon separated commands
5) for each individual command, get each argument token and parse
whitespace by calling parseW, which returns array of strings
of every single command and their arguments
6) hands control off to runSimple(), which runs the list of commands
appropriately, and handles command operators (>, <, 1>, etc)
Main method also has a signal handler for SIGINT (ctrl+c), in which event
another signal handler for SIGINT is setup, and a jump is made to the before
the while loop so that the shell can print prompt and listen for commands again
*/
int main(int argc, char *argv[])
{
int i = 0, size = 0;
char *semi;
char *semiSavePter;
signal(SIGINT, sigintHandler);
siginterrupt(SIGINT, 1);
sigsetjmp(mark, 1);
while (1)
{
if (isatty(STDIN_FILENO))
{
// print prompt
printf("\n\033[1;32mmyshell\033[0m>");
char *line = get_args();
// parse semis
semi = strtok_r(line, ";", &semiSavePter);
// for every semicolon, parse each cmd
while (semi)
{
// get array of args
struct command *args = parseW(semi);
// if error in parsing, break and reprint prompt
if (!args)
break;
// if pipe runPipedCommands
if (strchr(semi, '|'))
{
int result = runPipedCommands(semi);
}
else
{
runSimple(args, STDIN_FILENO, STDOUT_FILENO);
}
semi = strtok_r(NULL, ";", &semiSavePter);
}
}
}
return 0;
};