Fix hang when daemonizing within a subshell

Paulo Alcantara paulo at paulo.ac
Wed Aug 1 23:09:14 UTC 2018


Hi,

In case we don't have either a /var/log/samba directory, or pass a
non-existent log directory through '-l' option, all commands that are
daemonized with '-D' option hang when executed within a subshell.

An example on how to trigger that:

  # rm -r /var/log/samba
  # s=$(nmbd -D -s /etc/samba/smb.conf -l /foo123)
  (never returns)

So, when the above command is executed within a subshell the following
happens:

  (a) Parent shell creates a pipe, sets write side of it to fd 1 (stdout),
    call read() on read-side fd, forks off a new child process and then
    executes nmbd in it.
  (b) nmbd sets up initial logging to go through fd 1 (stdout) by
    calling setup_logging(..., DEBUG_DEFAULT_STDOUT). 'state.fd' is now
    set to 1.
  (c) reopen_logs() is called by the first time which then calls
    reopen_logs_internal()
  (d) in reopen_logs_internal(), it attempts to create log.nmbd file in
    /foo123 directory and fails because directory doesn't exist.
  (e) Regardless whether the log file was created or not, it calls
    dup2(state.fd, 2) which dups fd 1 into fd 2.
  (f) At some point, fd 0 and 1 are closed and set to /dev/null

The problem with that is because parent shell in (a) is still blocked in
read() call and the new write side of the pipe is now fd 2 -- after
dup2() in (e) -- and remains unclosed.

So, IMHO, we should be checking if the log file was created successfuly
in (d), and if so, take over stderr with the new created file.

Basically, my proposed fix would be replacing the following check in
reopen_logs_internal():

	/* Take over stderr to catch output into logs */
	if (state.fd > 0) {
		if (dup2(state.fd, 2) == -1) {
                	...
                }
		...
	}
With:

	/* Take over stderr to catch output into logs */
	if (new_fd != -1) {
		if (dup2(state.fd, 2) == -1) {
                	...
                }
		...
	}

By doing so, we properly closes stdout and unblocks read() call from
parent shell and nmbd returns correctly.

Besides, I've got a few questions:

(1) Is it assumed that we will always have a /var/log/samba directory so
    we don't have to care about it?

(2) If we do pass an invalid directory with '-l' option and no
    /var/log/samba, should we return with a proper error instead, and do
    not let it run?

Thanks!

	Paulo



More information about the samba-technical mailing list