This is my own shell program.
The TSTP signal is sent by this part of main function, this c file will be ran as a program in the shell.
When the c program runs, it's in a forked child process, when it sends TSTP to itself, the expectation is that the signal should be received by tstp-handler (the printf should be showing up) and then the child process stopped.
However, it's the child-handler that received a signal(17), i.e the printf in sigchld_handler was called without printf in sigtstp_handler being called first. It's the parent process that gets received the sigchld signal, how can I make the child process itself receive the TSTP signal?
All handlers registered correctly as CTRL+Z will invoke the tstp-handler correctly.
Can anyone please help me with this part? Very much appreciate it.
If you need all the source code, please find it here
// the program sending the signal
int main(int argc, char **argv)
{
int i, secs;
pid_t pid;
if (argc != 2) {
fprintf(stderr, "Usage: %s <n>\n", argv[0]);
exit(0);
}
secs = atoi(argv[1]);
for (i=0; i < secs; i++)
sleep(1);
pid = getpid();
// printf("main process id: %d\n", pid);
if (kill(-pid, SIGTSTP) < 0)
fprintf(stderr, "kill (tstp) error");
exit(0);
}
void eval(char *cmdline){
char *argv[MAXARGS];
char buf[MAXLINE];
strcpy(buf, cmdline);
int bg = parseline(buf, argv);
if(argv[0] == NULL) return;
if(builtin_cmd(argv)){
return;
}
/* not built-in commands */
pid_t cpid;
sigset_t mask, prev_mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &prev_mask);
cpid = fork();
if(cpid == 0){ // child
setpgid(0, 0);
sigprocmask(SIG_UNBLOCK, &mask, NULL);
Execve(argv[0], argv, environ);
}else{
addjob(jobs, cpid, (bg? BG: FG), cmdline);
sigprocmask(SIG_SETMASK, &prev_mask, NULL);
if(bg){
printf("[%d] (%d) %s", pid2jid(cpid), cpid, cmdline);
/* when bg job is done, how is the child process reaped? => sigchld_handler will be triggered */
}else{
/* fg job, parent process blocked;
delete fg job after it's complete(in sigchld_handler)
*/
waitfg(cpid);
}
}
}
void sigtstp_handler(int sig){
//printf("=== in sigtstp_handler, sig: %d, from %d\n", sig, getpid());
pid_t foreground_pid = fgpid(jobs);
if(foreground_pid == 0) return;
struct job_t *fg_job = getjobpid(jobs, foreground_pid);
if(fg_job != NULL && fg_job->state == FG){
pid_t pgid = getpgid(fg_job->pid);
printf("Job [%d] (%d) stopped by signal %d\n", fg_job->jid, fg_job->pid, sig);
Kill(-pgid, SIGTSTP);
}
}
void sigchld_handler(int sig){
// printf("=== in sigchld_handler: %d\n", sig);
pid_t cpid;
int status;
if((cpid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0){
if(WIFEXITED(status)){
// printf("in sigchld_handler, terminated cpid: %d\n", cpid);
deletejob(jobs, cpid);
}
if(WIFSTOPPED(status)){
struct job_t *stopped_job = getjobpid(jobs, cpid);
stopped_job->state = ST;
}
if(WIFSIGNALED(status)){
deletejob(jobs, cpid);
}
}
}
Related
I am new in using signals on C programming.
I wrote two simple source codes child.c and parent.c in order to demonstrate my issue.
child.c
void errorHandler(char* message);
void sigterm_handler(int signum, siginfo_t* info, void* ptr);
int main(){
struct sigaction action1;
memset(&action1, 0, sizeof(action1));
action1.sa_sigaction = sigterm_handler;
action1.sa_flags = SA_SIGINFO;
if(sigaction(SIGTERM, &action1, NULL) != 0)
errorHandler("Signal sigterm_handler registration failed");
for(int i = 0; i < 10; i++){
printf("%d\n", i);
if(i == 5){
if(raise(SIGSTOP)!= 0)
errorHandler("Raise SIGSTOP failed");
}
}
return EXIT_SUCCESS;
}
void errorHandler(char* message){
printf("Error: %s: %s\n", message, strerror(errno));
exit(EXIT_FAILURE);
}
void sigterm_handler(int signum, siginfo_t* info, void* ptr){
printf("Child: Process %d finishes\n", getpid());
exit(EXIT_SUCCESS);
}
parent.c
int main(int argc, char* argv[]){
int status;
pid_t mem;
pid_t child = fork();
if(child == 0){
char* arr[] = {"./child", NULL};
execv(arr[0], arr);
}
else if(child > 0){
mem = child;
waitpid(mem, &status, WUNTRACED);
}
if(WSTOPSIG(status)){
printf("Sending SIGTERM to child\n");
kill(mem, SIGTERM);
waitpid(mem, &status, 0);
}
return 0;
}
When I run parent.c, the program print into stdout:
1
2
3
4
5
Sending SIGTERM to child
but then the program get stuck, probably because sigterm_handler don't invoke, instead of printing "Child: Process *** finishes".
I tried to read on the linux manual page but I still can't to figure it out.
Can anyone please explain to me what is causing this issue?
Any answer would be appreciated!
The problem is, that the child process, that calls
if(raise(SIGSTOP) != 0)
is still stopped, when the parent here
kill(mem, SIGTERM);
sends the signal SIGTERM. The signal is not lost, but is still pending at the child's process and will be delivered, as soon as the process continues to run. You can achieve that by issuing
kill(mem, SIGCONT);
directly after sending the SIGTERM. Then, the child will resume to run, the signal will be delivered and the handler will be executed, printing the diagnostic message and exiting the process.
I'm implementing a simple shell where a child process, with it's own PGID, is given terminal control.
Problem is, when I stop the child process with SIGTSTP (ctrl + z), the terminal just hangs and gets stuck in waitpid:
How do I get waitpid to stop hanging?
void wait_job() {
int status;
int corpse;
int wait_flags = WUNTRACED | WCONTINUED;
while ((corpse = waitpid(-1, &status, wait_flags)) > 0) {
if (WIFSTOPPED(status)) {
printf("child stopped, sign num = %d, corpse = %d\n", WSTOPSIG(status), corpse);
}
else if (WIFEXITED(status)) {
printf("child exited, sign num = %d, corpse = %d\n", status, corpse);
}
else if (WIFCONTINUED(status)) {
printf("child continued, sign num = %d, corpse = %d\n", status, corpse);
}
}
}
int main() {
signal(SIGINT, SIG_IGN);
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
char buffer[100];
char prompt[] = "Press enter: ";
while (1) {
write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);
int n = read(STDIN_FILENO, buffer, 10);
buffer[n - 1] = '\0';
int pid;
if ((pid = fork()) == 0) { // Child
signal(SIGINT, SIG_DFL);
signal(SIGTSTP, SIG_DFL);
signal(SIGTTIN, SIG_DFL);
signal(SIGTTOU, SIG_DFL);
setpgid(pid, pid);
tcsetpgrp(STDIN_FILENO, pid);
char* argv[] = { "sleep", "5", NULL };
execvp(argv[0], argv);
exit(0);
}
// Parent
setpgid(pid, pid);
tcsetpgrp(STDIN_FILENO, pid);
wait_job();
tcsetpgrp(STDIN_FILENO, getpgid(0));
}
}
Here I want to send SIGINT signal to parent while it is sleeping. I have tried it by writing following the program. In this program, I am not getting why the signal handler for SIGINT from the parent is not executing at all?
here is the code:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void sig_usr(int signo){
if(signo == SIGINT)
printf("Signal caught!");
return;
}
int main(void){
pid_t pid, ppid;
ppid = getpid();
printf("ppid = %d\n", ppid);
if((pid = fork()) == 0){
printf("killing parent...\n");
kill(ppid, SIGINT);
printf("After killing parent...\n");
}
else{
sleep(5);
printf("%d %d ",ppid, pid);
if(signal(SIGINT,sig_usr) == SIG_ERR)
printf("Signal processed ");
}
return 0;
}
Output:
The output is printing only this much content. I think parent is not executing at all.
You need to set the signal handler before SIGINT is sent to the parent process, otherwise, the handler will not be executed. Also, the parent process is being killed before it executes anything. The easy way to fix this would be to move the sleep call after the code for the parent process, and add a delay to the child process.
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void sig_usr(int signo){
if(signo == SIGINT)
printf("Signal caught!");
return;
}
int main(void){
pid_t pid, ppid;
ppid = getpid();
printf("ppid = %d\n", ppid);
if((pid = fork()) == 0){
sleep(1); // Wait for parent to finish setting up
printf("killing parent...\n");
kill(ppid, SIGINT);
printf("After killing parent...\n");
}
else{
printf("%d %d ",ppid, pid);
if(signal(SIGINT,sig_usr) == SIG_ERR)
printf("Signal processed ");
sleep(5); // Wait to be killed
}
return 0;
}
When you send SIGINT signal has not been called yet.
I think you want to set signal handler before sending SIGINT:
#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>
void sig_usr(int signo){
if(signo == SIGINT)
printf("Signal caught!");
return;
}
int main(void){
pid_t pid, ppid;
ppid = getpid();
printf("ppid = %d\n", ppid);
if((pid = fork()) == 0){
sleep(1);
printf("killing parent...\n");
kill(ppid, SIGINT);
printf("After killing parent...\n");
}
else{
printf("%d %d ",ppid, pid);
if(signal(SIGINT,sig_usr) == SIG_ERR)
printf("Signal processed ");
sleep(5);
}
return 0;
}
I'm writing a Unix program where the parent process has to send signals to children and a grandson. How could I know if all processes have been already created before sending signals? Because sometimes they don't exist yet. Thanks a lot!
void t(int sig)
{
kill(SIGKILL, pidc1);
kill(SIGKILL, pidc2);
kill(SIGKILL, pidg2);
kill(SIGKILL, pidc3);
}
void handler()
{
write(1, "Signal SIGUSR1\n", 15);
}
pid_t pidc1, pidc2, pidc3, pidg2;
int main(int argc, char **argv)
{
struct sigaction action;
int status;
action.sa_flags = 0;
action.sa_handler = handler;
sigaction(SIGUSR1, &action, NULL);
pidc1 = fork();
if(pidc1 == 0)
{
printf("Child 1\n");
}
pidc2 = fork();
if(pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if(pidg2 == 0)
{
printf("Grandson 2\n");
}
wait(&status);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n");
}
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
signal(SIGALRM, t);
alarm(10);
wait(&status);
}
Preliminary note: The child code parts in your example program fall through to their parent's code, which is certainly not intended; I'll assume something like return sleep(5); at the end of each block. Also note that the printf()s may malfunction with fork()s and buffered output.
Barmar wrote:
If you need to wait for the grandchild processes to be created, you need some kind of communication from the child to the parent, so it can send the grandchild's PID. Shared memory and a mutex would be a way to do this.
That's absolutely correct. (The direct children are no problem, since the parent knows their PIDs.) Another way to communicate the grandchild's PID is a pipe; your example main() could become:
int main(int argc, char **argv)
{
int status;
sigaction(SIGUSR1, &(struct sigaction){.sa_handler = handler}, NULL);
setbuf(stdout, NULL); // printf() may malfunction without this
pidc1 = fork();
if (pidc1 == 0)
{
printf("Child 1\n"); return sleep(5);
}
int pipefd[2];
pipe(pipefd); // for communicating the grandson's PID
pidc2 = fork();
if (pidc2 == 0)
{
printf("Child 2\n");
pidg2 = fork();
if (pidg2 == 0)
{
printf("Grandson 2\n"); return sleep(5);
}
write(pipefd[1], &pidg2, sizeof pidg2); // write pidg2 to write end
wait(&status); return sleep(5);
}
pidc3 = fork();
if(pidc3 == 0)
{
printf("Child 3\n"); return sleep(5);
}
read(pipefd[0], &pidg2, sizeof pidg2); // read pidg2 from pipe's read end
kill(pidg2, SIGUSR1);
kill(pidc3, SIGUSR1);
}
I have to insert an odd number by terminal. After this, it generates two processes, A and B.
Then it sends SIGUSR2 signal to B, and his handler prints the reciprocal of the argv[1]. Then, B sleeps for argv[1] seconds and sends SIGUSR1 signal to A process before terminating. The SIGUSR1 handler for process A prints something and then terminates.
The problem is that SIGUSR1 handler for process A doesnt' work because the signal couldn't be sent by SIGUSR2 handler for process B. In fact, the kill(A,SIGUSR1) tells that there is no such process (for process A). After setting the signal handler in process A, it is in pause().
Can anyone help me to solve? Thank you.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int arg;
int pid1 = 11, pid2 = 12;
void sigusr2Handler1(int);
void sigusr1Handler2(int);
int main(int argc, char* argv[])
{
if(argc != 2){
printf("Usage: %s num(int)\n", argv[0]);
exit(1);
}
arg = atoi(argv[1]);
pid1 = fork();
if (pid1 != 0)
pid2 = fork();
if (arg%2 != 0) {
if (pid1 == 0) {
if (signal(SIGUSR1, sigusr1Handler2) == SIG_ERR) {
printf("PID %d can't catch SIGUSR1\n", getpid());
exit(1);
}
printf("PID1 %d sigusr1 handler2 installation\n", getpid());
pause();
}
if (pid2 == 0) {
signal(SIGUSR2, sigusr2Handler1);
printf("PID2 %d sigusr2 handler installation\n", getpid());
kill(0, SIGUSR2);
}
}
return 0;
}
void sigusr2Handler1(int sig)
{
printf("PID %d Received SIGUSR2. 1/%d = %f.\n", getpid(), arg, (float)1 / arg);
sleep(arg);
if (kill(pid1, SIGUSR1) < 0) {
perror("Kill error");
exit(1);
}
printf("PID %d. Sent SIGUSR1 to %d. Closing\n", getpid(), pid1);
exit(0);
}
void sigusr1Handler2(int sig)
{
printf("PID %d Received SIGUSR1. Closing.\n", getpid());
exit(0);
}
pid1 has been killed by the time pid2 attempts to send a it a SIGUSR1. pid2 is the killer.
When pid2 issues a kill(0, SIGUSR2), this sends SIGUSR2 to the entire process group, including pid1. This kills pid1, which is unprepared to receive a SIGUSR2.