The Internet Superserver Socket Server Design · Linux System Programming

 

Part 1: inetd — The Internet Superserver
Chapter 60 · Socket Server Design · Linux System Programming
inetd
Internet Superserver
5
Steps inetd Takes
TCP+UDP
Both Supported

1. What is inetd?

Imagine you are running a Linux server that provides many network services — echo, FTP, telnet, time, daytime, and more. The traditional approach is to run a separate server process for each service, all the time, even when no client is using them. That wastes memory and CPU.

inetd (pronounced “inet-dee”) solves this problem. It is a single daemon called the Internet Superserver. Instead of running one process per service, inetd listens on all the service ports. When a client connects to any of those ports, inetd wakes up and starts the right server program just for that connection. Once the request is handled, the server exits.

This means only one process (inetd) is always running instead of dozens, which saves a lot of system resources — especially important on older or resource-limited systems.

Traditional vs inetd Approach

❌ Traditional: Many Processes Always Running

echo_server   [RUNNING – idle]
ftp_server    [RUNNING – idle]
telnet_server [RUNNING – idle]
time_server   [RUNNING – idle]
daytime_server [RUNNING – idle]

Wastes memory — all idle most of the time!

✅ With inetd: One Process, On-Demand Servers

inetd          [RUNNING – monitors all ports]
echo_server   [started only when needed]
ftp_server    [started only when needed]
telnet_server [started only when needed]

Saves memory — servers start only on real requests!

2. How inetd Works — Step by Step

When a client connects to a port managed by inetd, inetd follows these steps automatically. Understanding this helps you write servers that work with inetd without worrying about all the setup code yourself.

1

Select the Service Entry

inetd reads /etc/inetd.conf to find which server program handles this port. Each line in that file maps a port/protocol to a server program. inetd picks the right entry based on the incoming connection’s port and protocol (TCP or UDP).

2

fork() — Create a Child Process

inetd calls fork() to create a child process. The child will become the actual server. The parent (inetd) goes back to monitoring ports. This means inetd is never blocked waiting for one server to finish — it can accept new connections on other ports simultaneously.

inetd also sets up a handler for SIGCHLD so that when a child server finishes, it is automatically reaped (cleaned up) to avoid zombie processes.

3

Set Up the Child as a Daemon

The child process is automatically configured as a daemon. inetd handles all the usual daemon initialization steps — things like detaching from the terminal, setting the working directory, and more. The server programmer does not need to write any of this boilerplate code.

4

Redirect Socket to stdin/stdout/stderr

This is the key step that makes inetd-invoked servers so simple. inetd duplicates the socket file descriptor onto file descriptors 0, 1, and 2 — which are standard input (STDIN_FILENO), standard output (STDOUT_FILENO), and standard error.

After this, the server can just read from fd 0 to receive data from the client and write to fd 1 to send data back — exactly like reading/writing to a terminal. No need to call accept() or handle socket file descriptors directly.

All other file descriptors that are open in inetd but not needed by the server are closed.

5

exec() — Run the Server Program

Finally, inetd calls exec() to replace the child process with the actual server program. Since the socket is already mapped to stdin/stdout, the server can immediately start communicating with the client using simple read() and write() calls.

3. How File Descriptor Redirection Works

This is the most important trick inetd uses. Here is what happens to file descriptors before exec() runs:

Before inetd Remaps

fd 0 → terminal (stdin)
fd 1 → terminal (stdout)
fd 2 → terminal (stderr)
fd 3 → TCP socket
fd 4, 5… → other FDs

After inetd Remaps (dup2)

fd 0 → TCP socket (client)
fd 1 → TCP socket (client)
fd 2 → TCP socket (client)
fd 3, 4, 5… → closed
Key Insight: After this remapping, when the server program does read(STDIN_FILENO, buf, size), it is actually reading from the network socket connected to the client. And write(STDOUT_FILENO, buf, size) sends data back to the client. The server does not even know it is talking to a network socket!

4. The /etc/inetd.conf Configuration File

inetd reads its configuration from /etc/inetd.conf. Each line in this file describes one network service. The format is:

service  socket-type  protocol  wait/nowait  user  server-path  server-args

Here is a real example for the echo service:

echo  stream  tcp  nowait  root  /bin/is_echo_sv  is_echo_sv

Field Value in Example Meaning
service echo Service name from /etc/services (maps to port 7)
socket-type stream stream = TCP, dgram = UDP
protocol tcp Transport protocol: tcp or udp
wait/nowait nowait Controls whether inetd waits for the server to finish (see below)
user root User account to run the server as
server-path /bin/is_echo_sv Full path to the server executable
server-args is_echo_sv argv[0] for the server (usually same as program name)

5. nowait vs wait — The Critical Difference

nowait (used for TCP)

After inetd forks a child to handle a client, inetd does not wait for that child to finish. It immediately goes back to monitoring the listening socket. This means inetd can accept a new client connection even while the previous client’s server is still running.

This is the right choice for TCP because each TCP connection gets a dedicated connected socket, so inetd can keep accepting new connections on the listening socket.

Used for: TCP (stream) services

wait (used for UDP)

After inetd forks a child for a UDP service, inetd stops monitoring that socket and waits for the server to exit before listening again. This is needed because UDP has no concept of a separate connected socket — all datagrams arrive on the same socket, so inetd must hand full control of that socket to the server.

Used for: UDP (dgram) services

6. What inetd Does For You (Summary)

Without inetd, every server must do all of these steps itself. With inetd, none of this is needed:

✓ Create and bind the listening socket
✓ Call listen() and accept()
✓ fork() to handle each client
✓ Set up daemon (detach from terminal, etc.)
✓ Reap dead children (SIGCHLD handler)
✓ Redirect socket to stdin/stdout
✓ Close unused file descriptors
Result: Your server only needs to write the code that actually handles the client request — reading from stdin, processing, writing to stdout. That’s it.

7. inetd and System Load

inetd reduces system load in two ways:

  • Fewer idle processes: Without inetd, each service runs a server process continuously even when no clients are connected. With inetd, these processes only exist when actually needed.
  • Simplified programming: Since inetd handles all initialization, server programs are shorter and less likely to have bugs in the setup code.

This is especially important on embedded systems or servers with many services but low simultaneous usage — exactly the kind of scenario common in Linux-based embedded devices.

Interview Questions — Part 1

Q1: What is inetd and why was it created?

inetd (Internet superserver daemon) is a single daemon that listens on multiple network ports simultaneously. It was created to reduce the number of always-running server processes on a system. Instead of having a separate process for each service waiting for connections, inetd monitors all ports and starts the appropriate server only when a connection arrives. This saves memory and simplifies server programming.

Q2: What are the five steps inetd takes when a connection arrives on a TCP port?

(1) Select the matching service entry from /etc/inetd.conf. (2) Call fork() to create a child process. (3) Set up the child as a daemon. (4) Duplicate the connected socket onto file descriptors 0, 1, and 2 (stdin/stdout/stderr) and close all other file descriptors. (5) Call exec() to replace the child with the actual server program.

Q3: What is the difference between “nowait” and “wait” in inetd.conf?

nowait is used for TCP services. After forking a child, inetd does not wait for that child and immediately returns to monitoring the listening socket for new connections. wait is used for UDP services. After forking a child, inetd stops monitoring that socket and waits for the server child to exit, because UDP has no per-connection socket and the server needs exclusive access to the datagram socket.

Q4: Why does inetd duplicate the socket onto file descriptors 0, 1, and 2?

By mapping the socket to stdin (fd 0) and stdout (fd 1), the server program can communicate with the client using standard read() and write() calls. The server does not need to know about sockets at all — it just reads input and writes output. This dramatically simplifies server code and even allows non-network programs to be used as inetd servers.

Q5: What is a zombie process and how does inetd prevent it?

A zombie process is a process that has finished but whose exit status has not yet been collected by the parent using wait(). This leaves a dead process entry in the process table. inetd prevents this by installing a SIGCHLD signal handler. When a child server exits, the kernel sends SIGCHLD to inetd, and the handler calls wait() to reap the zombie. This keeps the system clean even as many server children are created and exit.

Q6: What does dup2() do and why does inetd use it?

dup2(oldfd, newfd) makes newfd refer to the same file description as oldfd, closing newfd first if it was open. inetd uses dup2() to map the connected socket (which has some fd number like 3 or 4) onto fd 0 (stdin), fd 1 (stdout), and fd 2 (stderr). After this, the server can use STDIN_FILENO and STDOUT_FILENO to communicate with the client without knowing the actual socket fd number.

Leave a Reply

Your email address will not be published. Required fields are marked *