I am trying to write a simple shell which accepts command line input and execute it as a background task.
How do I allow the child process to report to the parent process once it is completed, like the Bash shell?
user#user-desktop:~$ sleep 10 &
[1] 3729
user#user-desktop:~$ sleep 2 &
[2] 3730
user#user-desktop:~$
[1]- Done sleep 10
[2]+ Done sleep 2
Since this is your homework, I won't give you full answer.
The GNU Glibc manual list the requirnment for job control shell. Let's see if you can understand it.
Basically:
you have to change the control terminal to make jobs run in background
you have to handle SIGCHLD (or wait) to monitor jobs
Ask again after you have read it.
You need to code a signal handler that handles the SIGCHLD (sometimes named SIGCLD) signal which is sent to parent processes when a child terminates.
Related
So my understanding is that if you terminate a command with ‘&,’ it runs the process in the background. If two commands were separated by ‘&,’ they would run simultaneously.
e.g. command1 & command2 //runs simultaneously
To implement this, I would have the shell fork a child process and have the child execute the 1st command I. The background. The parent would not wait and would execute the second command in parallel.
Is this the right approach? And how can I apply this to support wait()?
This is using bash on a generic unix:
BigMac:tmp steve$ sleep 10 & sleep 13 & sleep 20 &
[1] 69168
[2] 69169
[3] 69170
BigMac:tmp steve$ wait %1; wait 69169; kill %3
[1] Done sleep 10
[2]- Done sleep 13
[3]+ Terminated: 15 sleep 20
The first line launches three separate sleeps. The shell assigns them job numbers [1], [2], [3]; whereas the system assigns them process id's 69178,69169,69170 resp. The commands wait and kill can take either identifier, as shown in the second line.
The second line waits for the first job % indicates to the shell that the following number is in its naming scheme, then waits for the second process the lack of % indicates a process identifier, then kills the third job.
There is a lot more to this than such a simple example can convey; particularly the notion of job is quite a bit more involved than this example portrays.
The UNIX Programming Environment ISBN despite being nearly 40 years old, provides a great fundamental understanding of how and why UNIX works the way it does. There are more modern, detailed, and system specific works as well.
I am creating my own shell in C language. So far I implemented many features but the thing I am having problems with is CTRL-Z handling(SIGTSTP). Let me specify the problem over successful attempts:
When I execute a program in my shell (like gedit), and then press Ctrl-Z it executes kill(p_id, SIGTSTP) and stops that process. The shell also adds the process id in background_processes array so we can reach it in further. Then if I type "fg" in my shell, it brings the process to the foreground and executes kill(p_id, SIGCONT) so we can continue to use the program. Also the shell waits for the process to complete by executing waitpid function. We close the program by clicking X button or pressing Ctrl-C. Exact same thing in Linux shell. SUCCESFULL!!!
If I execute a program in my shell (like gedit) in background by specifying & (ampersand), it automatically starts this process in backgrounds by not waiting the process. But it adds the process id in background_processes array so we can reach it in further. Then when I type "fg" in my shell, it brings the process to the foreground. It actually waits for the process to complete by executing waitpid function. Also it doesn't matter if have more than one process in background, they will be bring to the foreground one by one. We close the programs by clicking X button or pressing Ctrl-C. Exact same thing in Linux shell. SUCCESFULL!!!
Lets execute a process in the foreground and then send it to the background by Ctrl-Z, and execute a process in background. We have 2 processes in the background. If I type "fg" it brings the first background process to the foreground and waits it. If I press X button (close button) which closes the program the shell brings the second process to the foreground and waits for it. Going very well right, thats what we want. So this scenario also worked very well.
The problem scenario is the same as the previous scenario in creating processes. When I type "fg" it brings the first background process to the foreground and waits it. But then if I press Ctrl-C it closes both processes!!!!!! It should only closed the first process and should have wait for the second process!!!
I searched everywhere, tried everything but couldn't figure it out. But the problem seems like with line 525. When I send SIGCONT signal it closes the process. But if comment that line it doesn't close but also I can't use the process since it is stopped!!!
I have the code in my GitHub repo here : https://github.com/EmreKumas/Myshell
Thanks for reading...
It seems like the problem is caused because of process groups. I did only create different process groups for background jobs but since you cannot change the process group of a child after it executed exec command, you better do it at the beginning before exec call. Now, the problem is solved thanks to "#that other guy" and "#John Bollinger".
How can I wait for multiple instance of same program to finish ? Below is my scenario any suggestion or pointers ?
I need to restart a running C process. After googling for long time, I figured out restarting can only done by fork and exec(I need the new instance to have a different pid than the original one hence using only exec wont work). Below is the sequence i did.
Shell 1:
Bash script
1. Start the first instance(./test.exe lets say pid 100)
2. Wait for it complete(pid 100) <<< Here need to wait for all instances of test.exe to complete
Shell 2:
1. Send a signal to above process(pid-100)
2. In signal handler had fork(new pid 200) a new process with exec command(./test.exe --restart) and kill parent (pid 100)
Now my question is that how can wait for all instances of test.exe to complete in shell1's bash script ?(basically have to wait until pid 200 is completed)
With my current approach shell1's bash script exits as soon as I send signal to kill pid 100
Update:
Actually I am looking for some bash/unix command to wait for all instances of test.exe is finished. Something like - 'wait $(pgrep -f test.exe)'
Basically you are looking for inter-process synchronisation mechanisms, i.e. inter-process semaphores and inter-process mutexes.
Two approaches that come to mind are POSIX semaphores and the older System V semaphores. I would recommend the former.
Also check out this SO reply.
Hope this helps :)
In my C program I want to know if my executable is run in foreground like this
$./a.out
or like this
$./a.out &
If you are the foreground job,
getpgrp() == tcgetpgrp(STDOUT_FILENO)
or STDIN_FILENO or STDERR_FILENO or whichever file descriptor you're attached to your controlling terminal by. (If you're not sure, open("/dev/tty") will always get you a file descriptor to your controlling terminal, if one exists.)
This is what openssh does, and is a bit easier than handling SIGTTIN/SIGTTOU if you just want a quick check.
On the other hand, you may have been backgrounded
$ ./a.out
^Z
[1]+ Stopped ./a.out
$ bg
[1]+ ./a.out &
or foregrounded
$ fg
./a.out
at any point in time. You cannot expect that you can check this once and it will still be true (or false) later.
From the Bash Reference Manual: Job Control Basics:
Background processes are those whose process group id differs from the terminal's; such processes are immune to keyboard-generated signals. Only foreground processes are allowed to read from or write to the terminal. Background processes which attempt to read from (write to) the terminal are sent a SIGTTIN (SIGTTOU) signal by the terminal driver, which, unless caught, suspends the process.
So the solution is to install a signal handler for SIGTTIN and then try to read from stdin (turn buffering off or it will block). If you get "0 bytes read" back, then you're running in the foreground.
[EDIT] Note that the status of a process can change. You can use the job control commands of the shell (Ctrl-Z, bg, fg and jobs) to do this.
To my knowledge this is not possible and usually not necessary either.
Please explain why you want to do this.
[invalid]IIRC, getppid() (on *nix systems) will give you the parent id. if it is 0, the 'console' is your parent and so you are running in the background.
[/invalid]
[edit]
int devtty;
if ((devtty = open ("/dev/tty", O_RDWR)) < 0)
printf ("daemon\n");
note that this is only valid on *nix systems (and then only if nobody has deleted /dev/tty -- for whatever reason)
[/edit]
There may be a possibility that you have more than one process
running in the background:
$ jobs
[1] Stopped teamviewer
[2]- Stopped vim
[3]+ Stopped firefox
use: fg %2 to send the vim process back to foreground.
To send the last process back to foreground simply use: fg with no
arguments.
You can also type % process_name to resume the stopped process.
To suspend the process running in the background, use:
kill -19 %job_id.
The -19 signal is SIGSTOP (the signal sent by Ctrl - Z) .
you can always see the list by typing kill -l
Moving jobs between background / foreground:
If you have already typed a command and forgot to use the &, you can put a foreground job into the background by typing ^Z (CTRL-Z) to suspend the job, followed by bg, to put it into the background:
$ sleep 99
^Z
[1]+ Stopped sleep 99
$ bg
[1]+ sleep 99 &
You can list the jobs of the current shell using the jobs command.
Just remember that "exiting shell" affects jobs as well:
Jobs running in the background when the shell exits are left running.
Jobs that are paused (“Stopped”) when the shell exits are terminated.
Sending signals to jobs and processes
You can send signals, including termination signals, to jobs that are started from the current shell using job numbers using %(JOBID) instead of process numbers(PID):
$ kill %1
[1]+ Terminated sleep 99
To send signals to processes or jobs that are not started from the current shell, you first need to use ps to find their process numbers(PID).
You can refer to this link:
processes and jobs
The general job control commands in Linux are:
jobs - list the current jobs
fg - resume the job that's next in the queue
fg %[number] - resume job [number]
bg - Push the next job in the queue into the background
bg %[number] - Push the job [number] into the background
kill %[number] - Kill the job numbered [number]
kill -[signal] %[number] - Send the signal [signal] to job number [number]
disown %[number] - disown the process(no more terminal will be owner), so command will be alive even after closing the terminal.
That's pretty much all of them. Note the % infront of the job number in the commands - this is what tells kill you're talking about jobs and not processes.
I have a child process which runs in a pseudo terminal. The parent process does not run as root, but the child process does, through su or sudo. Because of this it is not possible to send a signal to the child process to force it to exit. I want to force it to exit by one of these means:
emulating a Ctrl-C.
emulating a terminal hangup.
How do I do either of these? I already have a pty master fd, and I've tried something like this:
write(master, &termios.c_cc[VINTR], 1)
but it doesn't do anything.
It seems to me that if you truly have a pty (unless you mean something else by pseudo terminal), that all you have to do is send a Control-C to that FD. As evidence of this, I submit the following code in Python (but fairly close to the C required to do it):
import pty, os, sys, time
pid, fd = pty.fork()
if pid == 0:
os.execv('/bin/sh', ['/bin/sh', '-c',
'while true; do date; sleep 1; done'])
sys.exit(0)
time.sleep(3)
os.write(fd, '^C')
print 'results:', os.read(fd, 1024)
This forks a process under a pty, which runs an infinite loop printing the
date. Then the parent waits 3 seconds and sends a control-C.
This results in the following output:
guin:/tmp$ time python /tmp/foo
results: Fri Feb 5 08:28:09 MST 2010
Fri Feb 5 08:28:10 MST 2010
Fri Feb 5 08:28:11 MST 2010
python /tmp/foo 0.02s user 0.01s system 1% cpu 3.042 total
guin:/tmp$
It ran just over 3 seconds, printed out the date 3 times, and exited.
I eventually went with the following solution:
After forking, instead of exec'ing sudo immediately, I exec() a helper child process instead, which in turn forks and execs sudo and calls waitpid on it. So the process hierarchy looks like this:
original process <---- runs as user
|
+-- helper process <---- runs as user, session leader,
| has own pty, in pty's foreground process group
|
+--- sudo <---- runs as root
By killing the helper process, the pty does not have a foreground process anymore. This will cause the OS to send SIGHUP to the entire foreground process group, regardless of the user, so sudo is SIGHUP'ed too.
I think you need to use ioctl to insert the interrupt character instead of write. Unfortunately the mechanism for this does not seem to be portable. For linux it looks this might work:
ioctl(master, TIOCSTI, &termios.c_cc[VINTR]);
There is two ways to achieve this:
From the child process, trap the SIGCHLD signal and handle it, you could _exit(0) to end the child process
There's a program called ptree. You could cheat this by doing it this way...in pseudocode:
obtain the parent's pid.
using _popen("ptree %d", parent_pid)
for each entry of child process
system ("kill -1 %d", child_process_pid)
There the two that comes to mind...sorry if its not of further help to you,
Hope this helps,
Best regards,
Tom.
Closing the master should signal a hangup to the controlling process group of the slave.
The first thing I'd check is if you need to make it the controlling terminal on the slave side. It turns out this is more complex than I remember, with ptys possibly not becoming controlling by default. That link is for Linux, other systems should do one or the other depending on their SysV vs. BSD-ness, but it looks like the TIOCSCTTY is a good bet to try.
Secondly, I'd check if you're setting ISIG in your termios; if not, VINTR and VQUIT won't work.
Of course, if the other end is catching SIGINT and SIGQUIT, you will have other issues.