Unix网络编程代码 第13章 守护进程和inetd超级服务器

来源:互联网 发布:二炮有多厉害知乎 编辑:程序博客网 时间:2024/06/05 13:36

第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 */#defineMAXFD64#defineMAXLINE4096/* max text line length */#defineLISTENQ1024/* 2nd argument to listen() */#defineSAstruct sockaddr#define IPV6int 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 */#ifdefHAVE_VSNPRINTFvsnprintf(buf, MAXLINE, fmt, ap);/* safe */#elsevsprintf(buf, fmt, ap);/* not safe */#endifn = 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) {#ifdefEPROTOif (errno == EPROTO || errno == ECONNABORTED)#elseif (errno == ECONNABORTED)#endifgoto again;elseerr_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 */#ifdefIPV6case 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#ifdefAF_UNIXcase 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)");elsesnprintf(str, sizeof(str), "%s", unp->sun_path);return (str);}#endif#ifdefHAVE_SOCKADDR_DL_STRUCTcase 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);elsesnprintf(str, sizeof(str), "AF_LINK, index=%d", sdl->sdl_index);return (str);}#endifdefault: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) {#ifdefSA_INTERRUPTact.sa_flags |= SA_INTERRUPT;/* SunOS 4.x */#endif} else {#ifdefSA_RESTARTact.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);elselistenfd = 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>#defineMAXLINE4096/* max text line length */#define IPV6int 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 */#ifdefHAVE_VSNPRINTFvsnprintf(buf, MAXLINE, fmt, ap);/* safe */#elsevsprintf(buf, fmt, ap);/* not safe */#endifn = 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 */#ifdefIPV6case 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#ifdefAF_UNIXcase 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)");elsesnprintf(str, sizeof(str), "%s", unp->sun_path);return (str);}#endif#ifdefHAVE_SOCKADDR_DL_STRUCTcase 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);elsesnprintf(str, sizeof(str), "AF_LINK, index=%d", sdl->sdl_index);return (str);}#endifdefault: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);}

原创粉丝点击