* Copyright (c) 1983 Regents of the University of California.
* Redistribution and use in source and binary forms are permitted
* provided that this notice is preserved and that due credit is given
* to the University of California at Berkeley. The name of the University
* may not be used to endorse or promote products derived from this
* software without specific prior written permission. This software
* is provided ``as is'' without express or implied warranty.
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
static char sccsid
[] = "@(#)inetd.c 5.12 (Berkeley) %G%";
* Inetd - Internet super-server
* This program invokes all internet services as needed.
* connection-oriented services are invoked each time a
* connection is made, by creating a process. This process
* is passed the connection as file descriptor 0 and is
* expected to do a getpeername to find out the source host
* Datagram oriented services are invoked when a datagram
* arrives; a process is created and passed a pending message
* on file descriptor 0. Datagram servers may either connect
* to their peer, freeing up the original socket for inetd
* to receive further messages on, or ``take over the socket'',
* processing all arriving datagrams and, eventually, timing
* out. The first type of server is said to be ``multi-threaded'';
* the second type of server ``single-threaded''.
* Inetd uses a configuration file which is read at startup
* and, possibly, at some later time in response to a hangup signal.
* The configuration file is ``free format'' with fields given in the
* order shown below. Continuation lines for an entry must being with
* a space or tab. All fields must be present in each entry.
* service name must be in /etc/services
* socket type stream/dgram/raw/rdm/seqpacket
* protocol must be in /etc/protocols
* wait/nowait single-threaded/multi-threaded
* user user to run daemon as
* server program full path name
* server program arguments maximum of MAXARGS (5)
* Comment lines are indicated by a `#' in column 1.
#include <sys/resource.h>
#define TOOMANY 40 /* don't start more than TOOMANY */
#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
#define RETRYTIME (60*10) /* retry after bind or server fail */
#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
int reapchild(), retry();
char *se_service
; /* name of service */
int se_socktype
; /* type of socket to use */
char *se_proto
; /* protocol used */
short se_wait
; /* single threaded server */
short se_checked
; /* looked at during merge */
char *se_user
; /* user name to run as */
struct biltin
*se_bi
; /* if built-in, description */
char *se_server
; /* server program */
char *se_argv
[MAXARGV
+1]; /* program arguments */
int se_fd
; /* open descriptor */
struct sockaddr_in se_ctrladdr
;/* bound address */
int se_count
; /* number started since se_time */
struct timeval se_time
; /* start of se_count */
int echo_stream(), discard_stream(), machtime_stream();
int daytime_stream(), chargen_stream();
int echo_dg(), discard_dg(), machtime_dg(), daytime_dg(), chargen_dg();
char *bi_service
; /* internally provided service name */
int bi_socktype
; /* type of socket supported */
short bi_fork
; /* 1 if should fork before call */
short bi_wait
; /* 1 if should wait for child */
int (*bi_fn
)(); /* function which performs it */
"echo", SOCK_STREAM
, 1, 0, echo_stream
,
"echo", SOCK_DGRAM
, 0, 0, echo_dg
,
"discard", SOCK_STREAM
, 1, 0, discard_stream
,
"discard", SOCK_DGRAM
, 0, 0, discard_dg
,
/* Return 32 bit time since 1970 */
"time", SOCK_STREAM
, 0, 0, machtime_stream
,
"time", SOCK_DGRAM
, 0, 0, machtime_dg
,
/* Return human-readable time */
"daytime", SOCK_STREAM
, 0, 0, daytime_stream
,
"daytime", SOCK_DGRAM
, 0, 0, daytime_dg
,
/* Familiar character generator */
"chargen", SOCK_STREAM
, 1, 0, chargen_stream
,
"chargen", SOCK_DGRAM
, 0, 0, chargen_dg
,
#define NUMINT (sizeof(intab) / sizeof(struct inent))
char *CONFIG
= "/etc/inetd.conf";
register struct servtab
*sep
;
register struct passwd
*pwd
;
if (envp
== 0 || *envp
== 0)
LastArg
= envp
[-1] + strlen(envp
[-1]);
while (argc
> 0 && *argv
[0] == '-') {
for (cp
= &argv
[0][1]; *cp
; cp
++) switch (*cp
) {
"inetd: Unknown flag -%c ignored.\n", *cp
);
(void) open("/", O_RDONLY
);
{ int tt
= open("/dev/tty", O_RDWR
);
ioctl(tt
, TIOCNOTTY
, (char *)0);
signal(SIGTSTP
, SIG_IGN
);
signal(SIGTTIN
, SIG_IGN
);
signal(SIGTTOU
, SIG_IGN
);
openlog("inetd", LOG_PID
| LOG_NOWAIT
, LOG_DAEMON
);
bzero((char *)&sv
, sizeof(sv
));
sigvec(SIGALRM
, &sv
, (struct sigvec
*)0);
sigvec(SIGHUP
, &sv
, (struct sigvec
*)0);
sv
.sv_handler
= reapchild
;
sigvec(SIGCHLD
, &sv
, (struct sigvec
*)0);
(void) sigblock(SIGBLOCK
);
if ((n
= select(maxsock
+ 1, &readable
, (fd_set
*)0,
(fd_set
*)0, (struct timeval
*)0)) <= 0) {
if (n
< 0 && errno
!= EINTR
)
syslog(LOG_WARNING
, "select: %m\n");
for (sep
= servtab
; n
&& sep
; sep
= sep
->se_next
)
if (FD_ISSET(sep
->se_fd
, &readable
)) {
fprintf(stderr
, "someone wants %s\n", sep
->se_service
);
if (!sep
->se_wait
&& sep
->se_socktype
== SOCK_STREAM
) {
ctrl
= accept(sep
->se_fd
, (struct sockaddr
*)0,
fprintf(stderr
, "accept, ctrl %d\n", ctrl
);
syslog(LOG_WARNING
, "accept: %m");
(void) sigblock(SIGBLOCK
);
dofork
= (sep
->se_bi
== 0 || sep
->se_bi
->bi_fork
);
if (sep
->se_count
++ == 0)
(void)gettimeofday(&sep
->se_time
,
else if (sep
->se_count
>= TOOMANY
) {
(void)gettimeofday(&now
, (struct timezone
*)0);
if (now
.tv_sec
- sep
->se_time
.tv_sec
>
"%s/%s server failing (looping), service terminated\n",
sep
->se_service
, sep
->se_proto
);
FD_CLR(sep
->se_fd
, &allsock
);
(void) close(sep
->se_fd
);
if (!sep
->se_wait
&& sep
->se_socktype
== SOCK_STREAM
)
if (pid
&& sep
->se_wait
) {
FD_CLR(sep
->se_fd
, &allsock
);
if (dofork
&& (tt
= open("/dev/tty", O_RDWR
)) > 0) {
signal(SIGTSTP
, SIG_IGN
);
signal(SIGTTIN
, SIG_IGN
);
signal(SIGTTOU
, SIG_IGN
);
for (i
= getdtablesize(); --i
> 2; )
(*sep
->se_bi
->bi_fn
)(ctrl
, sep
);
if ((pwd
= getpwnam(sep
->se_user
)) == NULL
) {
"getpwnam: %s: No such user",
if (sep
->se_socktype
!= SOCK_STREAM
)
recv(0, buf
, sizeof (buf
), 0);
(void) setgid((gid_t
)pwd
->pw_gid
);
initgroups(pwd
->pw_name
, pwd
->pw_gid
);
(void) setuid((uid_t
)pwd
->pw_uid
);
fprintf(stderr
, "%d execl %s\n",
getpid(), sep
->se_server
);
execv(sep
->se_server
, sep
->se_argv
);
if (sep
->se_socktype
!= SOCK_STREAM
)
recv(0, buf
, sizeof (buf
), 0);
syslog(LOG_ERR
, "execv %s: %m", sep
->se_server
);
if (!sep
->se_wait
&& sep
->se_socktype
== SOCK_STREAM
)
register struct servtab
*sep
;
pid
= wait3(&status
, WNOHANG
, (struct rusage
*)0);
fprintf(stderr
, "%d reaped\n", pid
);
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
if (sep
->se_wait
== pid
) {
fprintf(stderr
, "restored %s, fd %d\n",
sep
->se_service
, sep
->se_fd
);
FD_SET(sep
->se_fd
, &allsock
);
register struct servtab
*sep
, *cp
, **sepp
;
struct servtab
*getconfigent(), *enter();
syslog(LOG_ERR
, "%s: %m", CONFIG
);
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
while (cp
= getconfigent()) {
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
if (strcmp(sep
->se_service
, cp
->se_service
) == 0 &&
strcmp(sep
->se_proto
, cp
->se_proto
) == 0)
omask
= sigblock(SIGBLOCK
);
sep
->se_wait
= cp
->se_wait
;
#define SWAP(a, b) { char *c = a; a = b; b = c; }
SWAP(sep
->se_user
, cp
->se_user
);
SWAP(sep
->se_server
, cp
->se_server
);
for (i
= 0; i
< MAXARGV
; i
++)
SWAP(sep
->se_argv
[i
], cp
->se_argv
[i
]);
print_service("REDO", sep
);
print_service("ADD ", sep
);
sp
= getservbyname(sep
->se_service
, sep
->se_proto
);
syslog(LOG_ERR
, "%s/%s: unknown service",
sep
->se_service
, sep
->se_proto
);
if (sp
->s_port
!= sep
->se_ctrladdr
.sin_port
) {
sep
->se_ctrladdr
.sin_port
= sp
->s_port
;
(void) close(sep
->se_fd
);
* Purge anything not looked at above.
omask
= sigblock(SIGBLOCK
);
FD_CLR(sep
->se_fd
, &allsock
);
(void) close(sep
->se_fd
);
print_service("FREE", sep
);
(void) sigsetmask(omask
);
register struct servtab
*sep
;
for (sep
= servtab
; sep
; sep
= sep
->se_next
)
register struct servtab
*sep
;
if ((sep
->se_fd
= socket(AF_INET
, sep
->se_socktype
, 0)) < 0) {
syslog(LOG_ERR
, "%s/%s: socket: %m",
sep
->se_service
, sep
->se_proto
);
#define turnon(fd, opt) \
setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
if (strcmp(sep
->se_proto
, "tcp") == 0 && (options
& SO_DEBUG
) &&
turnon(sep
->se_fd
, SO_DEBUG
) < 0)
syslog(LOG_ERR
, "setsockopt (SO_DEBUG): %m");
if (turnon(sep
->se_fd
, SO_REUSEADDR
) < 0)
syslog(LOG_ERR
, "setsockopt (SO_REUSEADDR): %m");
if (bind(sep
->se_fd
, &sep
->se_ctrladdr
,
sizeof (sep
->se_ctrladdr
)) < 0) {
syslog(LOG_ERR
, "%s/%s: bind: %m",
sep
->se_service
, sep
->se_proto
);
(void) close(sep
->se_fd
);
if (sep
->se_socktype
== SOCK_STREAM
)
FD_SET(sep
->se_fd
, &allsock
);
if (sep
->se_fd
> maxsock
)
register struct servtab
*sep
;
sep
= (struct servtab
*)malloc(sizeof (*sep
));
if (sep
== (struct servtab
*)0) {
syslog(LOG_ERR
, "Out of memory.");
omask
= sigblock(SIGBLOCK
);
char *skip(), *nextline();
fseek(fconfig
, 0L, L_SET
);
fconfig
= fopen(CONFIG
, "r");
return (fconfig
!= NULL
);
register struct servtab
*sep
= &serv
;
char *cp
, *arg
, *strdup();
while ((cp
= nextline(fconfig
)) && *cp
== '#')
return ((struct servtab
*)0);
sep
->se_service
= strdup(skip(&cp
));
if (strcmp(arg
, "stream") == 0)
sep
->se_socktype
= SOCK_STREAM
;
else if (strcmp(arg
, "dgram") == 0)
sep
->se_socktype
= SOCK_DGRAM
;
else if (strcmp(arg
, "rdm") == 0)
sep
->se_socktype
= SOCK_RDM
;
else if (strcmp(arg
, "seqpacket") == 0)
sep
->se_socktype
= SOCK_SEQPACKET
;
else if (strcmp(arg
, "raw") == 0)
sep
->se_socktype
= SOCK_RAW
;
sep
->se_proto
= strdup(skip(&cp
));
sep
->se_wait
= strcmp(arg
, "wait") == 0;
sep
->se_user
= strdup(skip(&cp
));
sep
->se_server
= strdup(skip(&cp
));
if (strcmp(sep
->se_server
, "internal") == 0) {
register struct biltin
*bi
;
for (bi
= biltins
; bi
->bi_service
; bi
++)
if (bi
->bi_socktype
== sep
->se_socktype
&&
strcmp(bi
->bi_service
, sep
->se_service
) == 0)
if (bi
->bi_service
== 0) {
syslog(LOG_ERR
, "internal service %s unknown\n",
sep
->se_wait
= bi
->bi_wait
;
for (arg
= skip(&cp
); cp
; arg
= skip(&cp
))
sep
->se_argv
[argc
++] = strdup(arg
);
sep
->se_argv
[argc
++] = NULL
;
register struct servtab
*cp
;
for (i
= 0; i
< MAXARGV
; i
++)
register char *cp
= *cpp
;
while (*cp
== ' ' || *cp
== '\t')
if (c
== ' ' || c
== '\t')
if (cp
= nextline(fconfig
))
while (*cp
&& *cp
!= ' ' && *cp
!= '\t')
if (fgets(line
, sizeof (line
), fd
) == NULL
)
new = malloc((unsigned)(strlen(cp
) + 1));
syslog(LOG_ERR
, "Out of memory.");
if (getpeername(s
, &sin
, &size
) == 0)
(void) sprintf(buf
, "-%s [%s]", a
, inet_ntoa(sin
.sin_addr
));
(void) sprintf(buf
, "-%s", a
);
strncpy(cp
, buf
, LastArg
- cp
);
* Internet services provided internally by inetd:
echo_stream(s
, sep
) /* Echo service -- echo data back */
setproctitle(sep
->se_service
, s
);
while ((i
= read(s
, buffer
, sizeof(buffer
))) > 0 &&
echo_dg(s
, sep
) /* Echo service -- echo data back */
if ((i
= recvfrom(s
, buffer
, sizeof(buffer
), 0, &sa
, &size
)) < 0)
(void) sendto(s
, buffer
, i
, 0, &sa
, sizeof(sa
));
discard_stream(s
, sep
) /* Discard service -- ignore data */
setproctitle(sep
->se_service
, s
);
while (read(s
, buffer
, sizeof(buffer
)) > 0)
discard_dg(s
, sep
) /* Discard service -- ignore data */
(void) read(s
, buffer
, sizeof(buffer
));
for (i
= 0; i
<= 128; ++i
)
chargen_stream(s
, sep
) /* Character generator */
setproctitle(sep
->se_service
, s
);
text
[LINESIZ
+ 1] = '\n';
if ((len
= endring
- rs
) >= LINESIZ
)
bcopy(rs
, text
, LINESIZ
);
bcopy(ring
, text
+ len
, LINESIZ
- len
);
if (write(s
, text
, sizeof(text
)) != sizeof(text
))
chargen_dg(s
, sep
) /* Character generator */
if (recvfrom(s
, text
, sizeof(text
), 0, &sa
, &size
) < 0)
if ((len
= endring
- rs
) >= LINESIZ
)
bcopy(rs
, text
, LINESIZ
);
bcopy(ring
, text
+ len
, LINESIZ
- len
);
text
[LINESIZ
+ 1] = '\n';
(void) sendto(s
, text
, sizeof(text
), 0, &sa
, sizeof(sa
));
* Return a machine readable date and time, in the form of the
* number of seconds since midnight, Jan 1, 1900. Since gettimeofday
* returns the number of seconds since midnight, Jan 1, 1970,
* we must add 2208988800 seconds to this figure to make up for
* some seventy years Bell Labs was asleep.
if (gettimeofday(&tv
, (struct timezone
*)0) < 0) {
fprintf(stderr
, "Unable to get time of day\n");
return (htonl((long)tv
.tv_sec
+ 2208988800));
(void) write(s
, (char *) &result
, sizeof(result
));
if (recvfrom(s
, (char *)&result
, sizeof(result
), 0, &sa
, &size
) < 0)
(void) sendto(s
, (char *) &result
, sizeof(result
), 0, &sa
, sizeof(sa
));
daytime_stream(s
, sep
) /* Return human-readable time of day */
clock
= time((time_t *) 0);
(void) sprintf(buffer
, "%.24s\r\n", ctime(&clock
));
(void) write(s
, buffer
, strlen(buffer
));
daytime_dg(s
, sep
) /* Return human-readable time of day */
clock
= time((time_t *) 0);
if (recvfrom(s
, buffer
, sizeof(buffer
), 0, &sa
, &size
) < 0)
(void) sprintf(buffer
, "%.24s\r\n", ctime(&clock
));
(void) sendto(s
, buffer
, strlen(buffer
), 0, &sa
, sizeof(sa
));
* Dump relevant information to stderr
print_service(action
, sep
)
"%s: %s proto=%s, wait=%d, user=%s builtin=%x server=%s\n",
action
, sep
->se_service
, sep
->se_proto
,
sep
->se_wait
, sep
->se_user
, sep
->se_bi
, sep
->se_server
);