第13章 守护进程和inetd超级服务器
13.4 daemon_init函数
13.4.1 作为守护进程运行的协议无关时间获取服务器程序
运行: ./server localhost daytime
查看日志: tail /var/log/syslog
#define _POSIX_SOURCE
#include <time.h>
#include <strings.h>
#include <sys/socket.h> /* basic socket definitions */
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <syslog.h>
#include <signal.h>
#include <fcntl.h> /* for nonblocking */
typedef void Sigfunc(int); /* for signal handlers */
#define MAXFD 64
#define MAXLINE 4096 /* max text line length */
#define LISTENQ 1024 /* 2nd argument to listen() */
#define SA struct sockaddr
#define IPV6
int daemon_proc; /* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save, n;
char buf[MAXLINE + 1];
errno_save = errno; /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
#else
vsprintf(buf, fmt, ap); /* not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
void err_msg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_INFO, fmt, ap);
va_end(ap);
return;
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
int Socket(int family, int type, int protocol)
{
int n;
if ((n = socket(family, type, protocol)) < 0)
err_sys("socket error");
return (n);
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
void Listen(int fd, int backlog)
{
char *ptr;
/*4can override 2nd argument with environment variable */
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
int n;
again:if ((n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return (n);
}
void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
if (bind(fd, sa, salen) < 0)
err_sys("bind error");
}
void Write(int fd, void *ptr, int nbytes)
{
if (write(fd, ptr, nbytes) != nbytes)
err_sys("write error");
}
void Setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
{
if (setsockopt(fd, level, optname, optval, optlen) < 0)
err_sys("setsockopt error");
}
int tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)
{
int listenfd, n;
const int on = 1;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
err_quit("tcp_listen error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res;
do {
listenfd =
socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < 0)
continue; /* error, try next one */
Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
break; /* success */
Close(listenfd); /* bind error, close and try next one */
} while ((res = res->ai_next) != NULL);
if (res == NULL) /* errno from final socket() or bind() */
err_sys("tcp_listen error for %s, %s", host, serv);
Listen(listenfd, LISTENQ);
if (addrlenp)
*addrlenp = res->ai_addrlen; /* return size of protocol address */
freeaddrinfo(ressave);
return (listenfd);
}
int Tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)
{
return (tcp_listen(host, serv, addrlenp));
}
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
== NULL)
return (NULL);
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ":%d",
ntohs(sin->sin_port));
strcat(str, portstr);
}
return (str);
}
/* end sock_ntop */
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
str[0] = '[';
if (inet_ntop
(AF_INET6, &sin6->sin6_addr, str + 1,
sizeof(str) - 1) == NULL)
return (NULL);
if (ntohs(sin6->sin6_port) != 0) {
snprintf(portstr, sizeof(portstr), "]:%d",
ntohs(sin6->sin6_port));
strcat(str, portstr);
return (str);
}
return (str + 1);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
struct sockaddr_un *unp = (struct sockaddr_un *)sa;
/* OK to have no pathname bound to the socket: happens on
every connect() unless client calls bind() first. */
if (unp->sun_path[0] == 0)
strcpy(str, "(no pathname bound)");
else
snprintf(str, sizeof(str), "%s", unp->sun_path);
return (str);
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen > 0)
snprintf(str, sizeof(str), "%*s (index %d)",
sdl->sdl_nlen, &sdl->sdl_data[0],
sdl->sdl_index);
else
snprintf(str, sizeof(str), "AF_LINK, index=%d",
sdl->sdl_index);
return (str);
}
#endif
default:
snprintf(str, sizeof(str),
"sock_ntop: unknown AF_xxx: %d, len %d", sa->sa_family,
salen);
return (str);
}
return (NULL);
}
char *Sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char *ptr;
if ((ptr = sock_ntop(sa, salen)) == NULL)
err_sys("sock_ntop error"); /* inet_ntop() sets errno */
return (ptr);
}
Sigfunc *signal(int signo, Sigfunc * func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */
#endif
} else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */
#endif
}
if (sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
Sigfunc *Signal(int signo, Sigfunc * func)
{ /* for our signal() function */
Sigfunc *sigfunc;
if ((sigfunc = signal(signo, func)) == SIG_ERR)
err_sys("signal error");
return (sigfunc);
}
pid_t Fork(void)
{
pid_t pid;
if ((pid = fork()) == -1)
err_sys("fork error");
return (pid);
}
int daemon_init(const char *pname, int facility)
{
int i;
pid_t pid;
if ((pid = Fork()) < 0)
return (-1);
else if (pid)
_exit(0); /* parent terminates */
/* child 1 continues... */
if (setsid() < 0) /* become session leader */
return (-1);
Signal(SIGHUP, SIG_IGN);
if ((pid = Fork()) < 0)
return (-1);
else if (pid)
_exit(0); /* child 1 terminates */
/* child 2 continues... */
daemon_proc = 1; /* for err_XXX() functions */
chdir("/"); /* change working directory */
/* close off file descriptors */
for (i = 0; i < MAXFD; i++)
close(i);
/* redirect stdin, stdout, and stderr to /dev/null */
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
openlog(pname, LOG_PID, facility);
return (0); /* success */
}
void *Malloc(size_t size)
{
void *ptr;
if ((ptr = malloc(size)) == NULL)
err_sys("malloc error");
return (ptr);
}
int main(int argc, char **argv)
{
int listenfd, connfd;
socklen_t addrlen, len;
struct sockaddr *cliaddr;
char buff[MAXLINE];
time_t ticks;
if (argc < 2 || argc > 3)
err_quit("usage: daytimetcpsrv2 [ <host> ] <service or port>");
daemon_init(argv[0], 0);
if (argc == 2)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
cliaddr = (struct sockaddr *)Malloc(addrlen);
for (;;) {
len = addrlen;
connfd = Accept(listenfd, cliaddr, &len);
err_msg("connection from %s", Sock_ntop(cliaddr, len));
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
}
13.6 daemon_inetd函数
要运行这个例子程序,
1.先要添加服务:做法,在/etc/services最后加上: mydaytime 9999/tcp
2.安装xinetd: sudo apt-get install xinetd
3.编辑配置:在/etc/xinetd.d/目录下新建一个mydaytime文件,内容如下:
service mydaytime
{
socket_type = stream
protocol = tcp
wait = no
user =root
server =/home/xpmo/unp/server
}
其中server是例子程序的路径
4.重启xinetdsudo killall -HUP xinetd
5.查看启动成功否:
sudo netstat -tlp
如果看到mydaytime就说明启动成功了。
例子代码:
#include <time.h>
#include <sys/socket.h> /* basic socket definitions */
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <stdarg.h> /* ANSI C header file */
#include <arpa/inet.h>
#include <sys/un.h>
#include <syslog.h>
#define MAXLINE 4096 /* max text line length */
#define IPV6
int daemon_proc; /* set nonzero by daemon_init() */
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save, n;
char buf[MAXLINE + 1];
errno_save = errno; /* value caller might want printed */
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, MAXLINE, fmt, ap); /* safe */
#else
vsprintf(buf, fmt, ap); /* not safe */
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level, "%s", buf);
} else {
fflush(stdout); /* in case stdout and stderr are the same */
fputs(buf, stderr);
fflush(stderr);
}
return;
}
void err_msg(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_INFO, fmt, ap);
va_end(ap);
return;
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
void Write(int fd, void *ptr, int nbytes)
{
if (write(fd, ptr, nbytes) != nbytes)
err_sys("write error");
}
void Setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
{
if (setsockopt(fd, level, optname, optval, optlen) < 0)
err_sys("setsockopt error");
}
char *sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char portstr[8];
static char str[128]; /* Unix domain is largest */
switch (sa->sa_family) {
case AF_INET:{
struct sockaddr_in *sin = (struct sockaddr_in *)sa;
if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str))
== NULL)
return (NULL);
if (ntohs(sin->sin_port) != 0) {
snprintf(portstr, sizeof(portstr), ":%d",
ntohs(sin->sin_port));
strcat(str, portstr);
}
return (str);
}
/* end sock_ntop */
#ifdef IPV6
case AF_INET6:{
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
str[0] = '[';
if (inet_ntop
(AF_INET6, &sin6->sin6_addr, str + 1,
sizeof(str) - 1) == NULL)
return (NULL);
if (ntohs(sin6->sin6_port) != 0) {
snprintf(portstr, sizeof(portstr), "]:%d",
ntohs(sin6->sin6_port));
strcat(str, portstr);
return (str);
}
return (str + 1);
}
#endif
#ifdef AF_UNIX
case AF_UNIX:{
struct sockaddr_un *unp = (struct sockaddr_un *)sa;
/* OK to have no pathname bound to the socket: happens on
every connect() unless client calls bind() first. */
if (unp->sun_path[0] == 0)
strcpy(str, "(no pathname bound)");
else
snprintf(str, sizeof(str), "%s", unp->sun_path);
return (str);
}
#endif
#ifdef HAVE_SOCKADDR_DL_STRUCT
case AF_LINK:{
struct sockaddr_dl *sdl = (struct sockaddr_dl *)sa;
if (sdl->sdl_nlen > 0)
snprintf(str, sizeof(str), "%*s (index %d)",
sdl->sdl_nlen, &sdl->sdl_data[0],
sdl->sdl_index);
else
snprintf(str, sizeof(str), "AF_LINK, index=%d",
sdl->sdl_index);
return (str);
}
#endif
default:
snprintf(str, sizeof(str),
"sock_ntop: unknown AF_xxx: %d, len %d", sa->sa_family,
salen);
return (str);
}
return (NULL);
}
char *Sock_ntop(const struct sockaddr *sa, socklen_t salen)
{
char *ptr;
if ((ptr = sock_ntop(sa, salen)) == NULL)
err_sys("sock_ntop error"); /* inet_ntop() sets errno */
return (ptr);
}
void *Malloc(size_t size)
{
void *ptr;
if ((ptr = malloc(size)) == NULL)
err_sys("malloc error");
return (ptr);
}
void daemon_inetd(const char *pname, int facility)
{
daemon_proc = 1; /* for our err_XXX() functions */
openlog(pname, LOG_PID, facility);
}
void Getpeername(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
if (getpeername(fd, sa, salenptr) < 0)
err_sys("getpeername error");
}
int main(int argc, char **argv)
{
socklen_t len;
struct sockaddr *cliaddr;
char buff[MAXLINE];
time_t ticks;
daemon_inetd(argv[0], 0);
cliaddr = (struct sockaddr *)Malloc(sizeof(struct sockaddr_storage));
len = sizeof(struct sockaddr_storage);
Getpeername(0, cliaddr, &len);
err_msg("connection from %s", Sock_ntop(cliaddr, len));
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(0, buff, strlen(buff));
Close(0); /* close TCP connection */
exit(0);
}