📄 inetd.c
字号:
/* * Copyright (c) 1983, 1991 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */char copyright[] = "@(#) Copyright (c) 1983 Regents of the University of California.\n" "All rights reserved.\n";/* * From: @(#)inetd.c 5.30 (Berkeley) 6/3/91 */char rcsid[] = "$Id: inetd.c,v 1.38 2000/07/24 23:48:34 dholland Exp $";#include "../version.h"/* * 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 * and port. * * 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[.max] single-threaded/multi-threaded, max # * user[.group] user/group to run daemon as * server program full path name * server program arguments maximum of MAXARGS (20) * * For RPC services * service name/version must be in /etc/rpc * socket type stream/dgram/raw/rdm/seqpacket * protocol must be in /etc/protocols * wait/nowait[.max] single-threaded/multi-threaded * user[.group] user to run daemon as * server program full path name * server program arguments maximum of MAXARGS (20) * * Comment lines are indicated by a `#' in column 1. *//* * Here's the scoop concerning the user.group feature: * * 1) No group listed. * * a) for root: NO setuid() or setgid() is done * * b) nonroot: setuid() * setgid(primary group as found in passwd) * initgroups(name, primary group) * * 2) set-group-option on. * * a) for root: NO setuid() * setgid(specified group) * setgroups(1, specified group) * * b) nonroot: setuid() * setgid(specified group) * initgroups(name, specified group) * * All supplementary groups are discarded at startup in case inetd was * run manually. */#include <sys/types.h>#include <sys/param.h>#include <sys/stat.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/file.h>#include <sys/wait.h>#include <sys/time.h>#include <sys/resource.h>#include <sys/un.h>#include <netinet/in.h>#include <arpa/inet.h> /* for inet_ntoa */#include <errno.h>#include <netdb.h>#include <syslog.h>#include <pwd.h>#include <grp.h>#include <stdio.h>#include <string.h>#include <getopt.h>#include <stdlib.h>#include <unistd.h>#ifndef __linux__#ifndef RLIMIT_NOFILE#define RLIMIT_NOFILE RLIMIT_OFILE#endif#endif#include "pathnames.h"#include "inetd.h"#include "servtab.h"#include "sig.h"#include "daemon.h"#include "setproctitle.h"#include "mysleep.h"#ifdef RPC /* must come after inetd.h, which defines RPC *//* work around a compiler warning in rpc.h in libc5 */#define __wait __wait_foo#include <rpc/rpc.h>#include <rpc/pmap_clnt.h>#undef __wait#endif#include <rpc/pmap_clnt.h>#ifndef MIN#define MIN(a, b) ((a) < (b) ? (a) : (b))#endif#define DEFAULT_FILE_LIMIT 1024/* Reserve some descriptors, 3 stdio + at least: 1 log, 1 conf. file */#define FD_MARGIN (8)static void logpid(void);static int bump_nofile(void);static void attempt_to_restart(void);struct servtab *servtab; /* service table */const char *configfile = _PATH_INETDCONF; /* config file path */int debug = 0; /* debug flag *//* Length of socket listen queue. Should be per-service probably. */static int global_queuelen = 128;static volatile int nsock;static int maxsock;static fd_set allsock;static int options;static int timingout;static long rlim_ofile_cur = DEFAULT_FILE_LIMIT;#ifdef RLIMIT_NOFILEstruct rlimit rlim_ofile;#endif#ifdef sun/* * Sun's RPC library caches the result of `dtablesize()' * This is incompatible with our "bumping" of file descriptors "on demand" */int_rpc_dtablesize(){ return rlim_ofile_cur;}#endif/* * Remove things from environ[] that might have been inherited from the * parent process if we were started by root from a shell without "env -". * But, only remove the things that we can be reasonably sure are not * intended to be inherited by inetd's children, because such things might * have been deliberately set in /etc/rc. * * In the long run we need to be able to explicitly specify environment in * inetd.conf. Then we can just clear the environment, which is much * simpler. */staticvoiddiscard_stupid_environment(void){ static const char *const junk[] = { /* these are prefixes */ "CVS", "DISPLAY=", "EDITOR=", "GROUP=", "HOME=", "IFS=", "LD_", "LOGNAME=", "MAIL=", "PATH=", "PRINTER=", "PWD=", "SHELL=", "SHLVL=", "SSH", "TERM", "TMP", "USER=", "VISUAL=", NULL }; int i,k=0; for (i=0; __environ[i]; i++) { int found=0, j; for (j=0; junk[j]; j++) { if (!strncmp(__environ[i], junk[j], strlen(junk[j]))) { found=1; } } if (!found) { __environ[k++] = __environ[i]; } } __environ[k] = NULL;}/* * Exec a child, or run a builtin that's meant to be a subprocess. * (Not reached in the parent inetd process.) */staticvoidexec_child(struct servtab *sep){ struct passwd *pwd; struct group *grp = NULL; int tmpint; uid_t uid; gid_t gid; /* * If debugging, we're in someone else's session; make a new one. */ if (debug) { setsid(); } /* * Prepare to exec. */ pwd = getpwnam(sep->se_user); if (pwd == NULL) { syslog(LOG_ERR, "getpwnam: %s: No such user", sep->se_user); return; } /* * Use the uid and gid of the user. */ uid = pwd->pw_uid; gid = pwd->pw_gid; /* * If a group was specified, use its gid instead of the user's gid. */ if (sep->se_group) { grp = getgrnam(sep->se_group); if (grp == NULL) { syslog(LOG_ERR, "getgrnam: %s: No such group", sep->se_group); return; } gid = grp->gr_gid; } /* * If a nonroot user, do initgroups to run with that user's group * list. * * But if root, do not use root's group list - just use the one gid. * * If no group was specified, keep the group inetd was run under. * This is the traditional behavior, but seems dumb - shouldn't * we use the group from the password file? XXX. */ if (uid) { if (setgid(gid)) { syslog(LOG_AUTH|LOG_ERR, "setgid: %m"); return; } if (initgroups(pwd->pw_name, gid)) { syslog(LOG_AUTH|LOG_ERR, "initgroups: %m"); return; } if (setuid(uid)) { syslog(LOG_AUTH|LOG_ERR, "setuid: %m"); return; } /* * Just a bit of extra paranoia. */ seteuid(0); if (getuid()==0 || geteuid()==0) { syslog(LOG_AUTH|LOG_ERR, "Failed to drop root"); return; } } else if (grp) { if (setgid(gid)) { syslog(LOG_AUTH|LOG_ERR, "setgid: %m"); return; } if (setgroups(1, &gid)) { syslog(LOG_AUTH|LOG_ERR, "setgroups: %m"); return; } } if (debug) { gid_t tmp[NGROUPS_MAX]; int n, i; fprintf(stderr, "pid %d: exec %s\n", getpid(), sep->se_server); fprintf(stderr, "uid: %d gid: %d\n", getuid(), getgid()); fprintf(stderr, "groups: "); n = getgroups(NGROUPS_MAX, tmp); for (i=0; i<n; i++) fprintf(stderr, "%d ", tmp[i]); fprintf(stderr, "\n"); }#ifdef MULOG if (sep->se_log) { dolog(sep, 0); }#endif#ifdef RLIMIT_NOFILE if (rlim_ofile.rlim_cur != rlim_ofile_cur) { if (setrlimit(RLIMIT_NOFILE, &rlim_ofile) < 0) { syslog(LOG_ERR,"setrlimit: %m"); } }#endif /* * Transfer stdout to stderr. This is not with the other dup2's * so debug logging works. */ dup2(1, 2); for (tmpint = rlim_ofile_cur-1; tmpint > 2; tmpint--) { close(tmpint); } sig_preexec(); /* * If a builtin, now run it instead of execing. */ if (sep->se_bi) { (*sep->se_bi->bi_fn)(0, sep); exit(0); } execv(sep->se_server, sep->se_argv); syslog(LOG_ERR, "execv %s: %m", sep->se_server);}staticpid_tfork_child(struct servtab *sep){ pid_t pid; if (sep->se_count++ == 0) { gettimeofday(&sep->se_time, NULL); } else if (sep->se_count >= sep->se_max) { struct timeval now; gettimeofday(&now, (struct timezone *)0); if (now.tv_sec - sep->se_time.tv_sec > CNT_INTVL) { sep->se_time = now; sep->se_count = 1; } else { syslog(LOG_ERR, "%s server failing (looping or " "being flooded), service terminated for " "%d min\n", service_name(sep), RETRYTIME/60); FD_CLR(sep->se_fd, &allsock); close(sep->se_fd); sep->se_fd = -1; sep->se_count = 0; nsock--; if (!timingout) { timingout = 1; alarm(RETRYTIME); } return -1; } } pid = fork(); if (pid<0) { syslog(LOG_ERR, "fork: %m"); } return pid;}staticvoidlaunch(struct servtab *sep){ char buf[50]; int ctrl, dofork; if (debug) { fprintf(stderr, "launching: %s\n", sep->se_service); } if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) { /* Do nonblocking accept, just in case */ fcntl(sep->se_fd, F_SETFL, O_NDELAY); ctrl = accept(sep->se_fd, NULL, NULL); fcntl(sep->se_fd, F_SETFL, 0); if (debug) { fprintf(stderr, "accept: new socket %d\n", ctrl); } if (ctrl < 0) { if (errno != EINTR && errno != EWOULDBLOCK) { syslog(LOG_WARNING, "accept (for %s): %m", sep->se_service); } if (errno == EMFILE) { syslog(LOG_ALERT, "Out of files! Attempting restart..."); attempt_to_restart(); } return; } } else { ctrl = sep->se_fd; } dofork = (sep->se_bi == NULL || sep->se_bi->bi_fork); if (dofork) { pid_t pid = fork_child(sep); if (pid < 0) { if (ctrl != sep->se_fd) close(ctrl); mysleep(1); return; } if (pid==0) { /* child */ dup2(ctrl, 0); close(ctrl); dup2(0, 1); /* don't do stderr yet */ exec_child(sep); if (sep->se_socktype != SOCK_STREAM) recv(0, buf, sizeof (buf), 0); _exit(1); } if (sep->se_wait) { sep->se_wait = pid; FD_CLR(sep->se_fd, &allsock); nsock--; } if (!sep->se_wait && sep->se_socktype == SOCK_STREAM) close(ctrl); } else { sep->se_bi->bi_fn(ctrl, sep); if (ctrl != sep->se_fd) close(ctrl); }}staticvoidmainloop(void){ struct servtab *sep; int n, i; fd_set readable; sig_block(); syslog(LOG_INFO, "Online and ready (%d sockets)", nsock); for (;;) { /* * If there are no live sockets, hold until we have some. * (Is this necessary? Wouldn't the select just wait until * it got signaled?) */ if (nsock == 0) { while (nsock == 0) { sig_wait(); } } readable = allsock; sig_unblock(); n = select(maxsock + 1, &readable, NULL, NULL, NULL); sig_block(); if (n <= 0) { if (n < 0 && errno != EINTR) { syslog(LOG_WARNING, "select: %m"); mysleep(1); } continue; } /* * Was: * for (sep = servtab; n && sep; sep = sep->se_next) * if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) { * n--; * launch(sep); */ for (i=3; i<=maxsock; i++) { if (FD_ISSET(i, &readable)) { sep = find_service_by_fd(i); if (sep==NULL || sep->se_fd<0) { /* ? */ syslog(LOG_ERR, "selected closed socket!?"); continue; } launch(sep); } } }}/* * Saved state so we can try to restart. */static int got_dflag=0, got_iflag=0, got_qflag=0, got_conf=0;staticvoidattempt_to_restart(void){ struct stat statbuf; const char *argv[6]; const char **tmpargv1; char **tmpargv2; char qbuf[16]; int i=0; snprintf(qbuf, sizeof(qbuf), "-q%d", global_queuelen); argv[i++] = "inetd"; if (got_dflag) argv[i++] = "-d"; if (got_iflag) argv[i++] = "-i"; if (got_qflag) argv[i++] = qbuf; if (got_conf) argv[i++] = configfile; argv[i] = NULL; if (stat(_PATH_INETD, &statbuf)!=0 || (statbuf.st_mode & 0111)==0) { /* * Cannot restart - inetd is not there or not executable */ syslog(LOG_ALERT, "Cannot restart."); syslog(LOG_ALERT, "Recommend manually restarting inetd ASAP."); /* * Hopefully this will help the situation and not make too
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -