Implementing shell-like job control in C - c

I am trying to implement simple shell in C language and i am having a hard time implementing job control. Everything online seems complicated enough and i think some simplicity is always good. So let me ask this ... After fork() is called can i handle Ctrl-Z signal with just 2 function and just with the pid ?
I want to call a function e.x. put_background(pid_t pid) when i hit Ctrl-Z and make process with pid = pid to run background and finally call another function e.x. put_foreground(pid_t pid) when i write fg and i want the process with pid = pid to go to foreground again.
So, is this possible? Any help is appreciated.. code more however.

I am trying to implement simple shell in C language and i am having a
hard time implementing job control. Everything online seems
complicated enough and i think some simplicity is always good.
So let
me ask this ... After fork() is called can i handle Ctrl-Z signal with
just 2 function and just with the pid ?
Note that Ctrl-Z is meaningful primarily to the terminal driver. It causes a SIGTSTP to be sent to the foreground process group of the terminal in which that character was typed -- that is, the process group that has that terminal as its controlling one, and has permission to read from it. By default, this causes the processes in that group to stop, but that's it. You don't need to do anything to achieve that.*
I want to call a function e.x. put_background(pid_t pid) when i hit
Ctrl-Z and make process with pid = pid to run background and finally
call another function e.x. put_foreground(pid_t pid) when i write fg
and i want the process with pid = pid to go to foreground again.
By definition and design, at most one process group has control of a given terminal at any particular time. Thus, to move a foreground job to the background, all you need to do is move a different one to the foreground. That can be the shell itself or some other job under its control. The tcsetpgrp() library function accomplishes this. Unless it's the shell itself, you would also want to send a SIGCONT to that process group in case it was stopped.
You additionally need a mechanism to resume a stopped background job, but that's easy: just send that process group a SIGCONT.
So, is this possible? Any help is appreciated.. code more however.
Well sure, you could write one function for moving a job to the foreground and resuming it, and one for resuming a background job. The only information these functions need about the jobs they operate on is their process group IDs (which is the same as the process IDs of their initial processes).
But you also need to maintain some bookkeeping of the current active jobs, and you need to take some care about starting new jobs, and you need to monitor current jobs -- especially the foreground job -- so as to be able to orchestrate all of the transitions appropriately.
The GLIBC manual has an entire chapter on job control, including a substantial section specifically on implementing a job-control shell. This would probably be useful to you even if you are not writing for a GLIBC-based system. The actual code needed is not all that complicated, but getting it right requires a good understanding of a fairly wide range of concepts.
*But you do need to ensure that your shell puts commands it launches into process groups different from its own, else a Ctrl-Z will stop it, too.

Related

Managing stdout/stdin when writing a Linux shell

I am working on a school project, and though it's not required, I want to implement this functionality. With that said, I can't share code, but I think it's irrelevant in this case.
When using fork(), my understanding is that the child process created inherits stdin and stdout, as the child inherits all the file streams from the parent.
My shell requires background capability, and while it technically already has that, if the "background" program runs, it still receives all the data from stdin and continues output to the screen which is just a jumbled mess. For the record, my instructor's compiled sample shell does the same thing, but I don't want that to happen!
I'm pretty certain I should be using a combination of pipe(), fork(), and dup2(), but I can't put it all together. I understand fork, but I don't understand how pipe or dup2 works and how I should implement it in the shell. I'm thinking something along these lines:
thePipe[2] = pipe();
pid = fork();
close stdin/out on child somehow if backgrounded
But I don't understand the functionality of pipe() or dup2() so I'm stuck.
Thanks!
You don't want pipes here. Processes run in an interactive shell should share their standard file descriptors with the shell — doing otherwise would break a lot more things (including the child processes' ability to determine they're running interactively, and to interact with the tty to handle things like window size changes). It'd also seriously complicate pipelines. Don't do it.
The missing piece here is process groups, which are described in the "General Terminal Interface" section of the Open Group UNIX specs. In brief, the kernel can be made to explicitly recognize a "foreground process group" for the terminal. If a process that isn't in this group tries to read from or write to the terminal, it is automatically stopped.
A brief walkthrough of what is necessary to make a properly functioning shell is available as part of the GNU libc manual, under "Implementing a Job Control Shell". Try following their instructions and see how that goes.

Is it possible to adopt a process?

Process A fork()s process B.
Process A dies and therefore init adopts B.
A watchdog creates process C.
Is it somehow possible for C to adopt B from init?
Update:
Or would it even be possible to have C adopt B directly (when A dies), if C were created prior to A's dead, without init becoming an intermediate parent of B?
Update-1:
Also I would appreciate any comments on why having the possiblity to adopt a process the way I described would be a bad thing or difficult to impossible to implement.
Update-2 - The use case (parent and children refer to process(es)):
I have an app using a parent to manage a whole bunch of children, which rely on the parent's managment facility. To do its job the parent relies on being notified by a child's termination, which is done via receiving the related SIGCHLD signal.
If the parent itself dies due some accident (including segfaulting) I need to restart the whole "family", as it's impossible now to trigger something on a child's termination (which also might due to a segfault).
In such a case I need to bring down all children and do a full system's restart.
A possible approach to avoid this situation, would be to have a spare-process in place which could take over the dead parent's role ... - if it could again receive the step children's SIGCHLD signals!
No, most definitely not possible. It couldn't be implemented either, without some nasty race conditions. The POSIX guys who make these APIs would never create something with an inherent race condition, so even if you're not bothered, your kernel's not getting it anytime soon.
One problem is that pids get reused (they're a scarce resource!), and you can't get a handle or lock on one either; it's just a number. So, say, somewhere in your code, you have a variable where you put the pid of the process you want to reparent. Then you call make_this_a_child_of_me(thepid). What would happen then? In the meantime, the other process might have exited and thepid changed to refer to some other process! Oops. There can't be a way to provide a make_this_a_child_of_me API without large restructuring of the way unix handles processes.
Note that the whole deal with waiting on child pids is precisely to prevent this problem: a zombie process still exists in the process table in order to prevent its pid being reused. The parent can then refer to its child by its pid, confident that the process isn't going to exit and have the child pid reused. If the child does exit, its pid is reserved until the parent catches SIGCHLD, or waits for it. Once the process is reaped, its pid is up for grabs immediately for other programs to start using when they fork, but the parent is guaranteed to already know about it.
Response to update: consider a more complicated scheme, where processes are reparented to their next ancestor. Clearly, this can't be done in every case, because you often want a way of disowning a child, to ensure that you avoid zombies. init fulfills that role very well. So, there has to some way for a process to specify that it intends to either adopt, or not, its grandchildren (or lower). The problem with this design is exactly the same as the first situation: you still get race conditions.
If it's done by pid again, then the grandparent exposes itself to a race condition: only the parent is able to reap a pid, so only the parent really knows which process a pid goes with. Because the grandparent can't reap, it can't be sure that the grandchild process hasn't changed from the one it intended to adopt (or disown, depending on how the hypothetical API would work). Remember, on a heavily-loaded machine, there's nothing stopping a process from being taken off the CPU for minutes, and a whole load could have changed in that time! Not ideal, but POSIX's got to account for it.
Finally, suppose then that this API doesn't work by pid, but just generally says, "send all grandchildren to me" or "send them to init". If it's called after the child processes are spawned, then you get race conditions just as before. If it's called before, then the whole thing's useless: you should be able to restructure your application a little bit to get the same behaviour. That is, if you know before you start spawning child processes who should be the parent of whom, why can't you just go ahead and create them the right way round in the first place? Pipes and IPC really are able to do all the required work.
No there is no way that you can enforce Reparenting in the way you have described.
I don't know of a good way to do this, but one reason for having it is that a process running can stand on its own or add a capability to a parent process. The adoption would occur as the result of an event, know by the (not yet) child, but not the parent. The soon-to-be child would send a signal to the parent. The parent would adopt (or not) the child. Once part of the parent, the parent/child process would be able to react to the event, whereas neither could react to the event when standing alone.
This docking behavior could be coded into the apps, but I don't know how to do it in real-time. There are other ways to achieve the same functionality. A parent, who could accept docking children could have its functionality extended in novel ways not previously known to the parent.
While the original question is tagged with unix, there is a way to achieve this on linux so it's worth mentioning. This is achievable with the use of a subreaper process. When a process's parent, it will get adopted by the nearest subreaper ancestor or init. So in your case you'll have process C set as subreaper via prctl(PR_SET_CHILD_SUBREAPER) and spawns process A, when process A dies, process B will be adopted by C
An alternative on Linux would be to spawn C in a separate PID namespace, making it the init process of the PID namespace and hence can adopt the children of A when A dies.

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.

Fork, Parent and child process

In C, is it possible to have the forked() process alive indefinitely even after the parent exits?
The idea of what I am trying to do is, Parent process forks a child, then exits, child keeps running in background until another process sends it a kill signal.
Yes, it is definitely possible to keep the child alive. The other responders are also correct; this is how a "daemon" or background process runs in a Linux environment.
Some call this the "fork off and die" approach. Here's a link describing how to do it:
http://wiki.linuxquestions.org/wiki/Fork_off_and_die
Note that more than just fork()-ing is done. File descriptors are closed to keep the background process from tying up system resources, etc.
Kerrek is right, this exactly the way how every daemon is implemented. So, your idea is perfect.
There is a daemon library function which is very easy to use for that.
The daemon() function call is not without limitations if you want to
write a well-behaved daemon. See On Starting Daemons
for an explanation.
Briefly: A good daemon should only background when it is ready to field requests, but do its setup under its own PID and print startup errors

Passing the shell to a child before aborting

Current scenario, I launch a process that forks, and after a while it aborts().
The thing is that both the fork and the original process print to the shell, but after the original one dies, the shell "returns" to the prompt.
I'd like to avoid the shell returning to the prompt and keep as if the process didn't die, having the child handle the situation there.
I'm trying to figure out how to do it but nothing yet, my first guess goes somewhere around tty handling, but not sure how that works.
I forgot to mention, the shell takeover for the child could be done on fork-time, if that makes it easier, via fd replication or some redirection.
I think you'll probably have to go with a third process that handles user interaction, communicating with the "parent" and "child" through pipes.
You can even make it a fairly lightweight wrapper, just passing data back and forth to the parent and terminal until the parent dies, and then switching to passing to/from the child.
To add a little further, as well, I think the fundamental problem you're going to run into is that the execution of a command by the shell just doesn't work that way. The shell is doing the equivalent of calling system() -- it's going to wait for the process it just spawned to die, and once it does, it's going to present the user with a prompt again. It's not really a tty issue, it's how the shell works.
bash (and I believe other shells) have the wait command:
wait: wait [n]
Wait for the specified process and report its termination status. If
N is not given, all currently active child processes are waited for,
and the return code is zero. N may be a process ID or a job
specification; if a job spec is given, all processes in the job's
pipeline are waited for.
Have you considered inverting the parent child relationship?
If the order in which the new processes will die is predictable, run the code that will abort in the "child" and the code that will continue in the parent.

Resources