I'm trying to run the following C program:
/*
* Illustrates system call fork
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int
main (
int argc,
char *argv[]
)
{
int tC, tF;
pid_t pid;
tC = atoi (argv[1]);
tF = atoi (argv[2]);
fprintf (stdout, "Main : ");
fprintf (stdout, "PID=%d; My parent PID=%d\n",
getpid(), getppid());
pid = fork();
if (pid == 0){
// Child
sleep (tC);
fprintf(stdout, "Child : PIDreturned=%d ", pid);
fprintf (stdout, "PID=%d; My parent PID=%d\n",
getpid(), getppid());
} else {
// Father
sleep (tF);
fprintf(stdout, "Father: PIDreturned=%d ", pid);
fprintf (stdout, "PID=%d; My parent PID=%d\n",
getpid(), getppid());
}
return 0;
}
I'm able to compile the code, but when I try to run the executable file I get a "segmentation fault (core dump)" error message.
Can anyone tell me what is causing such issue and how to fix it?
You are not checking if the user supplied any arguments. If fewer than 2 arguments are supplied to the program, it'll do atoi on argv out of bounds (which has undefined behavior and the program may crash as a result).
I suggest that you check it directly at start of main. Example:
int main(int argc, char* argv[]) {
if(argc != 3) {
fprintf(stderr, "USAGE: %s child-sleeptime parent-sleeptime\n", argv[0]);
return 1;
}
// ... the rest of `main`
Related
I'm trying to better understand exec() - so I have the following script in testing.c
#include <stdio.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
if(argc < 2) {
fprintf(stderr,"Error:: Expecting an Argument!\n");
exit(-1);
}
pid_t pid;
pid = fork();
if (pid==0) {
execlp("./testing","testing",NULL);
fprintf(stderr, "I want to get to here...\n");
exit(-1);
}
wait(NULL);
printf("Parent and child done\n");
return 0;
}
The block below is the output after I execute with ./testing one:
Error:: Expecting an Argument!
Parent and child done
In reading how exec() works, I was expecting to be able to fprintf after my execlp call since it should be returning -1, I'm wondering if I need to be setting an errno or something or being more explicity about throwing something so that execlp recoginize the error?
If the execlp function successfully starts the given program, it does not return. The current program image gets replaced with the program image of the new program. So even if the new program exits with status -1, it still doesn't go back to the program that called execlp.
If you want to get the exit status of the child process, pass the address of an int to wait and read that:
int main(int argc, char ** argv)
{
if(argc < 2) {
fprintf(stderr,"Error:: Expecting an Argument!\n");
exit(-1);
}
pid_t pid;
pid = fork();
if (pid == -1 {
perror("fork failed");
exit(-1);
} else if (pid == 0) {
execlp("./testing","testing",NULL);
perror("execlp failed");
exit(-1);
}
int status;
wait(&status);
printf("child exit status: %d\n", WEXITSTATUS(status));
printf("Parent and child done\n");
return 0;
}
The output:
Error:: Expecting an Argument!
Parent and child done
is from
(first line) child process tries to run but no command line parameter.
(second line) parent process finishes
I have a very simple c program:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char *argv[] ){
int rc = fork();
if (rc < 0){
// fork failed
fprintf(stderr, "fork failed\n");
} else if (rc == 0) {
printf("hi, I'm child! %d\n", (int) getpid());
fflush(stdout);
} else {
printf("hi, I'm the parent of %d\n", rc);
fflush(stdout);
}
return 0;
}
For some reason, when I compile and run this program, sometimes both print statements will show, but other times only the parent process will show.
Why is this? Even if I don't use wait(), both processes should still run.
Using gcc 7.5.0 ubuntu 18.04
Because the IDE stops listening when the parent process closes, or the shell leaves the child process stuck in the background.
To see the arbitrary printf order:
int status;
printf("hi, I'm the parent of %d\n", rc);
fflush(stdout);
waitpid(pid, &status, 0);
I made some weird experiment and created a program that forks itself and executes itself in the child process, which is forked again and executes itself again...and so on.
Only after exiting the ssh session I was connected to it stopped.
What are the possible ways to kill such program, which continuously changes it's own PID ?
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
int spawn (char* program, char** arg_list)
{
pid_t child_pid;
child_pid = fork ();
if (child_pid != 0)
/* This is the parent process. */
return child_pid;
else {
printf("my pid is %d my ppid is %d\n", getpid(), getppid());
execvp (program, arg_list);
fprintf (stderr, "an error occurred in execvp\n");
abort ();
}
}
int main ()
{
char* arg_list[] = {
"./1", /* argv[0], the name of the program. */
NULL
};
spawn ("./1", arg_list);
return 0;
}
Create a parent process that gets from the command line n arguments arg1, arg2, ... , argn. arg1 is the name to a source C, arg2 is the name of the executable file results from compile arg1, and arg3, ... , argn are arguments to start.
The parent compiles arg1 and creates the executable arg2, after that runs it into a son process.
I tried to solve the problem, using some examples, but I didn't really understand them, so the program is not working. I really need some help...
#include<unistd.h>
#include<stdio.h>
#include<sys/wait.h>
#include<string.h>
int main(int argc, char* argv[]){
char com[200];
int p;
p=fork();
strcpy(com,"gcc -o prog.c");
strcat(com,argv[1]);
if(p==0){
if(WEXITSTATUS(system(com))==0)
execl("./prog.c","./prog.c",argv[3],argv[4],argv[5],NULL);
}
wait(0);
exit(0);
return 0;
}
The C program I want to use, reads some input data from two files and stores data into another file.
This code more or less does what you say your program should do. In particular, it uses argv[2] as the program name. It uses snprintf() to avoid overflows with long arguments (but doesn't verify that it didn't overrun). It prints various status messages — partly as a debugging aid, partly to give meaning to the various parts of the program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
int p;
if (argc != 6)
{
fprintf(stderr, "Usage: %s source program file1 file2 file3\n", argv[0]);
return(1);
}
if ((p = fork()) == 0)
{
char com[200];
snprintf(com, sizeof(com), "gcc -o %s %s", argv[2], argv[1]);
if (system(com) == 0)
{
printf("Compilation of %s successful\n", argv[2]);
fflush(0);
execl(argv[2], argv[2], argv[3], argv[4], argv[5], (char *)NULL);
fprintf(stderr, "Failed to execute %s\n", argv[2]);
return(1);
}
fprintf(stderr, "Compilation of %s from %s failed\n", argv[2], argv[1]);
return(1);
}
int status;
wait(&status);
printf("Compilation and execution of %s yielded status %d\n",
argv[2], WEXITSTATUS(status));
return 0;
}
When this file is named gc.c and is compiled to make gc, it can be run as:
$ ./gc gc.c ./gc2 gc.c gc.c gc.c
Compilation of ./gc2 successful
Usage: ./gc2 source program file1 file2 file3
Compilation and execution of ./gc2 yielded status 1
$
The usage message from gc2 is correct; the program expects 6 arguments, not the 4 it is given by the program.
You should look into the manual of exec which will tell you how to run exec to fork another process that behaves according to the specification. This code can help you how to pass on variables to a child process:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
int main()
{
/*Spawn a child to run the program.*/
pid_t pid=fork();
if (pid==0) { /* child process */
static char *argv[]={"echo","Foo is my name.",NULL};
execv("/bin/echo",argv);
exit(127); /* only if execv fails */
}
else { /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
}
return 0;
}
I am learning the way to use ordinary pipeline in linux for the communication between parent and child process. The basic task is just to send a message to the child process from parent process, and then the child do some conversion and pass the result back to the parent. My result shown is some random character like ���. I have been contemplating for a long while and still couldn't figure out the bug. Thanks for your help.
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define READ_END 0
#define WRITE_END 1
void convert(char* str);
int main(int argc, char *argv[]){
int pid; /* Process ID */
int status;
char *input;
char *read_msg_c;
char *read_msg_p;
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
printf("Please provide the string for conversion \n");
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END],input,strlen(input)+1);
close(pfd1[WRITE_END]);
read(pfd2[READ_END],read_msg_p,strlen(input)+1);
printf("%s\n",read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
read(pfd1[READ_END],read_msg_c, strlen(input)+1);
printf("Child: Reversed the case of the received string. \n");
write(pfd2[WRITE_END],read_msg_c,strlen(input)+1);
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
void convert(char *str){
int i = 0;
while (str[i]){
if (isupper(str[i])){
str[i] = tolower(str[i]);
}
else if (islower(str[i])){
str[i] = toupper(str[i]);
}
i++;
}
}
Your primary bug is that your variables read_msg_p and read_msg_c are uninitialized pointers.
Make them into arrays:
char read_msg_p[1024];
char read_msg_c[1024];
You seem to be missing <stdio.h> (but you don't really need <sys/types.h> any more). You should error check your reads and writes; your reads will probably use a different maximum size once you've allocated the space for them. Etc.
I spotted the problem by looking at the compiler warnings:
$ gcc -O3 -g -std=c99 -Wall -Wextra pipes-14420398.c -o pipes-14420398
pipes-14420398.c: In function ‘main’:
pipes-14420398.c:40:22: warning: ‘read_msg_p’ may be used uninitialized in this function [-Wuninitialized]
pipes-14420398.c:52:22: warning: ‘read_msg_c’ may be used uninitialized in this function [-Wuninitialized]
$
Ignore the line numbers; I'd moderately seriously hacked your code by the time these were the only warnings left. But the lines in question are the read() calls.
Example output form the hacked code, working correctly.
$ ./pipes-14420398 string-to-convert
Process ID of the parent is 37327.
Process ID of the child is 37328.
Child read 18 bytes: <<string-to-convert>>
Parent read 18 bytes: <<string-to-convert>>
$
Note that the code below reads 18 bytes (including the null), but does not print the null (because of the nbytes-1 argument to printf().
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define READ_END 0
#define WRITE_END 1
int main(int argc, char *argv[])
{
int pid; /* Process ID */
char *input;
char read_msg_c[1024];
char read_msg_p[1024];
int pfd1[2], pfd2[2];
if (argc !=2){/* argc should be 2 for correct execution */
/* We print argv[0] assuming it is the program name */
fprintf(stderr, "Usage: %s string-to-convert\n", argv[0]);
exit(-1);
}
input = argv[1];
if(pipe(pfd1) < 0 || pipe(pfd2) < 0){
printf("Failed to create a pipe between parent and child \n");
exit(-1);
}
if((pid = fork()) < 0){ /* Fork the process */
printf("Fork error \n");
exit(-1);
}
else if(pid > 0){ /* Parent code */
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
printf("Process ID of the parent is %d. \n", getpid()); /* Print parent's process ID */
write(pfd1[WRITE_END], input, strlen(input)+1);
close(pfd1[WRITE_END]);
int nbytes = read(pfd2[READ_END], read_msg_p, sizeof(read_msg_p));
if (nbytes <= 0)
printf("Parent: read failed\n");
else
printf("Parent read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_p);
close(pfd2[READ_END]);
}
else if(pid == 0){ /* Child code */
close(pfd1[WRITE_END]);
close(pfd2[READ_END]);
printf("Process ID of the child is %d. \n", getpid()); /* Print child's process ID */
int nbytes = read(pfd1[READ_END], read_msg_c, sizeof(read_msg_c));
if (nbytes <= 0)
printf("Child: read failed\n");
else
{
printf("Child read %d bytes: <<%.*s>>\n", nbytes, nbytes-1, read_msg_c);
write(pfd2[WRITE_END], read_msg_c, nbytes);
}
close(pfd1[READ_END]);
close(pfd2[WRITE_END]);
exit(0); /* Child exits */
}
}
As noted by WhozCraig, there are numerous other changes that could be made. This, however, gets things working reasonably cleanly. You were very close to OK.
Note the debugging techniques:
Compile with high warning levels and fix all warnings.
Print information as it becomes available (or run in a debugger and observe the information as it becomes available).