I'm writing a program in C in Linux which creates main and assistant process and communication should be like: Main process (parent) receives keyboard input (char array) then passes it through pipe to assistant (child) process, then child should call some other .c file with execl (my .c file to call is named simple.c) and then pass the result on the second pipe... and at that point with execl I have some issue, here is my child code:
if (childpid == 0) {
printf("I am child!\n");
if (dup2(fd[0], STDIN_FILENO) == -1
|| dup2(fd2[1], STDOUT_FILENO) == -1)
fprintf(stderr,
"Child error: Redirection of std input/output failed!");
else if (close(fd[0]) == -1 || close(fd[1]) == -1
|| close(fd2[0]) == -1 || close(fd2[1]) == -1)
fprintf(stderr, "Child error: Pipes closing failed!");
else {
execl("/home/myproj/simple.c", " ", NULL);
fprintf(stderr, " Execl failed ! ");
}
return 0;
}
simple.c should be something like this:
int main()
{
// ...
read(0, string, sizeof(string));
string[0] = string[1];
write(1, string, sizeof(string)); // sending new string on second pipe
return 0;
}
Can anybody help me whats the problem with execl line? Thanks a lot.
Can anybody help me whats the problem with execl line?
You could have helped yourself by not using the poor error message
fprintf(stderr, " Execl failed ! ");
but something more sensible, like
perror("execl /home/myproj/simple.c");
this would have told you Permission denied and you could have looked up the possible reasons in the manual, finding the most probable:
The file described by path or file is not
executable.
Related
I have this function that executes a command
int cmd2(char * const *cmd, char * std_out)
{
char tmp[4096];
int pipefds[2], r, status, x;
pid_t pid;
if (pipe(pipefds) == -1){
return -1;
}
if ( (pid = fork()) == -1){
return -1;
}
if (pid == 0)
{
dup2(pipefds[1], STDOUT_FILENO);
dup2(piepfds[1], STDERR_FILENO);
close(pipefds[0]);
close(pipefds[1]);
execvp(cmd[0] , cmd);
}
else
{
close(pipefds[1]);
x = read(pipefds[0], tmp, 4096);
printf("Got %d bytes\n",x);
wait(NULL);
}
return 0;
}
the error message that should be outputted
┌──(kali㉿kali)-[~/]
└─$ ./wow
zsh: no such file or directory: ./wow
when running another c code that fputs a buffer into stderr it is display by the cmd2 without any problems
I tried to redirect stderr to stdout using 2>&1 but this does not seem to have an effect
how to read any/all results from executing the command
If you try running a non-existent command with cmd2, execvp will set errno and return. It will not print anything anywhere. You need to check errno and print the error. Something as simple as:
execvp(cmd[0], cmd);
perror(cmd[0]);
exit(EXIT_FAILURE);
should usually suffice. zsh does something like that when you try to run a non-existent command with it, that's why you see an error message.
Note however that it is the child process that prints the message. You discard child process output and only print its length. You need to do something about it if you want to see the message.
I'm currently working on an assignment that is teaching us on how to implement pipes in my custom shell. Before I actually implement pipes on my shell and change my code, they want us to create two children, and run a command on each child while implementing a pipe:
Execute "ls -l" on child 1
Execute "tail -n 2" on child 2
Currently, my code looks like this:
int main (int argc, char * argv[]){
int debugMode=0;
int p[2];
int writeDup;
int readDup;
int status;
if (strcmp(argv[1],"-d")==0)
debugMode=1;
if (pipe(p)<0)
return 0;
int child1= fork();
if (child1 == 0)
{
if (debugMode == 1)
fprintf(stderr, "Child 1 is redirecting stdout to write end of pipe.\n");
fclose(stdout);
writeDup = dup(p[1]);
close(writeDup);
char *args[] = {"ls","-l",NULL};
if (execvp(args[0],args)<0){
if (debugMode ==1)
perror("ls -l failed ");
return 0;
}
}
else
{
if (debugMode == 1)
fprintf(stderr, "Parent process is waiting to close write end of pipe.\n");
while ((child1=waitpid(-1,&status,0))!=-1);
close(p[1]);
}
int child2 = fork();
if (child2 == 0)
{
fclose(stdin);
readDup = dup(p[0]);
close(readDup);
char *args[] = {"tail","-n","2",NULL};
if (execvp(args[0],args)<0){
if (debugMode ==1)
perror("tail -n 2 failed ");
return 0;
}
}
else{
if (debugMode == 1)
fprintf(stderr, "Parent process is closing read end of pipe.\n");
while ((child2=waitpid(-1,&status,0))!=-1);
close(p[0]);
}
if (debugMode == 1 && child1 != 0 && child2 !=0)
fprintf(stderr, "Waiting for child processes to terminate.\n");
while ((child1=waitpid(-1,&status,0))!=-1 && (child2=waitpid(-1,&status,0))!=-1 );
return 0;
}
However, while executing, I receive several errors:
ls: write error : bad file descriptor
tail: cannot fstat 'standard input': Bad file descriptor
tail: -: bad file descriptor
They requested us to close the standard inputs & outputs, so by doing so I assume that the program should default into reading/writing into the pipe. I'm continuing to try to find a solution, I would appreciate any help or direction!
I have a problem with dup2 syscall.
I added a while loop(a part of my program) that running over a students directory,
each student has a "c" file that I compile to an "exe" file. the students' programs scan from
testInput file (using dup2 to take the keyboard) 2 numbers, add them and then the answers is written down to programOutPut file.
afterward, I compare the 2 programs with a comparison program I wrote and it helps me with WEXITSTATUS to know whether a student succeeded in the test.
The problem is that I try to write the grade sheet into the file and then print it also to the screen.
somehow it only appears on the file or the screen but not both.
while (myDirent = readdir(dir)) {
if (strcmp(myDirent->d_name, ".") == 0 || strcmp(myDirent->d_name, "..") == 0)
continue;
if ((pid = fork()) == 0) {
status=execlp("gcc", "gcc", "-o", mainPath, cPath, NULL); //compiling students code to main.out path
if (status == -1) {
fprintf(stderr, "gcc Exec failed\n");
exit(-1);
}
}
wait(&status);
fdin = open(testInputPath, O_RDONLY); //test input file I wrote to compare with student's output
if(fdin==-1){
fprintf(stderr, "Error opening test input file\n");
exit(-1);
}
fdout = open(programOutputPath, O_WRONLY | O_CREAT | O_TRUNC,0777); //opening file for each student
if(fdout==-1){
fprintf(stderr, "Error opening Student's program output file\n");
exit(-1);
}
if ((pid = fork()) == 0) {
dup2(fdin, 0);
dup2(fdout, 1);
status= execlp(mainPath,mainPath, NULL);
if (status == -1) {
fprintf(stderr, "Student's main Exec failed\n");
exit(-1);
}
}
wait(&status);
fdresults = open("results.txt", O_WRONLY | O_CREAT | O_APPEND, 0777); //grades sheet
if (fdresults == -1) {
fprintf(stderr, "Error opening results.csv file\n");
exit(-1);
}
if ((pid = fork()) == 0) {
status= execlp("./comp.out", "./comp.out", programOutputPath, expectedOutputPath, NULL); //compare program I wrote that simply compare 2 files and return value to status
if (status == -1) {
fprintf(stderr, "Compare Exec failed\n");
exit(-1);
}
}
wait(&status);
**dup2(fdresults, 1); //trying to write to the file the grades
printf("%s,%d\n", myDirent->d_name,WEXITSTATUS(status));
dup2(fdscreen, 1); // trying to return to stdout unsuccessfuly**
}//end of while loop
First, do not open a file that is already open (source). Right now, you are opening testInputPath and programOutputPath during each iteration of the the loop without closing them. This can lead to undefined behavior.
It's unclear where fdscreen came from. If you do something similar to this answer, then you can achieve what you're looking for using dup(2). You should flush the corresponding userspace file buffer using fflush(3) before calling dup2.
A Better Solution
A much better solution is to open results.txt outside the loop, and then use dprintf(3) to write directly to the file. This is more elegant, and avoids having to change stdout. So, you can easily replace the last three lines of the loop with
dprintf(fdresults, "%s,%d\n", myDirent->d_name, WEXITSTATUS(status));
Don't forget to close fdresults after the loop is complete. In general, each open(2) should have a corresponding close(2). Using this approach is strongly encouraged over changing stdout every iteration of the loop.
im currently implementing a shell in C.
My problem arises when i try to run a command like this:
SHELL$: sort < txtFile | grep key
im running sort < txtFile in a process (child), and in the parent i.e else if(pid > 0) im running the other command to the right of the pipe.
The program runs fine, but it exits the infinite loop that i set up in main to keep receiving input from the user.
How could i solve this problem?
this is the code i have so far to deal with the pipe, i didnt include the code that i have to deal with the redirects:
c2p is the pipe i setup for this.
if(pid == 0)
{
if( PIPE_FLAG )
{
close(c2p[0]);
if(dup2(c2p[1], STDOUT_FILENO) == -1){
perror("dup2() failed");
exit(2);
}
}
/* Execute command */
execvp(cmd_args[0], cmd_args);
perror("exec failed 1. "); /* return only when exec fails */
exit(-1);
}
else if(pid > 0)
{
if(PIPE_FLAG)
{
close(c2p[1]);
if(dup2(c2p[0], STDIN_FILENO) == -1){
perror("dup2() failed");
exit(-1);
}
execvp(nxt_args[0], nxt_args);
perror("exec failed 2. ");
exit(-1);
}
}
else
{
/* error occurred */
perror("fork failed");
exit(1);
}
I'm running sort < txtFile in the child process, and in the parent I'm running the command to the right of the pipe.
What happens to your shell process, then? The parent process is the shell. By running the right-side command in the parent process you're having it take over the shell's process. Remember that exec() replaces the current process.
You'll need to fork() twice, and execute the two sides of the pipe in the child processes. The parent must remain the shell, which will then wait() for the children to exit before presenting the next command prompt.
/* How shell works */
#include<stdio.h>
#include<unistd.h>
main (int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "\nUsage: ./a.out cmd [options]...\n");
}
if (!fork ())
{
argv++;
execvp (argv[0], argv);
}
}
In my C program, I am creating a child process and running execvp in the child. But I'm trying to change the error message to something else, for the execvp command (if there was an error).
I know that if it returned, then it was an error, then I can print my own custom error message on the next line. That's one type of error that can occur, for example this happens if I give the command "sdfsd" to execvp. This part is working for me.
But if I type, "find sdfsd" then it does not return and prints "find: `sdfsd': No such file or directory".
I want to change this message (and essentially any kind of error message coming from exevcp) to my own custom one.
I believe I can use dup2 to do this, but I'm not sure how...
In the child process I tried
dup2(STDERR_FILENO, 1);
fclose(stderr);
But this just stops the child process from writing any error messages. I still can't print my own message in all cases..
Does anyone know how to do this?
thanks
Since execvp never returns if it successfully starts the new program, you won't be able to print your own error message in the child process after the program run by execvp fails. One option would be to pipe stderr to the parent process, intercept the error message there, and then print your own error message.
For example:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
int ff, p[2];
FILE *f;
char *vv[] = {"find", "garbage", (char *)NULL};
char msg[100];
if (pipe(p) != 0)
{
fprintf(stderr, "Pipe failed\n");
return 1;
}
if ((ff = fork()) == -1 )
{
fprintf(stderr, "Fork failed\n");
return 1;
}
if (ff == 0)
{
/* In the child process */
close(2);
close(p[0]);
dup2(p[1], 2);
execvp("find", vv);
return 1;
};
/* In the parent process */
close(p[1]);
f = fdopen(p[0], "r");
if (f == NULL)
{
fprintf(stderr, "Fdopen failed\n");
return 1;
}
if (fgets(msg, sizeof(msg), f) == NULL)
{
fprintf(stderr, "Fgets failed\n");
return 1;
}
printf("Error message was: %s", msg);
/* and so on */
return 0;
}