Prevent control-c from sending SIGINT to all process group children - c

I'm trying to interface with a really crappy, completely opaque API that creates two subprocesses within a POSIX-like environment (OS X/Linux) in C. Basically, it starts an external program and provides rudimentary support for passing messages back and forth. The process tree looks something like this:
+ My_program
\
+ an initiation shell script (csh -f -c external_program_startup_script)
\
- the external program instance
When I press control-c in the terminal while My_program is running, the controlling terminal sends SIGINT to all processes in its process group — all three above processes. I want SIGINT to get to the program instance, but if it also hits the shell script then that middle process is terminated and the communication link is severed.
Within My_program, I can setup a signal handler to ignore SIGINTs. But I have absolutely no control over the two child processes (the API doesn't even expose their PIDs), so existing solutions such as changing their process group or attaching handlers won't work. Is there a way to prevent the controlling terminal from sending SIGINT to all processes in the foreground process group?
(The API in question is MATLAB's libeng, which allows an external C program to process commands within MATLAB. But it has absolutely no functionality for sending interrupts beyond that which the OS provides.)

You need to run sub-processes in separate control group. You can achieve that by proper usage of setpgid() function. After fork() call, in the child process run setpgid(), then exec the program you want to be run in separate group.

Related

Check exit code of an unrelated process

I want to check the exit code of a foreground process using C code running on linux. As I understand, wait() and waitpid() are useful for child processes, but in my case, it is a foreign process. I am able to read information from /proc/<pid>/stat for that process while it is active, but as the process closes, reading from /proc/<pid>/ becomes problematic and I didn't find any information relating to exit code.
Other things I've tried:
popen() some bash commands. echo $? always returned 0, even when process of interest exited with an error code. I am not sure it targeted the process of interest. Another bash command I tried to call, was wait <pid> but this command returned immediately, while the process was still running.
If you have access to the foreground process' code, you can send a message via a message queue or even a socket (e.g. udp multicast) - and that will make the solution more general (your c program can run on a different machine).
Another option is to use a loggging service (syslog or something like that). it has some
useful interfaces that enable processes to log their exit codes.

Why the parent should die, in the process of creating a Daemon

" Thus, the common method for launching a daemon involves forking once or twice, and making the parent processes die while the child process begins performing its normal function."
I was going through OS concepts and I didn't understand the above said lines.
Why the parent process will be made to exit( or parent dying ),in the process of creating a Daemon?
Can someone pls explain me.
Traditionally, a daemon process is defined as a process whose parent is the system's init process and which runs in the background. For instance, if you were to execute some program in your terminal, your shell would create a process (either in the foreground or background) and the program would run with your shell as its parent. This is an example of a non-daemon process because its parent is your shell process.
So how do you produce a process whose parent is the init process? Well, a process whose parent process dies before it (the child) has exited becomes an orphan process. An orphan process will in turn be re-parented to the init process. Voila, the process now meets the definition of a daemon.
Tying this back to your quote, if you were to fork once and then kill the parent, you achieve the desired effect. Likewise, if you fork once and then have that child fork another process, followed by killing the first child, you also achieve the desired effect while keeping the (now grandparent) process alive.
This is not a requirement, as any background process could be a daemon. Technically a daemon process in one that runs to operate some general non interactive task. In Unix environment, a daemon is generally set as a process that have some characteristics: no controlling terminal, no umask, particular working directory, etc. Forking twice is a common way to obtain the grandchild to be inherited by init process and have the former properties, in some way to get a process fully detached of any user control (except root of course).
This applies only if a standard user want to create a daemon. Some other standard daemons are created almost normally (see init, launchd, etc)
If the parent exits while the daemon continues running, the daemon is orphaned, and the init process typically adopts it (i.e. becomes the parent).
There are some exceptions, but it is normally expected that a daemon process will be descended from the init process (e.g. the init process will launch daemons during system startup). So, if another process launches a daemon and terminates, it achieves the desired effect.
Note that some other actions are also needed, such as disassociating the daemon from any tty window.
Other answers already explained what happens when parent dies i.e. child is adopted by init process.
But why above is required to make a process daemon? A daemon by definition is non-interacting program i.e. it should not be associated with a terminal. That ensures that daemon continues to work in background even when user sends signals by Control-C, hangup etc. Now, how to prevent a process from ever attaching to a terminal? Make init it's parent by killing original parent.
init is a special process because:
It's not attached to any terminal.
It's first process (pid 1) after booting OS, and that makes it leader of it's session. Note that every UNIX process belongs a process group and that in turn belongs to a session. First process in the session becomes session leader.
In UNIX, only session leader can attach to (or control) terminal. As soon as you make init parent of your process, it joins init's session. Since init is the session leader, your process can never be the leader and hence can never attach to a terminal. That's what we wanted, right?
There are other ways to detach terminal e.g. calling setsid but that's not part of this discussion.

Outputting both stopped and Running processes in linux?

I'm presently using a Linux command in my c program to show a group of processes. When a process is stopped (suspended) though, this command does not list it. I need to list both running AND stopped jobs. Note: When I say stopped, I do NOT mean terminated jobs. The issue is with displaying suspended processes.
execvp("/bin/ps", parmList);
I have to use the ps command. Is there anyway to show both running and stopped (suspended) processes in this situation?
There are two ways to go about it:
1) Continuously monitor processes for change in state.
2) Register a handler to get notified asynchronously, when state changes.
1st case
execute a script that continously monitors process states.
You have to use ps with BSD style options ie
ps axo pid,stat
2nd case
you can only monitor the child processes.
you can monitor them by using waitpid() provided they are child processes.
So, basically you register a handler for signals and in the handler you use waitpid to get the status.
You will find the signals explained here:
[http://linux.die.net/man/2/waitpid][1]

Default behavior of a Unix-based shell with respect to child processes on exit?

I am implementing a basic Unix-based shell. The shell maintains a list of child processes running in background and stopped processes.
If a shell is exited:
Does the shell kill all the child processes running/stopped in the background?
Or
Does the shell simply ignore the child processes existing in the background or in stopped state, only to be later adopted by init?
Note that I need to know the exact behavior of the Unix shells. I found an answer here but still it doesn't talk about the exact implementation of Unix shells.
Read more about the SIGHUP signal. This signal is typically sent to all processes in a process group whenever their controlling terminal exits. In the case of a shell, this usually happens when the terminal window is closed. The default behaviour of the signal is to terminate the process. That is why programs such as nohup exist.
In order to write a real shell, you really have to understand the whole TTY subsystem and how signals are related. A recommended article is "The TTY Demystified".
Actually SIGHUP (signal number is 1, (to verify kill -l)) is signal, which is used to close all the processes (we can say children) , which are running under the terminal, by sending this signal by that terminal to all processes.
The SIGHUP signal is sent to a process when its controlling terminal is closed. It was
originally designed to notify the process of a serial line drop (a hangup). In modern
systems, this signal usually means that the controlling pseudo or virtual terminal has
been closed.Many daemons will reload their configuration files and reopen their
logfiles instead of exiting when receiving this signal.nohup is a command to make a
command ignore the signal.

How can I tell if a child is asking for stdin? How do I tell it to stop that?

In bash when I run a command like wc & or cat & that wants standard in right away, it returns immediately with
[1]+ Stopped cat
How is this accomplished? How do I stop a program that I started with exec, and how do I know to stop these programs in the first place? Is there some way to tell that these programs want stdin?
Thanks!
PS also, what is the + about? I've always wondered, but that's really hard to google...
If you want spawned programs to behave similarly to how the shell works, call setpgrp() after forking your child. This will cause the background program to run in its own process group, and therefore have a detached tty. When it tries to do I/O to the console, it will receive SIGTTIN or SIGTTOU signals. The default behaviour of SIGTTIN or SIGTTOU is to stop the process just like SIGSTOP.
As the parent, you can find out whether you have stopped child processes using waitpid() and WUNTRACED.
[Edited -- see other answers for the answer to the main question]
The + sign simply refers to the current job. Each pipeline of commands (such as foo | bar | baz) is a job, which can be referred to using a jobspec beginning with the % character. %1 is job number 1, %+ is the current job, and %- is the previous job.
For more information about jobs, see the Job Control section of the Bash manual.
The setpgid() manual page explains how this works:
A session can have a controlling terminal. At any time, one (and only
one) of the process groups in the session can be the foreground
process group for the terminal; the remaining process groups are in
the background. If a signal is generated from the terminal (e.g.,
typing the interrupt key to generate SIGINT), that signal is sent to
the foreground process group. (See termios(3) for a description of
the characters that generate signals.) Only the foreground process
group may read(2) from the terminal; if a background process group
tries to read(2) from the terminal, then the group is sent a
SIGTSTP signal, which suspends it. The tcgetpgrp(3) and
tcsetpgrp(3) functions are used to get/set the foreground process
group of the controlling terminal.
So what you want to do is this:
When you create a new pipeline, call setpgid() to put all the members of the pipeline in a new process group (with the PID of the first process in the pipeline as the PGID).
Use tcsetpgrp() to manage which process group is in the foreground - if you put a pipeline in the background with &, you should make the shell's own process group the foreground process group again.
Call waitpid() with the WNOHANG and WUNTRACED flags to check on the status of child processes - this will inform you when they are stopped by SIGTSTP, which will allow you to print a message like bash does.
You can use system("command &") in the forked child process and then manually make it exit, if there are no time and priority constraints.

Resources