Using SIGINT and SIGTSTP to handle foreground processes a unix shell - c++

I have some questions regarding the use of SIGINT and SIGTSTP in relation to managing processes in my own unix shell. But first of the code here:
void execute(vector<char *> argvv, bool x){
pid_t pid;
int status;
int error;
pid = fork();
a = pid;
argvv.push_back(NULL);
if(pid == -1){
cout << "error" << endl;
}else if(pid == 0){
error = execvp(argvv[0],argvv.data());
if(error == -1){
exit(-1);
}
// In Child Process
}else{
// If no "&", then wait for process
if(x == false){
if(wait(&status) != pid){
perror("wait()");
}
}else{
cout << "Pid des Hintergrundprozesses: " << pid << endl;
}
// in parent process
}
}
This function just receives the entered operation and parameters, forks a new process and executes it.
Now my signalhandler functions:
void signalHandlerSigInt(int signum){
cout << "Interrupt Signal (" << signum <<") received." << endl;
kill(a,SIGINT);
cout << "Killed Process: " << a << endl;
}
void signalHandlerSigTStp(int signum){
cout << "Interrupt Signal (" << signum <<") received." << endl;
kill(a,SIGTSTP);
cout << "Stop process..: " << a << endl;
}
and my main.cpp:
int main(int agc, char** argv) {
bool opBackground;
string operation;
vector<string> arguments;
vector<char *> argvv(arguments.size() + 1);
signal(SIGINT, signalHandlerSigInt);
signal(SIGTSTP, signalHandlerSigTStp);
while(true){
cout << "myshell>";
getline(cin,operation);
if(operation == "logout"){
logout();
}else{
opBackground = befehlUebersetzen(operation, &arguments);
vector<char *> argvv(arguments.size() + 1);
for(size_t i = 0; i != arguments.size(); ++i){
argvv[i] = &arguments[i][0];
}
execute(argvv, opBackground);
arguments.clear();
}
}
return 0;
}
The shell itself works fine, I now need to extend it to be able to kill the foreground process by pressing CTRL+C or stop the process with CTRL+Z.
I think I understood what a Signalhandler does, but is kill(a,SIGINT) the right way to transmit the signal SIGINT to my process? ("a" is a global variable for my forked pid, that means the last process I forked).
My problem is, when starting a process in the background and then start another process in the foreground it kills both processes when pressing CTRL+C.
Also the SIGTSTP signalhandler doesnt seem to work at all (does nothing - process just keeps running in the foreground).
Am I completely wrong with what im doing?

Related

How do I control the behavior of multiple child process using wait();

I'm trying to create a program where a parent process does some computation and sends it over to the child process, The child process forks another process and does some more computation and sends it over to the child2 process for further processing.
I can't seem to figure out how to do this.
My Output is all messed up, sometimes child 2 runs before child 1. sometimes child 1 is finished last.
I want the other processes to wait for them.
This is i think how the code should look like
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
int main(int argc, char *argv[])
{
int fd[6];
pipe(fd);
pipe(fd + 2);
pipe(fd + 4);
pid_t id = fork();
if (id == -1)
return 1;
if (id == 0)
{
wait(NULL);
pid_t id2 = fork();
if (id2 == -1)
return 2;
if (id2 == 0)
{
wait(NULL);
pid_t id3 = fork();
if (id3 == -1)
return 2;
if (id3 == 0)
{
wait(NULL);
cout << "Child 3" << endl;
// Read data from pipe and display
}
else
{
cout << "Child 2" << endl;
// Read Data from pipe and Display
// Some Computation
for (int i = 0; i < 10000; i++)
{
}
// Send Data to Child 3 through pipe
wait(NULL);
}
}
else
{
cout << "Child 1" << endl;
// Read Data from pipe
// Some Computation
for (int i = 0; i < 100000000; i++)
{
}
// Send Data to Child 2 through pipe
wait(NULL);
}
}
else
{
cout << "Parent" << endl;
// Some Computation
for (int i = 0; i < 2000; i++)
{
}
// Send Data to Child 1 through pipe
wait(NULL);
}
}
The wait call returns as soon as any child in the same process group terminates (thus including any grandchildren).
When you want to wait for a specific process, use waitpid instead. You should also check the exit code and the status.
Finally, you should wait for the child to exit after interacting with it via the pipe, not before, and you should not wait before starting the child.
#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
using namespace std;
void die(const char* msg) {
perror(msg);
exit(1);
}
int main(int argc, char* argv[]) {
int fd[6];
pipe(fd);
pipe(fd + 2);
pipe(fd + 4);
pid_t id = fork();
if (id == -1)
die("fork");
if (id == 0) {
pid_t id2 = fork();
if (id2 == -1)
die("fork 2");
if (id2 == 0) {
pid_t id3 = fork();
if (id3 == -1)
die("fork 3");
if (id3 == 0) {
cout << "Child 3" << endl;
// Read data from pipe and display
// Done. Nothing to wait for - just exit child 3
cout << "Child 3 exiting" << endl;
} else {
cout << "Child 2" << endl;
// Read Data from pipe and Display
// Some Computation
// Send Data to Child 3 through pipe
// Wait for child 3 to finish before exiting child 2
if (waitpid(id3, NULL, 0) < 0)
die("waitpid 3");
cout << "Child 2 exiting" << endl;
}
} else {
cout << "Child 1" << endl;
// Read Data from pipe
// Some Computation
// Send Data to Child 2 through pipe
// Wait for child 2 to finish before exiting child 1
if (waitpid(id2, NULL, 0) < 0)
die("waitpid 2");
cout << "Child 1 exiting" << endl;
}
} else {
cout << "Parent" << endl;
// Some Computation
// Send Data to Child 1 through pipe
// Wait for child 1 to finish before exiting parent
if (waitpid(id, NULL, 0) < 0)
die("waitpid 1");
cout << "Parent exiting" << endl;
}
}
Prints:
Parent
Child 1
Child 3
Child 2
Child 3 exiting
Child 2 exiting
Child 1 exiting
Parent exiting
Note that Child 1/2/3 are printed in random order - that's because all children run in parallel, which is perfectly normal (we want the processes to run in parallel, it's the whole point of forking). The ordering will be enforced when reading/writing the pipes - the child reading from a pipe will wait until the data arrives.

How to track termination/running of child process that is not being "waited" for?

I have an assignment where I have to write a subshell in C++. Essentially, I am forking (if the input is not jobs or exit) and in the child process, I am calling exec to run the command. It needs to run alongside the parent process, so for example if I call sleep 100, the program should immediately be ready to run the next command since the parent is not "waiting" for it since I am using WNOHANG. However, my issue is when I need to track the actual state- if sleep is still running, I want to be able to get that the command is still running, but I am unable to do so. It always shows up as exited, when I use the various macros. I have no idea how to approach this differently and would appreciate some guidance.
(i didn't include the declarations of the variables since the editor on this website was messing it up for some reason)
do{
cout << "# ";
getline(cin, input);
if (input == "jobs")
{
cout << "Process ID | State | Command " << endl;
cout << "-----------+--------------+-----------------------------------------------" << endl;
//jobs stuff goes here, need to print pids, states, and commands
if(counter == 0)
{
cout << " [NO PROCESSES] " << endl;
}
else
{
for(i = 0; i < counter; i++)
{
cout << pidArray[i] << " ";
cout << statusArray[i] << " ";
cout << cmdArray[i] << endl;
}
}
}
else
{
cmdArray[i] = input;
i++;
counter++;
pid = fork();
if( pid == 0)
{
execl("/bin/sh", "sh", "-c", input.c_str(), (char *)0);
//break;
}
else if (pid == -1)
{
break;
}
else
{
pidArray[i-1] = pid;
//int rc = waitid(P_PID, pid, NULL, WNOHANG);
// int rc =waitpid(pid, &status, WNOHANG | WNOWAIT );
//exitChecker = waitpid(pid, &status, WNOHANG);
usleep(100000);
if (WIFEXITED(status))
{
cout << "terminated" << endl;
}
else
{
cout << "running" << endl;
}
}
}
}while(input != "exit");
return 0;
}
Thanks in advance.

Need help in implementing conditional outputs using the fork() function

In the below program i'm trying to implement these conditions:
I'm trying to only implement the first child process to print “hi”?
and the root process to print “areyou”?
and the final child process must exit from the system without doing anything?
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
pid_t pid1,pid2,pid3,pid4;
int function(){
pid1=fork();
if(pid1>0)
{
cout << "hi" << getpid()<<" " << getppid()<< endl; /*first child process should print "hi"*/
}
pid2=fork();
cout << "hell" << getpid()<<" " << getppid()<< endl;
pid3=fork();
cout << "how " <<getpid() <<" "<<getppid() <<endl;
pid4=fork();
if(pid4>0){
return 0;/* final child process should exit from the system with out doing anything*/
}
else{
cout << "areyou "<<getpid()<<" "<<getppid()<<endl;
}
}
int main() {
/* and the root process should print "are you"*/
function();
}
-with if(pid1>0) i guess i tried to implement first child to output "hi" and i feel i'm lost in understanding how can i get only the root parent process to print "areyou", and how to control the last child to exit with out doing anything
You may do something like
void function()
{
pid_t pid1, pid2, pid3, pid4;
pid1 = fork();
if (pid1 == 0)
{
// first child process should print "hi"
cout << "hi " << getpid() << " " << getppid()<< endl;
}
pid2 = fork();
cout << "hell " << getpid() <<" " << getppid() << endl;
pid3 = fork();
cout << "how " << getpid() <<" "<<getppid() << endl;
pid4 = fork(); // Mostly useless as only parent print something for this one
if (pid1 == 0 && pid2 == 0 && pid3 == 0 && pid4 == 0){
return; // final child process should exit from the system with out doing anything
} else if (pid1 > 0 && pid2 > 0 && pid3 > 0 && pid4 > 0){
cout << "areyou "<< getpid() << " "<< getppid() << endl;
}
}
Demo
with if(pid1>0) i guess i tried to implement first child to output "hi"
No, it's the parent that gets a positive pid (on success), because it gets the process-id of the child it just forked, or -1 if the fork call fails. The child receives a return value of 0.
What you want to do goes like this:
if(pid1 < 0)
{
cout << "fork failed to create a child process."
}
else if (pid1 > 0) // Parent
{
cout << "areyou";
}
else // child
{
cout << "hi";
}

Semaphores failing to wait, with reason "Resource temporarily unavailable"

I'm trying to have a parent process wait on multiple child processes signalling, before the parent continues, using an array of unnamed semaphores (one per child process). However, when using sem_wait(), the parent process waits indefinitely, while sem_trywait() returns a "resource temporarily unavailable" error, and continues without the child processes having signalled. Neither sem_init() nor sem_post() return an error.
Relevent portion of the code:
int numsems = concurrent_instrs.size();
std::cout << "Num sems: " << numsems << "\n";
// create semaphores
sem_t* sems = new sem_t[numsems];
for (int i = 0; i < numsems; i++)
{
if (sem_init(&sems[i], 1, 0) < 0)
{
perror("sem initialization failed");
exit(1);
}
}
int child_num = 0;
// perform all calculations in block concurrently
for(unsigned int i = 0; i < concurrent_instrs.size() && !isChild; i++)
{
int pid = fork();
if (pid == -1)
{
perror("Error forking:");
exit(1);
}
if (pid == 0)
{
isChild = true;
instr = concurrent_instrs[i];
}
else
{
child_num++;
}
}
if (isChild)
{
std::cout << "Child process " << child_num << " calculating: " << instr << "\n";
perform_calculation(instr, input_vars, internal_vars, shm_input, shm_internal);
std::cout << "Child process " << child_num << " finished calculating\n";
if (sem_post(&sems[child_num]) < 0)
{
perror("Child signal failed");
}
std::cout << "Child "<< child_num << " signalled\n";
// detach from shared memory
if (shmdt(shm_input) < 0)
{
perror("child shm_input detach failed");
}
if (shmdt(shm_internal) < 0)
{
perror("child shm_internal detach failed");
}
exit(0);
}
else
{
// parent waits for all children to finish
for (int i = 0; i < numsems; i++)
{
std::cout << "Waiting on subprocess " << i << " of " << numsems << "\n";
if (sem_trywait(&sems[i]) < 0)
perror("Parent wait failed");
else
std::cout << "Parent wait " << i << " working\n";
}
std::cout << "Finished waiting\n";
// destroy semaphores
for (int i = 0; i < numsems; i++)
{
if(sem_destroy(&sems[i]) < 0)
{
perror("Sem destroy failed");
exit(2);
}
else
{
std::cout << "Sem " << i << " destroyed\n";
}
}
delete[] sems;
}
Am I setting something up incorrectly, or just misunderstanding how to use semaphores in this situation?
Edit to add: sem_wait() encounters the error regardless of whether the child processes call sem_post() before or after the wait.
The semaphores allocated with sem_t* sems = new sem_t[numsems]; are not in the shared memory. Therefore each process has its own copy, and posting in the child doesn't affect the parent.
The parent's copy remains locked. sem_trywait fails with EAGAIN, which translates to resource temporarily unavailable explanation.

How to get back to shell after SIGTSTP

I am writing on a UNIX shell.
When CTRL-C is pressed, the SIGINT signal is sent. (Is working!)
But when CTRL-Z is pressed, the process which gets the signal is stopped, but I cannot return to my shell. Only if I close the process, i can return.
Here is my signal_handler():
// in the main()
signal(SIGINT, signal_handler);
signal(SIGTSTP, signal_handler);
// when pid == 0, right over execvp()
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
// signal handler
void signal_handler(int signum) {
switch(signum) {
case SIGINT:
cout << "[caught SIGINT]" << endl;
kill(pid, SIGINT);
break;
case SIGTSTP:
cout << "[caught SIGTSTP]" << endl;
kill(pid, SIGTSTP);
break;
default:
break;
}
}
pid_t pid; // is a global variable
// this would be my main()
if((pid = fork()) < 0) {
cout << "Error" << endl;
exit(1);
} else if(pid == 0) { // child
setpgid(0,0);
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
execvp(arguments[0], &arguments[0]);
cout << "Argument not found" << endl;
exit(1);
} else if(checkIfBackground(inputNew) == false) { // mother
int status;
pid_t pid_r;
if(waitpid(pid, &status, 0) < 0) {
cout << "Error" << endl;
}
} else {
cout << "Process is in background" << endl;
}
You'll need to check the status of your children to see when they've stopped with the UNTRACED flag:
pid_t child = waitpid(-1, &status, WUNTRACED | WCONTINUED);
if (WIFSTOPPED(status))
... the child has stopped
else if (WIFCONTINUED(status))
... the child has been continued
else if (WIFEXITED(status))
... the child has exited with exit
else if (WIFSIGNALLED(status))
... the child exited with a signal
Note that if you don't care about switches to running status (the WIFCONTINUED case), you don't need the WCONTINUED flag.