I'm testing the execl() function tho I'm not being able to solve how to make the printf() after the execl() show up when I run the program. I figured out it has something to do with the fflush() function, although I still can't do it.
Heres the code.
#include<sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
void main(){
printf ("Show content from directory /:\n");
execl( "/bin/ls", "ls", "-l", "/", NULL );
fprintf (stdout,"End of command: ls -l /\n");
fflush(stdout);
}
From the man page
https://linux.die.net/man/3/execl
Return Value
The exec() functions only return if an error has occurred. The return value is -1, and errno is set to indicate the error.
If you really want to use execl() instead of system(), then
you should change the code like this.
#include<sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <stdio.h>
int main(void){
printf ("Show content from directory /:\n");
pid_t p=fork(); // create a child process
if(p==0) // if we are in the child process
{
execl( "/bin/ls", "ls", "-l", "/", NULL ); // replace it with a new program
perror("execl"); // reaching this line is necessarily a failure
exit(1); // terminate child process in any case
}
waitpid(p, NULL, 0); // wait for child process to terminate
fprintf (stdout,"End of command: ls -l /\n");
fflush(stdout);
return 0;
}
Related
I have the following program:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/wait.h>
int main()
{
int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = "-w";
argv[2] = NULL;
pipe(p);
if (fork() == 0)
{
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execvp(argv[0], argv);
}
else
{
close(p[0]);
write(p[1], "hello world\n", 12);
}
fprintf(stdout, "hello world\n");
}
And when I run it:
$ gcc a.c
$ ./a.out
I got the following:
hello world
$ 2
_ // the cursor is flickering here
After I type Enter, the program exit. What is the reason for this? In addition, if I exchange the content in the parent process and child process like this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <sys/wait.h>
int main()
{
int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = "-w";
argv[2] = NULL;
pipe(p);
if (fork() == 0)
{
close(p[0]);
write(p[1], "hello world\n", 12);
}
else
{
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
execvp(argv[0], argv);
}
fprintf(stdout, "hello world\n");
}
I got the output as I expect:
hello world
2
$
The program has exited and is ready to get next command. What's the problem of the first program?
If you look very carefully at your output, you'll see that your program has exited:
hello world
$ 2
_ // the cursor is flickering here
See how the $ is printed? This is your shell waiting for input as usual. By pressing enter you've just input a blank command to your shell and obtained a second $ prompt.
What's the 2 doing there? That's wc's output. Your program isn't actually waiting for wc to exit. This means that your program exits before wc does, so the shell resumes, prints its prompt, and only then does wc exit and print 2.
To fix, you'll probably want to add some kind of wait call to wait for the child process in the parent.
i tried to write c program in Unix environment that using execlp function.
the command is:
execlp("tsort","tsort","text.txt",">","1.txt",(char *)NULL);
syserr("execlp");
i am always gets the same error.
the error is:
tsort: extra operand `>'
what did i do wrong?
'>' is not a parameter, it is normally interpreted by a shell. If you want to achieve the same effect in C code, you have to do the same thing the shell normally does:
open a file (1.txt) for writing
fork() a new process
[in child] replace the stdout of the new process with the file's descriptor using dup2()
[in child] exec the command
Simplified example code for POSIX:
#define _POSIX_C_SOURCE 200101L
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int outfd = open("1.txt", O_CREAT|O_WRONLY|O_TRUNC, 0644);
if (!outfd)
{
perror("open");
return EXIT_FAILURE;
}
pid_t pid = fork();
if (pid < 0)
{
close(outfd);
perror("fork");
return EXIT_FAILURE;
}
if (pid)
{
// child code
dup2(outfd, 1); // replace stdout
close(outfd);
// just a "useless cat" for simplicity:
execlp("cat", "cat", "redir.c", 0);
}
else
{
// parent code
close(outfd);
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) return WEXITSTATUS(status);
else return EXIT_FAILURE;
}
}
As per the comment: If you don't mind replacing your process with the called program, you don't even need to fork and the program becomes very short:
#define _POSIX_C_SOURCE 200101L
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int outfd = open("1.txt", O_CREAT|O_WRONLY|O_TRUNC, 0644);
if (!outfd)
{
perror("open");
return EXIT_FAILURE;
}
dup2(outfd, 1); // replace stdout
close(outfd);
execlp("cat", "cat", "redir.c", 0);
}
This of course is not what an interactive shell does.
I'm trying to use date and wc with pipes on CentOS. I'm not able to printf that I'm in parent or child. Any help is appreciated.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
/* pipe1.c - send information through pipe. */
void syserr(char* msg)
{
printf("%s", msg);
}
void child(int pfd[]){
dup2(pfd[1],1);
execl("/bin/date", "date", 0);
}
void main()
{
int pfd[2], i, pid;
char str[] = "Hello World!\n";
if (pipe(pfd) == -1)
syserr("pipe");
printf("pfd[0] = %d, pfd[1] = %d\n", pfd[0], pfd[1]);
pid=fork();
switch(pid) {
case -1:
syserr("fork");
case 0:
{
printf("I'm child'");
child(pfd);
}
default:{ /* parent only */
if(pid!=0)
{
printf("I'm parent'");
dup2(pfd[0],0); //input
execl("/bin/wc", "wc", 0);
}/*default*/
} /*switch*/
}
}
Remember that <stdio.h> is buffered, and stdout is generally line-buffered, at least when it is a terminal. See setvbuf(3)
So you should either end each of your printf format control string with a \n or call fflush(3) at appropriate places. In particular, do a fflush(NULL); before your fork and your execl.
Also use perror on failure (i.e. replace every call to syserr by perror) to understand how system calls are failing. See perror(3) & errno(3) & strerror(3).
BTW, your main is incorrectly declared. You should enable all warnings and debug info when compiling (e.g. compile with gcc -Wall -Wextra -g). Improve your code to get no more warnings. Then use the debugger gdb ...
Notice that to avoid zombie processes, your parent process should use some waiting system call like waitpid(2) or wait(2) or wait4(2)
Works now. I had to add close(pfd[0]); in child, and close(pfd[1]); in parent.
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <semaphore.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
/* pipe1.c - send information through pipe. */
void child(int pfd[]){
printf("I'm in child func\n");
close(pfd[0]);
dup2(pfd[1],1);
execl("/bin/date", "date", 0);
}
int main(){
int pfd[2], pid;
if (pipe(pfd) == -1) perror("pipe");
printf("pfd[0] = %d, pfd[1] = %d\n", pfd[0], pfd[1]);
fflush(NULL);
pid=fork();
switch(pid) {
case -1:
perror("fork");
case 0:
{
printf("I'm child\n");
child(pfd);
}
default:{ /* parent only */
if(pid!=0){
printf("I'm daddy\n");
close(pfd[1]);
dup2(pfd[0],0); //input
execl("/bin/wc", "wc", 0);
}/*default*/
} /*switch*/
}
return 0;
}
I'd really love your help with understanding why doesn't the process reach the "son process" after using fork() command. I'm trying to write a program that runs another program, but It seems that the program dosen't even reach the son process. I can tell that since "son process" is not being printed to the screen, and I really wonder why.
Here's a sketch of the code- I can't even check if it is alright since as I said, it doesn't even reaching the son process, I always get "son exited with error".
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/time.h>
#include <time.h>
#define MAXARGV 5;
int main() {
char* cmd;
int child_status;
char* s;
char** argv;
int counter;
cmd= (char*) calloc( 5, sizeof(char)*20);
s=(char*) calloc(1,sizeof(char)*20);
argv=(char**) calloc(5, sizeof(char*)*20);
printf("Please write a command\n");
gets(cmd);
counter = 0;
while (strcmp(cmd, "exit") != 0) {
int pid = fork();
if (pid == 0) {
printf("son process");
while (sscanf(cmd, "%s", s) == 1) {
strcpy(argv[counter], s);
counter++;
}
execv(argv[0], argv);
printf("the command is not legal");
assert(0);
}
else {
if (wait(&child_status) == -1) {
printf("error waiting for pid=%d\n", pid);
exit(-1);
}
if(WIFEXITED(child_status)!=0)
printf("son status=%d\n", WEXITSTATUS(child_status));
else
printf("son exited with error\n");
}
printf("Please write a command");
gets(cmd);
}
free(s);
free(cmd);
free(argv);
printf("here as well");
return 1;
}
The program reaches the printf("son process") just fine, but that just puts the string in a buffer inside the process and since you didn't fflush() it, it doesn't make it to the screen and is discarded with the rest of the process' memory in the exec call. Note, that stdout is normally line-buffered, so if you had newline there, it would auto-flush. Also stderr is by default unbuffered and more suitable for debug prints (fprintf(stderr, "child process")).
You are trying to assemble the command read from standard input in argv, but it only has memory for the actual arguments given to you, so you overrun this memory and get segmentation fault.
if WIFEXITED gives zero, you should use WIFSIGNALED and WTERMSIG to confirm that the error is indeed SIGSEGV.
assert(0) is not a good way to terminate process after error. exit(1) is. Assertions are only for conditions that indicate bug in the code itself if they happen and are often eliminated (by defining NDEBUG) from production code.
I am reading GNU/Linux application programming the 2nd edition,you can reach what am reading from here.After I write the code similar to his,but it work strangely:
$ ./shell
./shell>>quit
$ ./shell
./shell>>date
Sun Aug 8 21:19:37 CST 2010
./shell>>quit
$ ./shell
./shell>>abc
execlp failed: No such file or directory
./shell>>quit
./shell>>quit
$./shell
./shell>>abcd execlp
execlp failed: No such file or directory
./shell>>quit
./shell>>quit
The first and second cases are ok,but the third and forth ones somewhat need two quit to quit.This is not what i am expecting.I guess something is wrong with fork(),or the waitpid(),but this still got unsolved after asking a few people around me.Now is summer time, i.e. summer holiday,I got no more mates to go for.Thanks always.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define LINE_LEN 80
int main(int argc,char* argv[])
{
pid_t pid;
char cmd[LINE_LEN+1]={'\0'};
while(1)
{
printf("%s>>",argv[0]);
if(fgets(cmd,sizeof(cmd),stdin)==NULL)
{
perror("fgets failed");
break;
}
cmd[strlen(cmd)-1]='\0';
if(strncmp(cmd,"quit",4)==0)
{
break;
}
if((pid=fork())==-1)
{
perror("fork failed");
break;
}else if(pid==0)
{
//TODO no option can be specified for cmd
execlp(cmd,cmd,NULL);
perror("execlp failed");
}else
{
waitpid(pid,NULL,0);
}
}
return 0;
}
Normally, if execlp succeeds, your child process becomes the new process, and thus execution of your code effectively stops at that point.
Now the execlp failed, you print out the error, and continue the child process as if nothing happened! You should exit(0) the child, if the execlp failed.
execlp will fail if cmd does not exist.
As mvds rightly said, when execlp fails, you should exit the child process. But I recommend not forking when cmd does not exist.
You could use something like access (see access(2)) before using fork to make sure cmd exists (within your PATH), and is an executable.
Just post the debug version to make it clear:strncmp is called in which process,and when child or parent exit
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define LINE_LEN 80
int main(int argc,char* argv[])
{
pid_t pid;
char cmd[LINE_LEN+1]={'\0'};
while(1)
{
printf("%s>>",argv[0]);
if(fgets(cmd,sizeof(cmd),stdin)==NULL)
{
perror("fgets failed");
break;
}
cmd[strlen(cmd)-1]='\0';
if(strncmp(cmd,"quit",4)==0)
{
printf("process :%d in strncmp equal\n",getpid());
break;
}
if((pid=fork())==-1)
{
perror("fork failed");
break;
}else if(pid==0)
{
printf("new child:%d\n",getpid());
//TODO no option can be specified for cmd
execlp(cmd,cmd,NULL);
perror("execlp failed");
//This is critical
//exit(0);
}else
{
printf("parent:%d(his child is %d)\n",getpid(),pid);
waitpid(pid,NULL,0);
}
}
printf("process :%d exit...\n",getpid());
return 0;
}
just see one case:
./shell
./shell>>abc
parent:8356(his child is 8357)
new child:8357
execlp failed: No such file or directory
./shell>>quit
process :8357 in strncmp equal
process :8357 exit...
./shell>>quit
process :8356 in strncmp equal
process :8356 exit...