2
fd = open("/dev/null", O_RDWR);
if (fd == -1) {
    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
                  "open(\"/dev/null\") failed");
    return NGX_ERROR;
}

if (dup2(fd, STDIN_FILENO) == -1) {
    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed");
    return NGX_ERROR;
}

if (dup2(fd, STDOUT_FILENO) == -1) {
    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed");
    return NGX_ERROR;
}


if (fd > STDERR_FILENO) {
    if (close(fd) == -1) {
        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed");
        return NGX_ERROR;
    }
}

man tells me that dup2() makes newfd be the copy of oldfd, closing newfd first if necessary.:

int dup2(int oldfd, int newfd);

But aren't STDIN_FILENO and STDOUT_FILENO read only?

Dump of assembler code for function dup2:
0x00000037aa4c6ac0 <dup2+0>:    mov    $0x21,%eax
0x00000037aa4c6ac5 <dup2+5>:    syscall 
0x00000037aa4c6ac7 <dup2+7>:    cmp    $0xfffffffffffff001,%rax
0x00000037aa4c6acd <dup2+13>:   jae    0x37aa4c6ad0 <dup2+16>
0x00000037aa4c6acf <dup2+15>:   retq   
0x00000037aa4c6ad0 <dup2+16>:   mov    0x28a4d1(%rip),%rcx        # 0x37aa750fa8 <free+3356736>
0x00000037aa4c6ad7 <dup2+23>:   xor    %edx,%edx
0x00000037aa4c6ad9 <dup2+25>:   sub    %rax,%rdx
0x00000037aa4c6adc <dup2+28>:   mov    %edx,%fs:(%rcx)
0x00000037aa4c6adf <dup2+31>:   or     $0xffffffffffffffff,%rax
0x00000037aa4c6ae3 <dup2+35>:   jmp    0x37aa4c6acf <dup2+15>

Or dup2 didn't change newfd at all?

Tadeusz A. Kadłubowski
  • 8,047
  • 1
  • 30
  • 37
cpuer
  • 7,413
  • 14
  • 35
  • 39

3 Answers3

5

The constants themselves (on POSIX, STDIN_FILENO is 0 and STDOUT_FILENO is 1) are indeed read-only, but the file descriptors they characterize may be closed and something else opened in their place; they're just ordinary file descriptors (usually with a flag set so that they stay open on an execve() system call).

The thing that is changing is the table of file descriptors for the process that resides inside the OS kernel. See that syscall instruction? That's really important here; that's the trap out of your process into the OS.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
  • @Donal Fellows,what does the final condition `if (fd > STDERR_FILENO)` before `close(fd)` mean? – cpuer Jun 01 '11 at 08:02
  • @cpuer: It tests (and subsequently closes) the file descriptor if it isn't one of stdin, stdout and stderr. POSIX systems have `STDERR_FILENO` as being `2`, and all other FDs are “small” integers that are larger than that. – Donal Fellows Jun 01 '11 at 08:04
  • @Donal Fellows ,but is it necessary? `fd = open("/dev/null", O_RDWR);` here `fd` will never be one of stdin,stdout and stderr,right? – cpuer Jun 01 '11 at 08:05
  • 1
    @cpuer: Correct. That's why you use `dup2()`, to replicate the FD from `open()` as the descriptor you want. – Donal Fellows Jun 01 '11 at 08:09
  • @Donal Fellows ,that's why I say the condition `if (fd > STDERR_FILENO)` before `close(fd)` is pointless:) – cpuer Jun 01 '11 at 08:14
  • @cpuer: Harmless though (unless you port the code to Windows, but in that case you wouldn't do the program that way anyway; daemonizing is a POSIX concept) so it's not worth worrying about. BTW, does the code take care to redirect stderr too? – Donal Fellows Jun 01 '11 at 08:17
  • @Donal: this is a part of the code of the Nginx web server: http://lxr.evanmiller.org/http/source/os/unix/ngx_daemon.c. Strangely, stderr redirection is surrounded by an `#if 0`. – Blagovest Buyukliev Jun 01 '11 at 08:20
3

This is the final part of daemonizing and involves redirecting stdout and stdin to /dev/null because they are not going to be used later.

Daemons normally write to log files, not to the standard output.

Citing this article:

Once it is running a daemon should not read from or write to the terminal from which it was launched. The simplest and most effective way to ensure this is to close the file descriptors corresponding to stdin, stdout and stderr. These should then be reopened, either to /dev/null, or if preferred to some other location. There are two reasons for not leaving them closed:

  • to prevent code that refers to these file descriptors from failing, and
  • to prevent the descriptors from being reused for some other purpose.
Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • @Blagovest Buyukliev,but what's the point,even if they still wright to stdin/stdout,we can't see it ,right? – cpuer Jun 01 '11 at 07:59
  • @cpuer: If they write to their old stdout, they may block. If they read from their stdin, they _will_ block because the process isn't the foreground process. – Donal Fellows Jun 01 '11 at 08:07
  • Writing to `/dev/null` is a no-op, while writing to `stdout` involves more work and may block (although the output may not be visible because the process has been detached from the terminal). – Blagovest Buyukliev Jun 01 '11 at 08:10
  • @Blagovest Buyukliev, I can understand that read stdin will block,but why will they block if they write to their old stdout? – cpuer Jun 01 '11 at 08:11
  • 1
    @cpuer well, where is the old stdout ? It might be a console where the daemon was started, maybe the system console if it was started during boot. Maybe a ssh terminal if someone started it over an ssh session. Or maybe a script redirected it somewhere else. Or maybe someone already redirected it to /dev/null. Writing to a console/terminal is slow, imagine writing to stdout where stdout is an ssh session that's now closed. The point is you don't know where stdout is, so the safe bet for a daemon is to redirect it to somewhere that will not cause any problems. – nos Jun 01 '11 at 08:26
  • @nos: couldn't be better said. – Blagovest Buyukliev Jun 01 '11 at 08:28
0

Closing stdin and stdout works perfectly fine. Although when you do this you cannot read from them anymore and have to use the dup()'d descriptors.

cmende
  • 397
  • 2
  • 11