Subject: bin/13357: inetd sometimes zaps its PID file when servicing internal services
To: None <gnats-bugs@gnats.netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: netbsd-bugs
Date: 07/02/2001 14:40:44
>Number: 13357
>Category: bin
>Synopsis: inetd sometimes zaps its PID file when servicing internal services
>Confidential: no
>Severity: serious
>Priority: medium
>Responsible: bin-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Mon Jul 02 11:38:00 PDT 2001
>Closed-Date:
>Last-Modified:
>Originator: Greg A. Woods
>Release: 2001/06/24
>Organization:
Planix, Inc.; Toronto, Ontario; Canada
>Environment:
System: NetBSD 1.5W
Architecture: all
Machine: all
>Description:
Inetd will sometimes zap its PID file (accidentally) when
servicing internal services, either always if -DLIBWRAP_INTERNAL
is used, or just for those that cause a fork() otherwise.
This is because the helpful little pidfile(3) function is a
little bit too helpful for any daemon that might fork() but not
exec(). Pidfile() uses atexit(3) to clean up the PID file when
the process exits. However if a caller uses fork() without
exec() then the exiting child process won't have had its
atexit() table "cleared" and thus this child process will call
the function registered by pidfile(), and that funcation of
course removes the PID file.
The hack below works, but relies on several more or less hidden
assumptions being true. A better hack might be to have the
pidfile() cleanup function check that the PID is the same now as
it was when the file was first created (i.e. only allow the
parent to remove the PID file).
A more general and perhaps additional (not alternate) fix would
be to allow a child process to de-register one or all atexit()
functions. Unfortunately there doesn't appear to be a standard
way to do this (i.e. ISO C standard), and grovelling about in
the *BSD global __atexit isn't portable.
>How-To-Repeat:
enable the "discard" service if necessary:
# echo "discard stream tcp nowait root internal" >> /etc/inetd.conf
# /etc/rc.d/inetd reload
now the test:
# ls -l /var/run/inetd.pid
(note it is there)
# telnet localhost discard
^]quit
# ls -l /var/run/inetd.pid
(note it is not there any more!)
>Fix:
apply the following nasty hack to src/usr.sbin/inetd.c (note
line numbers may be off due to other local changes)
***************
*** 620,627 ****
sigsetmask(0L);
if (pid == 0) {
run_service(ctrl, sep);
! if (dofork)
! exit(0);
}
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
close(ctrl);
--- 616,627 ----
sigsetmask(0L);
if (pid == 0) {
run_service(ctrl, sep);
! if (dofork) {
! if (sep->se_bi == 0)
! exit(0);
! else
! _exit(0);
! }
}
if (!sep->se_wait && sep->se_socktype == SOCK_STREAM)
close(ctrl);
>Release-Note:
>Audit-Trail:
>Unformatted: