1

I know how to "deamonize" a process (not to confused with Thread.setDaemon). There are some answers here and here and I'm using my own perl wrapper, which works fine.

But what I'd like to get now, is the parent process waiting for the Java process until it says "OK", i.e., until it has really started successfully (not only process started, but everything up and running well).

I could indicate this by writing a file, binding to a socket or alike, but it's ugly. Out of the eight items on the deamonize list, I only need the following three in a slightly simplified form:

  • Close standard input, standard output and standard error.
  • Run in the background (i.e., fork)
  • Ignore SIGHUP.

The first and last item can be done before process start, so only forking the process remains. Googling for "Java fork" is hopeless since the ForkJoinPool exists. Before I get my hands dirty, I'd like to know if

  • it's supported in Java 9 (then I'd simply wait)
  • someone did it already using JNA and what problems they ran in
  • there's a better solution

I don't care about Windows as it's for a Linux server.

Cœur
  • 37,241
  • 25
  • 195
  • 267
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • in-process daemonization is *really* not a good idea. Even if you *think* you don't need some of those things, you probably really do (e.g. failure to give up the controlling terminal may eventually make your entire system unusable) ... and that list of 8 is woefully incomplete. – o11c Jul 30 '17 at 23:16
  • 1
    @o11c You may well be right, but could you elaborate? I'm really doing just the three things and since two years in production (code inherited from an older project). I probably should switch to daemonize, but I'm still considering doing the forking part in Java. – maaartinus Jul 30 '17 at 23:34
  • For example, if you don't give up the controlling terminal, `/dev/pts/` may slowly fill up and then you won't be able to create any new terminals at all. Now, if you access to `/dev/tty1` you can use that to kill the evil daemons, but if you only have SSH, you're screwed. – o11c Jul 30 '17 at 23:38
  • Note: I don't actually know how to safely daemonize. Rather, I know enough not to *try*. – o11c Jul 30 '17 at 23:39
  • These days, it seems like the best way to daemonize properly is "docker run ..."... – Paul Hicks Jul 30 '17 at 23:42
  • @PaulHicks or systemd, but some people hate that answer. – o11c Jul 30 '17 at 23:45
  • Btw, the after-fork code can get *really* hairy, since you can't call most functions (including `malloc`), see [my code here](https://github.com/o11c/python-vterm/blob/master/vterm/c-sources/spawn.c) – o11c Jul 30 '17 at 23:56
  • @o11c I can't see it in your code. IUUYC then forking is Java is a non-sense as it always runs multithreaded and nobody knows what happens in the JVM threads. So if I wanted something like this, I'd have to write a native wrapper so that I could spawn a new Java process. – maaartinus Jul 31 '17 at 00:38
  • @maaartinus at the end of the day, no matter how many abstractions exist, it all boils down to the same kernel syscalls. You can either try to make the existing abstractions work, or write your own. E.g. my code used a lot of macros for error handling. – o11c Jul 31 '17 at 00:50
  • @o11c It's much more complicated than I thought. So at the very least, I'd need to write some C code packing `fork` and `execve` together as there mustn't be any Java in between. This alone gets pretty complicated because of error-handling and other low-level reasons. When doing this, I could also create the `sockerpair`, pass it somehow to Java (`dup2` to stdin/out which I don't need anyway?).... I guess, it'd take me much longer than I want and it'd be buggy like hell. For now, I'm giving up. Maybe [akuma](http://akuma.kohsuke.org/) is the solution? – maaartinus Jul 31 '17 at 01:12

1 Answers1

2

I don't know how well this translates to Java, but from a syscall perspective:

When both the parent and child are under your full control, you should call pipe or socketpair to create your own communication channel, and specify it to the child via environment variables or command-line arguments. Remember to immediately close one half in each process (either between fork and exec if you have control there, or via the CLOEXEC flag - this means the child executable never has to know about the parent's end at all).

I'm not sure why you seem hesitant to use sockets - perhaps you're under the impression that local sockets take up ports (they don't) - though if all the data travels in one direction I'd prefer pipes just for clarity.

If it is possible for the child to create its own children, you should set the CLOEXEC flag on the inherited pipe as soon as possible.

You must send a positive message to indicate success; closing the pipe early must be considered an error (though you may also have explicit errors). Note that this means you don't have to track the exit value (useful if the "child" is actually a grandchild).


Alternatively, perhaps you should make something else do the work: your init system, X11, and DBUS? E.g., what were you thinking you should do if the child crashes?

o11c
  • 15,265
  • 4
  • 50
  • 75
  • AFAIK, about nothing translates to plain Java, though with JNA, everything should do. In pure Java, I can create a `ServerSocket(int port)` and that's ugly, but I don't care about taking up ports; they're all mine and I can close them when done. `+++` I wanted to use process exit code: I'd start my Java process, let it `fork`, let the parent just wait until the child either finishes with an error exit value or disconnects. I see, I don't know how to do the latter. – maaartinus Jul 31 '17 at 00:26
  • Concerning the init system, X11 and DBUS, I'm pretty clueless. If the child crashes, then all I can do is to try to start it again. As it never happened, a cron job checking once per minute should do. I know, there are better solutions, but given the low probability of crash, a downtime of one minute is no problem and the stupid cron job seems to be less risky than an advanced tool I don't know well. – maaartinus Jul 31 '17 at 00:31
  • You *have* to use native code one way or another, because the `fork` syscall extremely limits what your code can do, and the VM does not obey those restrictions. But you can limit that to a few hundred lines. – o11c Jul 31 '17 at 00:37
  • Re init: if it failed this time, it is quite likely to fail again. But then, it might not. Init systems have given a lot of thought to this, with good defaults, but also configurability. And `sd_notify("READY=1")` for the "I actually started successfully" is at least semi-standardized. – o11c Jul 31 '17 at 00:39