I am writing a function to my shell where I need to redirect output to file. For example user write: ls -l >> file and ls -l should be written to file. Almost all things are correct but after first calling my function program stop and I can't write anything else. Below I present my function and I would appreciate any clues to resolve the problem:
void execute2(char *command, char **argv, char **argv2)
{
pid_t pid;
int status;
if ((pid = fork()) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
else if (pid == 0)
{
close(1);
parse(command, argv, argv2);
int output = open(*argv2, O_APPEND | O_WRONLY);
dup2(output,1);
if (strcmp(argv[0], "exit") == 0)
exit(0);
if (execvp(*argv, argv) < 0)
{
printf("*** ERROR ***\n");
exit(1);
}
close(output);
}
else
{
while (wait(&status) != pid);
}
}
command is command getting from user, argv is part of instruction and parameters and argv2 is the output file.
#Jonathan Leffler I checked this and I think it doesn 't resolve problem. I suggest that when I call first time execute2 it works as well as I want but I never finish that proces. Or I don 't undestand something
Related
I wrote 2 programs.
The first one gets 2 strings through argv and prints the program process id and the smallest string (by dictionary order).
int main(int argc,char **argv) {
int cmp;
if (argc != 3){
perror("Wrong arguments");
exit(EXIT_FAILURE);
}
printf("my ID: %d\n",getpid());
cmp = strcmp(argv[1],argv[2]);
if (cmp < 0)
puts(argv[1]);
else if (cmp > 0)
puts(argv[2]);
else puts(argv[1]);
exit(EXIT_SUCCESS);
}
I have compiled the code via terminal with:
gcc -Wall my_cmp.c -o my_cmp
The second program creates a child process and performs execvp() with my_cmp sending "abc" "de".
void do_sun(char **argv);
int main() {
pid_t status;
char *args[] = { "my_cmp", "abc","de", NULL };
status = fork();
if (status < 0){
perror("Cannot fork");
exit(EXIT_FAILURE);
}
if (status == 0){
char *args[] = { "my_cmp", "abc","de", NULL };
do_sun(args);
exit(EXIT_SUCCESS);
}
if (status > 0){
wait(&status);
if (execvp(args[0],args) != 0 )
perror("error");
exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
void do_sun(char **args){
if (execvp(args[0],args) == -1 )
perror("error");
exit(EXIT_SUCCESS);
}
When I run the program I am getting this message:
error: No such file or directory
error: No such file or directory
I have tried to switch directory to the my_cmp file with no success.
On some systems it won't search working directory for executable binary files by default.
You should use ./my_cmp instead of my_cmp to have it work on such systems.
If you use execvp, you are saying that the executable will be found "in the path"; that is, in one of the directories listed in $PATH. Few directories are on this path, and while it is possible to add . to $PATH, that's generally regarded to be asecurity vulnerability.
If you want to specify the path to the executable (in which case you can put it in whatever directory you want), use the non-path execv.
I am learning about how fork() works and I am running into an infinite loop when calling execvp(argh[0], argv);
Here is my program:
int main (int argc, char *argv[])
{
pid_t pid;
pid = fork();
if (pid < 0) { // error occurred
printf("Something went wrong!\n");
exit(-1);
} else if (pid == 0) { // child process
printf("Child process. ");
if(argc == 1 || argc == 0) {
printf("No arguments provided. Terminating child.\n");
exit(0);
} else if(argc == 2) {
printf("One argument provided.\n");
execlp(argv[1], argv[1], NULL);
} else {
printf("More than one argument\n");
execvp(argv[0], argv); // *** It's right here ***
}
} else { // parent process
printf("PARENT started with pid=%d.\n", pid);
int status = 0;
pid_t wait(int *status);
printf("PARENT resumed with status code: %i. Now terminating parent.\n", status);
exit(0);
}
return 0;
}
I'm trying to perform the operation (and arguments) that are passed into the function, with this code execvp(argv[0], argv), but I'm getting an infinite loop when I do ./arguments ls -l. How should I be calling execvp? Or should I call another method?
Thanks for any help
---Update---
I made the change suggested in an answer which was to call execvp(argv[1], argv); (change the arguv[0] to arguv[1]) and I now get cannot access ls: No such file or directory rather than an infinite loop.
When you do ./arguments ls -l, your code is executing execvp("./arguments", {"./arguments", "ls", "-l"}). Your program is forking and calling itself infinitely: you have made fork-bomb. :)
This is because argv[0] is your executable file (i.e. ./arguments).
You should use argv[1] instead, like this:
execvp(argv[1], &argv[1]);
Note you have to change both the first and the second argument.
When executed, that line will be equivalent to: execvp("ls", {"ls", "-l"}).
According to my understanding, I don't need to know the number of pipelines ahead (although I can, I implement it using a linked list). I have a structure called cmdLine:
typedef struct cmdLine
{
char* const arguments[MAX_ARGUMENTS];
int argCount;
char const *inputRedirect;
char const *outputRedirect;
char blocking;
int idx;
struct cmdLine *next;
} cmdLine;
And here's the function to execute (only the part of the code that matters):
void execute(cmdLine *pCmdLine)
{
int status = 0;
int fd[2];
pid_t id;
if(pCmdLine->next)
{
if(pipe(fd) == -1)
{
perror("Error opening pipe.\n");
exit(1);
}
if(pCmdLine->idx == 0)
{
dup2(fd[1], 1);
close(1);
close(fd[0]);
}
else
{
dup2(fd[0], 0);
close(0);
dup2(fd[1], 1);
close(1);
}
execute(pCmdLine->next);
close(fd[0]);
close(fd[1]);
waitpid(id, &status, 0);
}
id = fork();
if(id == 0)
{
if(execvp(pCmdLine->arguments[0], pCmdLine->arguments) == -1)
{
perror("execvp failed.\n");
exit(1);
}
close(fd[0]);
close(fd[1]);
exit(0);
}
if(pCmdLine->blocking)
{
waitpid(id, &status, 0);
}
if(pCmdLine->next)
{
execute(pCmdLine->next);
close(fd[0]);
close(fd[1]);
}
}
Basically, if it's the first command I close the input pipe (as it can't get any input) and replace fd[1] with the stdout so when I activate execvp() the next time it gets the input from the previous command executed and the output goes to the next up until it's the last command - when it only gets input and closes output pipe. I don't understand where the problem is, when I type something like ls -l | tail -n 2 it prints the long-listing of the WHOLE folder and then it is stuck (as if getting input - although I enter lots of input and nothing happens as preparing for segmentation fault).
What is the problem? How do I fix this? I think I'm not in the right direction at all, and I really need help with this.
I'm trying to fork and then execute two or more piped commands in the child process. My idea is to use a while loop to continuously fork and execute the command in one process while continuing the loop in the other. Here's my code:
void
execute_pipe_command(command_t *c)
{
command_t command = *c;
pid_t pid = fork();
if(pid > 0) {
int status;
while(waitpid(pid, &status, 0) < 0)
continue;
if(!WIFEXITED(status))
error(1, errno, "Child exit error");
command->status = WEXITSTATUS(status);
return;
} else if (pid == 0) {
while(command->type == PIPE_COMMAND)
{
int fd[2]; pipe(fd);
pid = fork();
if(pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
char **args = command->u.command[1]->u.word;
execvp(args[0], args);
} else if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
command = command->u.command[0];
continue;
} else {
error(1, errno, "forking error");
}
}
char **args = command->u.word;
execvp(args[0], args);
} else {
error(1, errno, "forking error");
}
}
Command is a struct that hold it's type, and if it's a pipe command it holds left and right children commands. Otherwise if it's a simple command it holds an array of strings that make up the command.
When I call this function with a pipe command like ls | cat it should execute the commands, but instead it behaves weirdly. The first two piped commands will run but won't give control back to the program. Instead it'll hang. The subsequent commands are just ignored. So if I give this ls | cat | wc this function will print ls and won't exit until I give a SIGINT.
I'm pretty much confused as to what's going on. I'd appreciate if someone could point out the problem.
while (command->type == PIPE_COMMAND) is always true! This is way it hangs.
I'm trying to write a shell in c and it mostly works except for grep. Whenever I give a grep command in the shell, it just doesn't output anything. Here is the part of the code I use to create a new child process and then run execvp() in it.
The file descriptors(fd_in and fd_out) in dup2 are passed as arguments to the function which has this code. And most interestingly, when I give 'grep' or 'grep --help' it displays as usual. Am I missing anything? Or something special has to be done with grep?
This is what happens with my shell: The last command outputs when run from bash.
--> grep
Usage: grep [OPTION]... PATTERN [FILE]...
Try `grep --help' for more information.
--> wc /etc/hosts
11 33 314 /etc/hosts
--> grep -i "perror" shell.c
-->
Here is the code :
void
create_process(char *cmd_argv[], int fd_in, int fd_out, char *buffer_copy) {
/*Flag bit for Background processes*/
int FLAG = 0;
pid_t cpid;
int status;
int i = 0,j = 0;
/*Find the no. of arguments*/
while(cmd_argv[j] != NULL)
j++;
/*Set the flag bit*/
if(strcmp("&", cmd_argv[j-1]) == 0) {
FLAG = 1;
cmd_argv[j-1] = NULL;
}
//Create a child process
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
//In the child...
if (cpid == 0) {
/*Checking if the file descriptors are already assigned*/
/*For stdin*/
if (fd_in != STDIN_FILENO) {
dup2(fd_in, STDIN_FILENO);
close(fd_in);
}
/*For stdout*/
if (fd_out != STDOUT_FILENO) {
dup2(fd_out, STDOUT_FILENO);
close(fd_out);
}
/*Run the cmd specified*/
status = execvp(cmd_argv[0], cmd_argv);
/*In case of errors*/
if(status < 0) {
perror("execvp ");
exit(1);
}
}
//In the parent...
else {
if(FLAG == 1) {
/*Find where the new bg process can be inserted*/
while(1) {
if (bgprocess[i].pid == 0) {
bgprocess[i].pid = cpid;
strcpy(bgprocess[i].cmd, buffer_copy);
break;
}
i++;
}
printf("[%d] : %s\n", cpid, cmd_argv[0] );
}
/*If not bg, wait for the process to exit*/
else
waitpid(cpid, NULL, 0);
}
}
The problem is using the quotes in your shell. Bash does a lot of things in background.
grep -i perror shell.c should give you output on your shell, whatever is anticipated when run from bash.
Quoth man dup2:
After a successful return from one of these system calls, the old and
new file descriptors may be used interchangeably. They refer to the
same open file description (see open(2))…
Therefore after after your call to dup2 (which you should be checking for error returns) you close oldfd and newfd because they are the exact same descriptor.
What I can't figure out is why grep --help works at all but you aren't showing enough code to answer that question.
Added following comment: You still aren't giving enough code. If grep isn't working than what is? Does wc /etc/hosts work? There is nothing special about grep at all, indeed it is a completely unspecial Unix filter.