Init script won't "stop" forked C program - linux

I have a C program what has a "daemon" mode, so that I can have it run in the background. When it is run with "-d" it forks using the following code:
if(daemon_mode == 1)
{
int i = fork();
if(i<0) exit(1); // error
if(i>0) exit(0); // parent
}
I created an init script, and when i manually run the init script to start my daemon, it starts ok, however, if i run it with "stop" the daemon isn't stopped.
I imagine the issue is that the PID has changed due to forking, what am I don't wrong and how do I fix it?

If you are using a pid file to control the process, then you are likely correct that changing the pid is causing a problem. Just write the pid file after you have daemonized rather than before.

Related

Erlang: How to make connected external OS process automatically die when controlling Erlang process crashes?

I am using Erlang port to read output of Linux process. I'd like the Linux process to be automatically killed whenever my connected Erlang process dies. From the docs, it seems to me that this should automatically happen, but it does not.
Minimal example. Put this in the file test.erl:
-module(test).
-export([start/0, spawn/0]).
start() ->
Pid = spawn_link(?MODULE, spawn, []),
register(test, Pid).
spawn() ->
Port = open_port({spawn, "watch date"},[stream, exit_status]),
loop([{port, Port}]).
loop(State) ->
receive
die ->
error("died");
Any ->
io:fwrite("Received: ~p~n", [Any]),
loop(State)
end.
Then, in erl shell:
1> c(test).
{ok,test}
2> test:start().
true
The process starts and prints some data received from the Linux "watch" command every 2 seconds.
Then, I make the Erlang process crash:
3> test ! die.
=ERROR REPORT==== 26-May-2021::13:24:01.057065 ===
Error in process <0.95.0> with exit value:
{"died",[{test,loop,1,[{file,"test.erl"},{line,15}]}]}
** exception exit: "died"
in function test:loop/1 (test.erl, line 15)
The Erlang process dies as expected, the data from "watch" stops appearing but the watch process still keeps running in the background as can be seen in Linux (not erl) terminal:
fuxoft#frantisek:~$ pidof watch
1880127
In my real-life scenario, I am not using "watch" command but other process that outputs data and accepts no input. How can I make it automaticall die when my connected Erlang process crashes? I can do this using Erlang supervisor and manually issuing the "kill" command when Erlang process crashes but I thought this could be done easier and cleaner.
The open_port function creates a port() and links it to the calling process. If the owning process dies, the port() closes.
In order to communicate with the externally spawned command, Erlang creates several pipes, which are by default tied to the stdin and stdout (file descriptors) of the external process. Anything that the external process writes through the stdout will arrive as a message to the owning process.
When the Port is closed, the pipes attaching it to the external process are broken, and so trying to read or write to them will give you a SIGPIPE/EPIPE.
You can detect that from your external process when writing or reading from the FDs and exiting the process then.
E.g.: With your current code, you can retrieve the external process OS pid with proplists:get_value(os_pid, erlang:port_info(Port)). If you strace it, you will see:
write(1, ..., 38) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=31297, si_uid=1001} ---
SIGPIPE in ports and Erlang
It seems that although the default action for SIGPIPE is to terminate the process, Erlang sets it to ignore the signal (and thus the children processes inherit this configuration).
If you're unable to modify the external process code to detect the EPIPE, you can use this c wrapper to reset the action:
#include <unistd.h>
#include <signal.h>
int main(int argc, char* argv[]) {
if (signal(SIGPIPE, SIG_DFL) == SIG_ERR)
return 1;
if (argc < 2)
return 2;
execv(argv[1], &(argv[1]));
}
just compile it and run it as wrapper path-to-executable [arg1 [arg2 [...]]] with open_port

Does adding '&' makes it run as a daemon?

I am aware that adding a '&' in the end makes it run as a background but does it also mean that it runs as a daemon?
Like:
celery -A project worker -l info &
celery -A project worker -l info --detach
I am sure that the first one runs in a background however the second as stated in the document runs in the background as a daemon.
I would love to know the main difference of the commands above
They are different!
"&" version is background , but not run as daemon, daemon process will detach with terminal.
in C language ,daemon can write in code :
fork()
setsid()
close(0) /* and /dev/null as fd 0, 1 and 2 */
close(1)
close(2)
fork()
This ensures that the process is no longer in the same process group as the terminal and thus won't be killed together with it. The IO redirection is to make output not appear on the terminal.(see:https://unix.stackexchange.com/questions/56495/whats-the-difference-between-running-a-program-as-a-daemon-and-forking-it-into)
a daemon make it to be in its own session, not be attached to a terminal, not have any file descriptor inherited from the parent open to anything, not have a parent caring for you (other than init) have the current directory in / so as not to prevent a umount... while "&" version do not
Yes the process will be ran as a daemon, or background process; they both do the same thing.
You can verify this by looking at the opt parser in the source code (if you really want to verify this):
. cmdoption:: --detach
Detach and run in the background as a daemon.
https://github.com/celery/celery/blob/d59518f5fb68957b2d179aa572af6f58cd02de40/celery/bin/beat.py#L12
https://github.com/celery/celery/blob/d59518f5fb68957b2d179aa572af6f58cd02de40/celery/platforms.py#L365
Ultimately, the code below is what detaches it in the DaemonContext. Notice the fork and exit calls:
def _detach(self):
if os.fork() == 0: # first child
os.setsid() # create new session
if os.fork() > 0: # pragma: no cover
# second child
os._exit(0)
else:
os._exit(0)
return self
Not really. The process started with & runs in the background, but is attached to the shell that started it, and the process output goes to the terminal.
Meaning, if the shell dies or is killed (or the terminal is closed), that process will be sent a HUG signal and will die as well (if it doesn't catch it, or if its output goes to the terminal).
The command nohup detaches a process (command) from the shell and redirects its I/O, and prevents it from dying when the parent process (shell) dies.
Example:
You can see that by opening two terminals. In one run
sleep 500 &
in the other one run ps -ef to see the list of processes, and near the bottom something like
me 1234 1201 ... sleep 500
^ ^
process id parent process (shell)
close the terminal in which sleep sleeps in the background, and then do a ps -ef again, the sleep process is gone.
A daemon job is usually started by the system (its owner may be changed to a regular user) by upstart or init.

reboot within an initrd image

I am looking for a method to restart/reset my linux system from within an init-bottom script*. At the time my script is executed the system is found under /root and I have access to a busybox.
But the "reboot" command which is part of my busybox does not work. Is there any other possibility?
My system is booted normally with an initramfs image and my script is eventually causing an update process. The new systemd which comes with debian irritates this. But with a power reset everything is fine.
I have found this:
echo b >/proc/sysrq-trigger
(it's like pressing CTRL+ALT+DEL)
If you -are- init (the PID of your process/script is 0), then starting the busybox reboot program won't work since it tries to signal init (which is not started) to reboot.
Instead, as PID 0, you should do what init would do. This is call the correct kernel API for the reboot. See Man reboot(2) for details.
Assuming you are running a c program or something, one would do:
#include <unistd.h>
#include <sys/reboot.h>
void main() { reboot(0x1234567); }
This is much better than executing the sysrq trigger which will act more like a panic restart than a clean restart.
As a final note, busybox's init actually forks a process to do the reboot for it. This is because the reboot systemcall actually also exists the program, and the system should never run without an init process (which will also panic the kernel). Hence in this case, you would do something like:
pid_t pid;
pid = vfork();
if (pid == 0) { /* child */
reboot(0x1234567);
_exit(EXIT_SUCCESS);
}
while (1); /* Parent (init) waits */

If I kill script running script running java?

If I kill gracefully (without -9) a script which is running another script, which is running java in turn, will java process receive kill signal by cascade?
I have seen java not properly shutdown in this case, and become owned by init (pid 1). I have fixed this in the past by recording the pid of the java process after it has launched, and then sending a kill -15 in a signal handler inside the bash script.
jpid=
trap_intr()
{
[ ! -z "$jpid" ] && kill $jpid
}
trap trap_intr INT TERM
java -cp ... foo &
jpid=$!
wait
UPDATE: I forgot to put the java process in the background, and have the bash script wait on $!

How do I stop Ctrl-C from killing spawned processes with jruby?

I have a ruby program, spawning new processes. I want these to survive their parent even when I press Ctrl-C. To accomplish this, I try to trap INT, However, this doesn't help.
The program below starts an xeyes each time you press enter, quits if you write anything, and is supposed to quit if you press Ctrl-C and then return.
If I quit the normal way, the xeyes survives.
If I press Ctrl-C, the xeyes dies.
Tracing the xeyes, it do receive a SIGINT, not a SIGHUP as suggested.
What can I do to keep my xeyes alive?
The program:
#!/usr/bin/jruby
require 'readline'
keep_at_it = true
trap("INT") { puts "\nCtrl-C!" ; keep_at_it = false }
while (keep_at_it) do
line = Readline.readline("Enter for new xeyes, anything else to quit: ", true)
if (line.length == 0 && keep_at_it == true)
Thread.new { system("nohup xeyes >/dev/null 2>/dev/null") }
else
keep_at_it = false
end
end
I have been testing with ruby as well, but since I need JMX support thats only available with jruby, I cannot use ruby as-is. The following way works in ruby:
fork { Process.setsid; exec("xeyes") }
The 'Process setsid' seems to make sure there is no controlling terminal, and I suspect this is central. However, I fail in getting jruby to accept fork, even using the -J-Djruby.fork.enabled=true flag.
Only the parent process is killed by SIGINT, the child processes are dying because they're being sent a SIGHUP signal that indicates their parent process has died. Try launching xeyes via the nohup command. It will prevent the SIGHUP signal from killing the processes it launches.
Thread.new { system("nohup xeyes") }

Resources