waitpid() hanging when foreground child process stopped SIGTSTP - c

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

Related

Why will signal TSTP received by sigchld_handler instead of sigtstp_handler?

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

How could I know if all processes have been already created before sending signals?

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

Kill() error: no such process?

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.

Fork Issue - After executing fork(), commands before fork are also running twice

Here is the output ---
Parent: my pid is 4525
Parent: my parent's pid is 3350
parant started- 4525 3350
Before Fork
Before Fork
Child 4526 4525
In parent
---Parent End---
When I try to execute the below code ---
void main(int argc, char *argv[])
{
int status;
pid_t my_pid, parent_pid,child_pid;
my_pid = getpid();
parent_pid = getppid();
printf("\nParent: my pid is %d", my_pid);
printf("\nParent: my parent's pid is %d", parent_pid);
printf("\nparant started- %d %d",my_pid,parent_pid);
printf("\nBefore Fork");
if((child_pid = fork()) < 0 )
{
perror("fork failure");
exit(1);
}
if(child_pid == 0)
{
printf("\n Child %d %d\n",getpid(),getppid());
}
else
{
printf("\nIn parent");
wait(&status);
printf("\n---Parent End---\n");
}
}
Why is the Before Fork is printing twice?? Thanks
It's because you're not flushing the output buffer prior to the fork(). Change to:
printf("\nBefore Fork\n");
or:
printf("\nBefore Fork");
fflush(stdout);

Ptrace/wait on a non child

int Enable ( int pid)
{
int status;
#if 1
{
printf ( "child pid = %d \n", pid );
long ret = ptrace (PTRACE_ATTACH, pid, NULL, NULL);
do {
int w = waitpid(-1, &status, 0);
if (w == -1) {
perror("waitpid error :");
exit(EXIT_FAILURE);
}
if (WIFEXITED(status)) {
printf("exited, status=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("killed by signal %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("stopped by signal %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("continued\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS);
}
#endif
// while ((result = wait(&status)) != -1 && result != pid){ printf (" this is not my child go back \n"); };
}
int main(int arg, char*argv[])
{
Enable(atoi(argv[1]));
sleep(125);
}
-- I ran a daemon with pid 6841 and tried to wait on it after ptrace-attach
./ptrace 6841
child pid = 6841
waitpid error :: No child processes
In short I want to be able to wait on a non-child process - any other program welcome.
oops. not working if Iam not root :)
This is documented behavior; see ptrace() - Unix, Linux System Call e. g.
non-root processes cannot trace processes that they cannot send
signals to
if(ret == 0)
{
//child process
}
else
{
//parent process
}

Resources