7

Background

I am writing a shared library in C, dynamically linked with LD_PRELOAD, that is meant to intercept and override network calls from the application preloading it, such as socket(), connect(), recv(), send(), etc.

In the init of the library I launch a thread with pthread_create(). This thread polls some kernel memory, which is mapped to user space.

The library is meant to be generic enough to deal with - but not limited to - networking benchmark applications such as netperf, iperf, sockperf.

My issue

Everything works fine, life is sweet in most of the cases except one. Deamonized applications. For instance, if I launch netserver (the server side of the netperf benchmarking application) as a deamon, i.e. without the -D parameter, one of the first things the application does is call fork(). On fork, the parent is closed using exit(EXIT_SUCCESS) and the child listens for connections. The fact that the parent exits kills my polling thread.

So what I'd like to achieve is have the child spawn a new polling thread if the parent has gone. I can intercept and override the fork() call but fundamentally, how can I have the child know whether the parent has gone or not? I can't take any assumption because the library has to be generic.

Thanks.

Asblarf
  • 483
  • 1
  • 4
  • 14
  • 3
    You need to daemonize your application properly, so that it is in its own process group, and so it does not get SIGHUP when the parent dies. Or you need to ignore SIGHUP. – Jonathan Leffler Sep 22 '12 at 01:27
  • What @JonathanLeffler said - in this case fork() fork() is your friend – Adrian Cornish Sep 22 '12 at 02:15
  • You might find some help at [What's the difference between nohup and a daemon](http://stackoverflow.com/questions/958249/). – Jonathan Leffler Sep 22 '12 at 02:35
  • I really hope he is not a device driver developer - it could explain why my printer doesnt work all the time ;-) – Adrian Cornish Sep 22 '12 at 02:37
  • @JonathanLeffler, I don't think you get it. First, my library has to be transparent to the application. I can't start changing process groups and umasks and chdirs blindly. That would put the calling application in a weird state and lead to errors. Second, you're suggesting I should isolate the child process from its parent, when I want the child to detect whether the parent is dead or still alive. – Asblarf Sep 22 '12 at 02:39
  • @Asblarf could you please clarify what exactly you are going to do? To `socksify` third-party application or a like? – Serge Sep 22 '12 at 02:59
  • You're probably right; I don't think I do understand what you're up to. If the information is of no help, ignore it. – Jonathan Leffler Sep 22 '12 at 04:24

3 Answers3

5

You can poll periodically getppid() function. As soon as it starts to return '1' (the id of init process) - your parent is dead.

Update

excerpt from 'man pthread_create':

The new thread terminates in one of the following ways: ...

  • Any of the threads in the process calls exit(3), or the main thread performs a return from main(). This causes the termination of all threads in the process.

So, if your thread is created by the netserver process that calls exit - yes this thread will be terminated

Serge
  • 6,088
  • 17
  • 27
  • How does that work? If your original parent dies, you're inherited by the system process (usually PID 1). So, if your parent PID changed, your parent has died. But if the system process has died, your program probably isn't running either. – Jonathan Leffler Sep 22 '12 at 01:23
  • Yes but, netserver, for instance, forks also whenever it gets a new incoming connection. So if it gets 100 simultaneous connections I'll end up with 100 threads polling over `getppid()` for nothing. – Asblarf Sep 22 '12 at 01:23
  • ... and these 100 threads will also poll your shared memory, right? – Serge Sep 22 '12 at 01:28
  • @JonathanLeffler I am sorry, Jonathan, the topic question is precise enough: "fork(): way for a child process to check if parent is dead?" – Serge Sep 22 '12 at 01:36
  • @Serge, no, they won't be. I just need only one polling thread on that piece of shared memory. – Asblarf Sep 22 '12 at 02:48
  • @Asblarf then your comment on polling `getppid()` from 100 threads is unclear to me – Serge Sep 22 '12 at 02:54
  • 1
    @Serge I tried your example. But for me `getppid()` don't returns 1 if parent dies. It returns another id, but always greater than 1. This should mean that some can not assume this behavior you described? – timmornYE Apr 25 '14 at 07:01
1

You could use ptread_atfork(3) to launch your thread in the child.

nemo
  • 55,207
  • 13
  • 135
  • 135
  • That would do if I didn't have to make sure I'm not launching a polling thread in a child whose parent isn't dead. – Asblarf Sep 22 '12 at 02:31
1

About a year later, I have the answer to my own question, which was "how does a child process know that its parent is dead?"

Well, the child can ask what the PID of its parent is, using getppid(). If the parent PID is 1, then it means the child process was reparented to the init process. Therefore, the parent has gone.

For instance:

void child_fork(void)
{
    int parent_pid;
    usleep(1);
    parent_pid = (int)getppid();

    if (parent_pid == 1)
        run_my_polling_thread();
}

Note: when a process forks the parent is put to sleep. The child process runs first, then the parent is woken up (for Copy-on-Write optimization, I won't go into details here). The usleep(1)call is here to put the child immediately to sleep so that the parent has time to wake up and terminate. Before the child wakes up it will be reparented to init.

Now, at lib init time I just need to call:

__register_atfork(NULL, NULL, child_fork, NULL)

However, this solution does not cover all cases. For instance, what if the parent has multiple children? Do I really want them all to launch a polling thread?

Right now it's an acceptable solution for me, although it's not ideal.

Asblarf
  • 483
  • 1
  • 4
  • 14
  • You really shouldn't be assuming anything about the execution order. also, how's this different from the first half of Serge's answer? – Hasturkun Aug 06 '13 at 18:24
  • @VladLazarenko Just reread Serge's answer and accepted it. Why do you feel the need to be mean? – Asblarf Aug 06 '13 at 18:38
  • @Hasturkun I can safely assume that the child is going to be run first after a fork. See Robert Love's Linux Kernel Development 3rd edition. – Asblarf Aug 06 '13 at 18:40