How to set status termination of a process C? - c

My program is a rudimental little shell.
It allow you to run programs in PATH as ls, cd..also with arguments.
To run the program type from terminal "./myshell2" then it starts and you can insert how many commands you want.
It starts a child process, runs execvp,it returns and restarts so you can type a new command.
When typed "Q" or "q" all the entire program should terminates.
The problem is that I don't know how to stop it,the code is below.
My idea is, when typed "Q" or "q", to kill the child process created and send a signal to comunicate its bad termination(of child process).
So the final status(from parent) 'll be not 1 and the function returns.
I commented some parts of the code hoping that it's easier to understand.
It works the problem is that to stop it I need of ctrl C.
I would like to say to child process that he must ends with a non-zero value.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <signal.h>
int main(int argc, char * argv[]) {
while(1)
{
pid_t pid = fork();
if (pid == -1) {
perror("fork error");
exit(EXIT_FAILURE);
}
if (pid == 0) { // child process
printf("type the command to start (and arguments if required) \n"
"Q to quit\n");
char *dest[10]; // allow you to insert
char line[4096];//commands from terminal
if (fgets(line,sizeof(line),stdin)==0) return 1;
int i;
line[strcspn(line, "\n")] = '\0';
char *st = line;
for (i=0; i< 10 && (dest[i]=strsep(&st," "))!=NULL;i++)
continue;//now you typed the command
if ( ( memcmp(dest[0],"Q",1)==0 ) // if Q or q the program
|| (memcmp(dest[0],"q",1)==0) ) //must end
{
printf("got it!\n");
if (kill(getpid(),SIGSEGV)==-1) printf("kill error\n");
//in theory the process should terminates with bad status
// and the value of the variable "status" 'll be not 0
// I think that the problem is in this part of the code
}
if( strcmp(dest[0]," ")!=0 )
{
int res = execvp(dest[0], dest);
}
else
{ int res= execvp(dest[1],dest+1);}
perror("execvp error");
exit(EXIT_FAILURE);
}
int status;
pid_t child = wait(&status);
if (child == -1) {
perror("wait error");
exit(EXIT_FAILURE);
}
if (status==1)
break; //so it can exit from the loop that creates new process
setenv("WAIT","TRUE",0); //dont' worry about
//perror("setenv error\n");
if (memcmp("TRUE",getenv("WAIT"),4) == 0 ) //these 6 lines
printf("WAIT=TRUE\n");
else if(memcmp("FALSE",getenv("WAIT"),4) == 0 )
printf("WAIT=FALSE\n");
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
return EXIT_SUCCESS;
}

You're printing out WEXITSTATUS() for all cases, but that isn't right. You need to check if the status returned by wait is an exit status or not using WIFEXITED(). If it's non-zero then the child exited normally. Otherwise, you can use WIFSIGNALED() to see if the child was terminated and you'll get the signal from WTERMSIG()
if(WIFEXITED(status))
{
printf("end current process (status=%d, child=%d)\n", WEXITSTATUS(status), son);
}
else if(WIFSIGNALED(status))
{
printf("end current process (signal=%d, child=%d)\n", WTERMSIG(status), son);
}
You really should have the parent process handle the inputting of the command and leave the child process to run it though.

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;
}

process loop in C

I'm trying to write a code about a process that executes programs from $PATH using the execlp() command.(it doesn't need to be the execlp command but I've found it useful for this one) I've achieved my expected output, but I need to run more than one commands. More specifically I want the child process to run the exec command, then the parent process to print a text indicating that it's ready to accept another command. Then the child process will run the new exec command. My code is this:
int main ( int argc, char *argp[]) {
pid_t progpid = fork(); // the fork command for the creation of the child process
int status = 0;
char com[256];
if (progpid < 0) // the check in case of failure
{
printf("PROGRAM ABORTED!");
return 0;
}
do
{
if (progpid == 0) // the child process
{
scanf( "%s", com);
if (com == "exit")
{
exit(0);
}
else
{
execlp(com, com, NULL);
}
}
else //the parent process
{
wait(&status);
printf("$");
}
}while (com != "exit");
return 0;
}
The expected output is :
<program which I input from keyboard> ( for example : ls )
<output of the program>
$<next program>
<output of the next program>
.
.
.
$exit
In short I want to keep running programs till I enter exit where it ends without doing anything else. However the output I get is this:
<program>
<output of program>
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
It keeps printing $ until I shut it down. I'm new to processes so please don't be too harsh about my code so far.
Thank you in advance!
This
if (com == "exit")
should be
if (strcmp(com, "exit") == 0)
Similarly change the while condition as well.
In C, string comparisons are done using strcmp(). == in your case, simply compares the address of com and the address of the string literal "exit". (In expressions, an array gets converted into a pointer to its first element. Hence, "address" comparison. Also see: What is array decaying?).
Note that your execlp() call has an issue. NULL may be defined as 0, in which case execlp(), being a variadic function, may be able to recognize it as the last argument.
I'd suggest to change it to:
execlp(com, com, (char*)0);
You'd also want to check if wait() failed or not by checking its return code.
Here's a simple example based on yours with improved error checking.
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main ( int argc, char *argp[]) {
for(;;) {
char com[1024];
printf("$ ");
fgets(com, sizeof com, stdin);
com[strcspn(com, "\n")] = 0; /* Remove if there's a newline at the end */
if (strcmp(com, "exit") == 0) {
exit(0);
}
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) { /* child process */
execlp(com, com, (char*)0);
}
int status;
int rc = wait(&status);
/* You can inspect 'status' for further info. */
if (rc == -1) {
perror("wait");
exit(1);
}
}
return 0;
}
Note that if you want your to execute commands with arguments then you need to do argument processing.

How to glue the system thanks to processes?

Generalities and explanations about the functioning of my program
I wrote a program whose aim is to create processes until it can't do it anymore (id est : it must glue the OS and completely fill the processes table). However, when OS is glued, a message like "fork can't be done anymore" appears, and all the processes can be killed by the final user thanks to CTRL+Z.
My program contains two important processes : the main one, which creates the second. The first is called "MAIN_P" in my code and the latter "P_ROOT". P_ROOT's aim is to fork until he can't do it anymore. When a fork error appears (id est : when my program has succeeded !), the final user can send a CTRL-Z signal to MAIN_P, which will kill P_ROOT and its children.
I precise that P_ROOT and its children have the same GPID (inheritance). But the latter is different than the MAIN_P's one, of course (setsid applied to P_ROOT).
My problem
When I launch my program, it fork the first child, which fork its children until the OS is glued (ie. : until the processes table is completely filled). The only problem is that I can't CTRL + Z in my console to stop it... And of course, if I just exit the terminal, it doesn't kill all these processes (and others continue to be forked moreover).
Thus, I don't recommend you to execute it...
What is wrong with my code ?
Source
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/resource.h>
int main(int argc, char* argv[]) {
pid_t pid_first_child = 0;
if((pid_first_child = fork()) == -1) { // We `fork` the first child, which will always `fork` (more precisely : until the OS is glued, processes table completely filled)
perror("fork");
exit(EXIT_FAILURE);
}
if(pid_first_child == 0) { // BEGINNING OF <FirstChild>'S CODE
pid_t pid_session_leader = 0;
if((pid_session_leader = setsid()) == -1) { // FirstChild is its process group's leader
perror("setsid");
exit(EXIT_FAILURE);
}
if(setpriority(PRIO_PGRP, pid_session_leader, -10) == -1) { // The priority of FirstChild (which is the group's leader)
perror("setpriority");
exit(EXIT_FAILURE);
}
unsigned children_counter = 0;
pid_t pid_calculation_process = 0;
while((pid_calculation_process = fork()) != -1) { // Now, FirstChild will `fork` until the limit ! When the limit is reached, -1 is returned : there isn't anymore `fork` and we exit the loop
if(pid_calculation_process > 0) {
children_counter++;
fprintf(stdout, "%u\n", children_counter);
} else { // BEGINNING OF <FirstChild's children>'s CODE (Why ? Consequently to the `while` and the `if` !)
float j=1;
while(1) { // Children can't die
int i = 0;
for(; i < 1000; i++) {
j /= 3;
}
usleep(1000);
}
} // END OF <FirstChild's children>'s CODE (FirstChild's children)
}
perror("fork"); // It's what we wanted ! This message will tell the user "OS is glued, program worked correctly"
exit(EXIT_SUCCESS); // `EXIT_SUCCESS` ? Because we reached the limit !
} // END OF <FirstChild>'S CODE
}
Comments:
To reach your fork() limit quickly, you have to make sure that each forked process doesn't consume too much resources. Your forked processes are spinning in the for-loop and taking up too much resources. If you remove the for-loop, you will hit your process limit more quickly since the processes will be blocked on the sleep() call instead of spinning.
You don't need the wait loop to wait for the processes to complete after the fork() error. That will happen automatically.
The updated source:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char* argv[]) {
// This (first !) child, say "P_ROOT", will create its own children, which will glue the system (thus, MAIN_P is freed
int p_root = fork();
if(p_root == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
// P_ROOT's PGID will be set to its PID (so we have : P_ROOT's PGID != MAIN_P's PGID)
if (p_root == 0) {
if(setpgid(p_root, p_root) == -1) {
perror("setpgid");
exit(EXIT_FAILURE);
}
int p_root_number_of_created_children = 0;
pid_t p_root_child = 0;
while((p_root_child = fork()) != -1) { // P_ROOT forks until it can't do it anymore...
if(p_root_child != 0) {
p_root_number_of_created_children++;
} else {
#ifdef CONSUME_RESOURCES
int i = 0;
while(i < 1000000000000000000) {
i++;
}
#endif
sleep(6000);
exit(EXIT_FAILURE);
}
}
// NOW it's impossible to create new child processes
perror("fork");
fprintf(stdout, "\nImpossible to create more children. Their number is : %d\n", p_root_number_of_created_children);
exit(EXIT_SUCCESS);
} else {
printf("Waiting, top level, root = %d\n", p_root);
wait(NULL); // MAIN_P waits for P_ROOT
char cmd = 0;
if(scanf("%c", &cmd) < 0) {
perror("scanf");
exit(EXIT_FAILURE);
}
if(cmd == '\n' && kill(-p_root, SIGKILL) == -1) {
perror("kill");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
}

Invalid commands for child process in background in C

I have the following code in C:
if ((childpid = fork()) == 0) {
if (execvp(argv[0], argv) < 0) {
//execute failed
exit(1);
}
} else if (childpid < 0) {
//fork failed
} else {
//if execvp failed don't do anything here
//else do something
}
What I want is:
I enter a command.
If it is not executable it should not do anything but wait for my next entered command.
If it is executable it should do some things in the parent process.
If I enter e.g. sleep 1m it should execute it in my child process, do things in the parent process and should be still able to execute more jobs (this works fine). But when I execute something like abcdef (invalid command) it does the stuff in my parent process anyway.
Can someone tell me how the code should look like?
I also tried the following:
void signalHandler(int signal)
{
if (signal==SIGCHLD) {
printf("Child ended\n");
wait(NULL);
}
}
//in main
signal(SIGCHLD,signalHandler);
//...
if ((childpid = fork()) == 0) {
if (execvp(t_argv[0], t_argv) < 0) {
kill(getppid(),SIGCHLD);
}
}
Is this correct?
This way I get an error afterwards (when it's finished).
waitpid(childpid, &status, WNOHANG)
tells me it finished with an error (-1).
One possible solution is to use a pair of anonymous pipes, where the child process writes in the write-end of the pipe any status it needs to pass on to the parent. Then in the parent you check the read-end of the pipe, if you don't receive anything before the child-process exits then everything was okay and the child process successfully executed the program.
If the parent does receive anything before the child process exits, then it means that the exec call failed.
One possible solution is to terminate abnormally with a signal (for example, SIGUSR1) and check for that in the parent. This assumes that whatever program you execute in the child never terminates with SIGUSR1 - a reasonable assumption in most cases, I'd say. The parent can then check the termination status of the child.
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
pid_t childpid;
if ((childpid = fork()) == 0) {
if (execvp(argv[1], &argv[1]) < 0) {
raise(SIGUSR1);
}
} else if (childpid < 0) {
perror("fork()");
exit(EXIT_FAILURE);
} else {
int term_status;
if (wait(&term_status) < 0) {
perror("wait()");
exit(EXIT_FAILURE);
}
if (WIFSIGNALED(term_status) && WTERMSIG(term_status) == SIGUSR1) {
printf("execvp failed\n");
} else {
printf("success\n");
}
}
return 0;
}
Side note: you probably want execvp(argv[1], &argv[1]), because execvp(argv[0], argv) will execute this same program over and over.
Again, this works as long as the process executed by execvp(2) never terminates with SIGUSR1. Notice that if the process executed by execvp(2) terminates with SIGSEGV or other abnormal termination condition, it is still seen as success by the parent.

Resources