Program quit unexpected after daemonize

Hi,

I write a program running as service, but I notice it quit very quickly after daemonize,

systemd logs shows it exit with 0

     Loaded: loaded (/home/zylthinking/.config/systemd/user/proxy.service; enabled; vendor preset: enabled)
     Active: inactive (dead) since Wed 2022-12-07 23:14:24 CST; 991ms ago
       Docs: info:emacs
             man:emacs(1)
             https://gnu.org/software/emacs/
    Process: 7185 ExecStart=/usr/local/bin/proxy (code=exited, status=0/SUCCESS)
   Main PID: 7186 (code=exited, status=0/SUCCESS)

And after I add some log written to a file, like the following, it confuses me:


fn main() {
    option_parse!("clap", "v0.1.0");
    let local = LOCAL.parse::<String>().unwrap();
    let remote = REMOTE.parse::<String>().unwrap();
    let mut mode = MODE.parse::<i32>().expect("only 0, 1, 2 accepted");
    if mode == 0 {
        mode = 47;
    } else if mode == 1 {
        mode = 0;
    }

    let local: SocketAddr = local.parse().expect("local address is illegal");
    let remote: SocketAddr = remote.parse().expect("remote address is illegal");
    let socket = TcpSocket::new_v4().unwrap();
    socket
        .set_reuseaddr(true)
        .expect("failed to set reuse addr");
    socket.bind(local).expect("faild to bind local address");

    if !DAEMON.parse::<bool>().unwrap() {
        let daemon = Daemonize::new();
        daemon.start().expect("failed run as daemon");
    }

    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open("/home/zylthinking/a.txt")
        .unwrap();

    file.write_all("2".as_bytes()).expect("write failed");
    _ = file.flush();

    let x = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(4)
        .enable_all()
        .build();

    file.write_all("5".as_bytes()).expect("write failed");
    _ = file.flush();

    x.unwrap().block_on(server(socket, remote, mode));

    file.write_all("6".as_bytes()).expect("write failed");
    _ = file.flush();
}

async fn server(socket: TcpSocket, remote: SocketAddr, m: i32) {
    let mut file = std::fs::OpenOptions::new()
        .append(true)
        .open("/home/zylthinking/a.txt")
        .unwrap();

    file.write_all("1".as_bytes()).expect("write failed");
    _ = file.flush();

    let listener: TcpListener = socket.listen(5).expect("failed to start listen");

    file.write_all("0".as_bytes()).expect("write failed");
    _ = file.flush();

    loop {
        if let Ok((cli, _)) = listener.accept().await {
            tokio::spawn(handle_request(cli, remote, m));
        }
    }
}

Some times, the file contains: 25; seems tokio::block_on returns without blocked at server. Which I can't understand.

Well, even stranger, some times the file contians only 2, meaning the program exit with 0 during

    let x = tokio::runtime::Builder::new_multi_thread()
        .worker_threads(4)
        .enable_all()
        .build();

What happens in both situations?

I don't know what happened.

It seems because of something related to crate Daemonize, because if I disable daemon and config systemd from type forking to simple, all the problems gone.

On the other side, however, running as daemon from terminal directly, it also works correctly ....

And, after daemonize, the process did write 2 to log file, and exiting after that can't because of things done by crate Daemonize.

It is because of systemd? well the log shows the program exited normally with code 0, the code should not be 0 if killed by systemd, and this phenomenon never happens for any other services....

Sorry for this posting, seems it is not a rust language issue.
The possible reason for this I think maybe some signal handler registered for some signal, which calls exit(0), and systemd sent that signal as it happens.

I think it is something related with crate Daemon, replace it with C Api daemon from unistd.h, the problem gone.


extern "C" {
    fn daemon(nochdir: i32, noclose: i32) -> i32;
}

fn main() {
    [...]
    if !DAEMON.parse::<bool>().unwrap() {
        // let daemon = Daemonize::new();
        // daemon.start().expect("failed run as daemon");
        unsafe {
            daemon(1, 1);
        };
    }
    [...]
}

What is the content of the .service file you passed to systemd? Systemd supports multiple run modes. I believe the default one expects the program to not daemonize.

I think it's Daemonize's bug. because C function works.
The problem arises when I call Daemonize with systemd run mode forking.

  • if I use simple run mode and does not call Daemonize, problem gone
  • if I call C daemon() with systemd run mode forking, problem gone