Why my child process exit without actual termination? - c

I am writing a program about process
The process accept a bash command and run it with exec
I use fork() to create child process run exec inside it
and plan to do some post process in parent process
but for some reason the exec run properly but wait is not working poperly
"post process here" print put before exec's program terminate
It worked before but suddenly not working,
I have not idea what changes I have made to cause this problem
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main(int argc, char *argv[]) {
/* code */
for(int i = 0; i < argc; i++){
//delete argv 0 for convincent
argv[i] = argv[i + 1];
}
int pid = fork();
if(pid == -1){
return 1;
}
if(pid == 0){
execvp(argv[0], argv);
}else{
wait(NULL);
printf("post process here");
}
return 0;
}

First:
//delete argv 0 for convincent
argv[i] = argv[i + 1];
i would us a new array with bound [argc+1] otherwise you are in danger of an array out of bound error (bufferoverflow).
Second:
the called bash command is important. Some commands are just starting a deamon and finish themself.

Related

How can I get my C Shell to recognize that this is a command?

I am very new at C but am currently working on creating a C program to serve as a shell interface. It is supposed to accept commands and then execute each command in a separate process. I am currently stuck trying to get C to recognize that it is a command. I am unsure how to do this, and can't seem to find any useful examples.
Here is my code, it is saying that everything is not a valid command ("no cmd"). Does anyone have any idea why this would be occurring? Is C not able to recognize it is a command in the execvp() function or do I need to implement something for that specific purpose?
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#define MAX_LINE 80
/* 80 chars per line per command */
int main(void) {
//char *args[MAX_LINE/2 + 1];
char *args = calloc(MAX_LINE, (MAX_LINE/2 +1));
const size_t sz = MAX_LINE;
pid_t pid;
/* command line (of 80) has max of 40 arguments*/
int should_run = 1;
while (should_run) {
printf("osh>"); //beginning of command line
fgets(args, sz, stdin); //gets the stdin
char *token = strtok(args, " \n"); //supposed to break str if it has a space or line and recognize there are 2 commands
printf("%s\n", token);
token = strtok(NULL," \n");
printf("%s\n", token);
pid_t parent = getpid();
pid = fork(); //forking child
if(pid == 0){ //if forking occurred
int status = execvp(&args[0], &args); //status of input, is it a command?
printf("%d", status);
printf("forked!");
if(status == -1) { //if cmd err, print
printf("no cmd");
return 1;
} else {
printf("line will be printed");
}
return 0;
}
fflush(stdout); //flush
/*
* After reading user input, the steps are :
* 1: fork a child process
* 2: the child process will invoke execvp()
* 3: parent process waits for the child to exit before
continuing
*/
}
exit(0);
/**
return to the operating system:
-exit this process
-flush all
*/
}
If you look at the documentation for the exec family of functions, you'll note that the functions only return if the exec failed. That's because exec, when successful, completely replaces the calling process with the invoked program.
What you need to do is, from the parent process (i.e., the one that got a positive value returned from fork), wait on the child process via waitpid.
pid_t pid;
pid = fork();
if ( pid < 0 ) {
// Handle the error.
}
else if ( pid == 0 ) {
execvp(&args[0], &args);
// The fact that we've reached this line means that execvp failed.
exit(1);
}
else {
int status;
while ( waitpid(pid, &status, 0) != pid ) {} // We need this loop in case waitpid gets interrupted by a signal.
// status doesn't equal the return value of the child process. We need to extract that with macros.
if ( WIFEXITED(status) ) {
printf("Child process exited with code %i\n", WEXITSTATUS(status));
}
else {
printf("Child process was terminated by signal number %i\n", WTERMSIG(status));
}
}

My program does not stop running after finishing child process

I am now learning about folk, exec etc and I have this piece of code:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
pid_t childpid;
int status;
childpid=fork();
if (childpid== -1){
perror("Failed to fork\n");
exit(1);
}
if (childpid==0) {
printf("I am in child process with id = %lu\n", (long)getpid());
execvp(argv[1], &argv[1]);
perror("exec failure ");
exit(1);
}
else {
printf("I am in parent process with id = %lu\n", (long)getpid());
exit(1);
}
}
The child process works fine but after that for some reason the program continues running without doing anything. It never prints "I am in child process with id = ...." or "I am in parent process with id =... ". It's like it never goes to parent process. Do you have any ideas why? Thanks in advance
From my top comment ...
You are creating a zombie process. This is because the parent process is not waiting for the child to complete.
The parent process will terminate [relatively] quickly. Thus, the child loses its parent and becomes a zombie. A zombie will be reparented by the kernel as a child of process 1 (e.g. systemd or initd).
To fix, add: wait(NULL); after the final printf
UPDATE:
Therefore do I need to always put wait(NULL) in these types of situations?
The TL;DR is ... Yes!
This is what you normally want to do for most programs.
One of the few times you would want to create a zombie is (e.g.) if you're a server program (e.g. inetd).
Servers want to run "detached". That is, as a child of the init process (e.g. systemd, initd, etc.). There is one and only one init process on the system.
All other processes are children of init, even if indirectly. For example, your program's process hierarchy was something like:
init -> window_manager -> xterm -> bash -> your_program
Anyway, most server programs these days are fired up by systemd directly. It examines some config files and starts things based on these config options. So, now, most server programs don't have to do anything special.
But, if you were testing a server of your own, invoked it from the command line, and wanted it to run [detached] in the background, you might do:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
int opt_d;
int
main(int argc, char **argv)
{
char *cp;
pid_t childpid;
int status;
// skip over program name
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'd':
opt_d = 1;
break;
}
}
// detach into background
if (opt_d) {
childpid = fork();
if (childpid == -1) {
perror("Failed to detach\n");
exit(1);
}
// exit the parent -- child is now detached [and a zombie] and a child
// of the init process
if (childpid != 0)
exit(0);
}
childpid = fork();
if (childpid == -1) {
perror("Failed to fork\n");
exit(1);
}
if (childpid == 0) {
printf("I am in child process with id = %lu\n", (long) getpid());
execvp(*argv, argv);
perror("exec failure ");
exit(1);
}
printf("I am in parent process with id = %lu\n", (long) getpid());
wait(&status);
return 0;
}

C: Multi process CMD does nothing after running all commands

I made this in C, and it does run all commands concurrently. But at the end, it puts the last command in the user input part of the CMD then does nothing right after.
This is the output of the command:
hen03:~/Lab_08> ./assign8 whoami , ls -a , pwd
Child Process: PID=11895, PPID=11894, Command=whoami
Child Process: PID=11897, PPID=11894, Command=pwd
Child Process: PID=11896, PPID=11894, Command=ls
/home/uid454/Lab_08
hen03:~/Lab_08> . .. assign8 assign8.c makefile uid454.zip
uid454
hen03:~/Lab_08>
And this is the code I wrote
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
void startProcesses(int commands, char *commandList[6][100]){
static pid_t forks[6];
int status[6];
int i;
for(i = 0; i < commands; i++){
switch(forks[i] = fork()){
case -1: //failed
perror("fork failed");
break;
case 0: //child does things
printf("Child Process: PID=%ld, PPID=%ld, Command=%s\n", (long) getpid(), (long) getppid(), commandList[i][0]);
if(execvp(commandList[i][0], commandList[i]) < 0){
printf("something failed\n");
}
break;
default:
if(i == commands-1){
waitpid(forks[i], &status[i], 0);
}
}
}
}
int main(int argc, char *argv[])
{
int commandNum = 0, index, numOfCommands = 1, j =0;
static char *commandList[6][100];
//start of separate algo
for (index = 1; index < argc; index++){
if(strcmp(strtok(argv[index], " "), ",") == 0){
commandNum++;
numOfCommands++;
commandList[commandNum-1][j] = NULL;
j = 0;
continue;
}
else{
commandList[commandNum][j] = argv[index];
j++;
}
}
//end of separation algo
startProcesses(numOfCommands, commandList);
return 0;
}
I honestly have no idea what the problem is nor how to even google it for that matter. Could someone explain why its doing this?
edit: took out line numbers
Is the problem that the output of some child-processes are shown tacked on to the shell prompt?
Then that's because you don't wait for all your child processes, only the last one. And there's no guarantee that the child-processes will execute in any specific order.
That means your parent process could exit before all child-processes have finished, which means that the shell is back in control and will display the prompt.
You should really wait for all child-processes. Preferably in a second loop if you want the child-processes to run in parallel.

C program that tells the user which child process finished first

I am working on an assignment that involves using fork. The program runs two separate programs simultaneously and tells the user which one finished first. If a child finishes, the other child still running should be killed immediately.
My code so far is this...
int main(int argc, char **argv) {
if (argc != 2) {
perror("Invalid number of arguments!");
exit(1);
}
pid_t pid;
pid_t wpid;
int status = 0;
for (int i = 0; i < 2; i++) {
if ((pid = fork()) == 0) {
execv("/bin/sh", argv[i+1]);
}
}
while ((wpid = wait(&status)) > 0);
printf("%s finished first!", <Insert winning program here>);
return 0;
}
From my understanding, this runs the programs and will not let the parent process continue until the child processes have finished. Now I'm wondering how I can terminate another child and return the winning process.
But how can I immediately get the pid of the losing process so that I can kill it?
Just as TonyB told: the "parent" saves the pid of the new child. 2) wait will tell you the pid of the winning process. More verbose: Save the PID of both children, wait for any one, compare the return value to (one of) the saved PIDs; the matching one is the winner, the non-matching one is the loser. E. g.:
#define _POSIX_SOURCE
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
int main(int argc, char **argv)
{
if (argc != 3) // with two program arguments, argc is 3
fputs("Invalid number of arguments!\n", stderr), exit(EXIT_FAILURE);
pid_t pid[2]; // to store both child pids
pid_t wpid;
for (int i = 0; i < 2; i++)
if ((pid[i] = fork()) == 0)
execl("/bin/sh", "sh", "-c", argv[i+1], NULL),
perror(argv[i+1]), exit(EXIT_FAILURE);
wpid = wait(NULL); // wait for first
int wi = wpid==pid[0] ? 0 : 1; // get index of winner
kill(pid[!wi], SIGKILL), wait(NULL); // kill and reap loser
printf("%s finished first!\n", argv[wi+1]);
return 0;
}

C - Junk characters on output

Let's say, I have two programs - input.c & output.c
All I want to do is send some payload/characters in "half pyramid" format into another one using execl() function.
input.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define SIZE 1024
int length;
int main(int argc, char *argv[])
{
pid_t pid;
char *target;
//char payload[length+1];
char payload[SIZE + 1];
int status;
int i = 0;
if(argc < 2)
{
printf("Usage %s <length> <target>\n", argv[0]);
exit(EXIT_FAILURE);
}
length = atoi(argv[1]);
target = argv[2];
while(i < length)
{
pid = fork();
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
payload[i] = 'A';
payload[i + 1] = '\0';
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
return 0;
}
Commented passages are just for debugging purposes. Because As you can see (while trying), when you want just print it, everything works properly.
output.c (or if you want 'target.c')
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[])
{
char buffer[30];
strncpy(buffer, argv[1], sizeof(buffer));
printf("Output: %s\n", buffer);
return 0;
}
When I compile input.c like:
gcc input.c -o input
& output.c:
gcc output.c -o output
Ok. Now, everything is prepared. Let's say, I'd like to send a payload - length 6
./input 6 ./output
but all I get on output is just this (or simply with another junks characters):
Output: A
Output: 0A
Output: 0,A
Output: 0,�A
Output: 0,�8A
Output: 0,�8�A
I tried so many things, but all of them failed and output was still the same, as you can see above.
I'd be very grateful if anyone can help me and possibly show me where is problem. Can be problem in using fork() and execl() functions together?
Got it, you should not update payload in the child block code...
Here's a fix (cannot test it now) :
while(i < length)
{
pid = fork();
payload[i] = 'A';
payload[i + 1] = '\0';
if(pid != 0)
{
waitpid(-1, &status, 0);
//exit(0);
}
if(pid == 0)
{
execl(target, target, payload, NULL);
//printf("%s\n", payload);
}
++i;
}
[removed unrelated sentence]
EDIT (additional explanations) : payload updating must be in both parent and child code. If you don't understand why, I can add more explanation.
EDIT2 (as requested). You want update payload for the next forked child process. In your code, all child code is replaced by execl() into target code. So the fork() is executed only by the first parent process (the root one).
You have to update payload by the first parent and make it accessible too all the children. Putting it into this block won't work either :
// block only executed by the first parent.
if(pid != 0)
{
waitpid(-1, &status, 0);
}
Therefore, You must update it in a place both accessible by the parent and the child : after the fork(), before if(pid == 0) block.
In your code, you increment i in the common block, but the parent's payload is never updated. So in the child block, just before the execl(), your adding 'A\0' at the end of an uninitialized C string.
When your program forks, it creates a new process. This new process, after if(pid == 0), alters the payload, and runs exec (ie executes output, which then dies. I.e, your change of payload stays in the child, and does not affect the parent process. Only ++i does, which is why you're seeing unitialized data.
Move payload change before fork (or at least out of child-only block), so it's in the parent as well.

Resources