本文将描述在使用inetd守护进程时,如何通过syslog函数打印消息到日志文件。
为什么需要这样做呢?根据《UNIX网络编程 卷1:套接字联网API》一书第13章的描述:由于守护进程没有控制终端,它们不能把消fprintf到stderr上。
从守护进程中登记消息的常用技巧就是调用syslog函数。
而sysolog函数需要syslogd服务的支持。因此在编译busybox时需要使能syslogd。
Linux系统启动后,需要打开syslogd服务。先来看看syslogd的命令选项:
- [root@BGM /]#syslogd -h
- syslogd: invalid option -- 'h'
- BusyBox v1.16.0 (2013-04-15 16:07:27 CST) multi-call binary.
-
- Usage: syslogd [OPTIONS]
-
- System logging utility.
- Note that this version of syslogd ignores /etc/syslog.conf.
-
- Options:
- -n Run in foreground
- -O FILE Log to given file (default:/var/log/messages)
- -l n Set local log level
- -S Smaller logging output
- -s SIZE Max size (KB) before rotate (default:200KB, 0=off)
- -b N N rotated logs to keep (default:1, max=99, 0=purge)
- -R HOST[:PORT] Log to IP or hostname on PORT (default PORT=514/UDP)
- -L Log locally and via network (default is network only if -R)
- -D Drop duplicates
- -C[size(KiB)] Log to shared mem buffer (read it using logread)
这里我们注意到该syslogd不支持/etc/syslog.conf文件。
我们需要使用-L选项,让消息输出到本地(locally),其次其默认的本地输出文件为/var/log/message,
如果要修改输出文件,则使用-O选项。
因此,我这里使用的syslogd命令如下:
/sbin/syslogd -L
这里建议将该命令添加到系统的启动脚本中。
为了测试函数需要编写一个简单的TCP服务器程序和相应的客户程序,参照《UNIX网络编程 卷1:套接字联网API》的13.6小结编写测试用例,
程序如下:
服务器程序:
- int main(void)
- {
- socklen_t len;
- struct sockaddr *cliaddr;
- char buf[MAX_TCPSERV_BUF];
-
- int ret, tmp;
-
- openlog("bgmtcpserv", LOG_PID, 0);
-
- dc = malloc(sizeof(struct data_content));
- if(!dc){
- perror("Unable to malloc data_content ");
- exit -1;
- }
-
- cliaddr = malloc(sizeof(struct sockaddr_storage));
- if(!cliaddr){
- perror("Unable to malloc sockaddr_storage ");
- exit -1;
- }
-
- len = sizeof(struct sockaddr_storage);
- if (getpeername(0, cliaddr, &len) == -1){
- perror("Getpeername error ");
- exit -1;
- }
-
- syslog(LOG_USER|LOG_ALERT, "Connecting from %s\n", Sock_ntop(cliaddr,len));
-
- close(0);
- closelog();
- exit(0);
- }
-
- char *
- sock_ntop(const struct sockaddr *sa, socklen_t salen)
- {
- char portstr[8];
- static char str[128];
-
- 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);
- }
-
-
- #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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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)
- perror("sock_ntop error");
- return(ptr);
- }
客户端程序:
- #define SERV_PORT 30001
- #define SERVIPADDR "192.168.0.200"
-
- ssize_t
- readn(int fd, void *vptr, size_t n)
- {
- size_t nleft;
- ssize_t nread;
- char *ptr;
-
- ptr = vptr;
- nleft = n;
- while (nleft > 0) {
- if ( (nread = read(fd, ptr, nleft)) < 0) {
- if (errno == EINTR)
- nread = 0;
- else
- return(-1);
- } else if (nread == 0)
- break;
-
- nleft -= nread;
- ptr += nread;
- }
- return(n - nleft);
- }
-
- int main(void)
- {
- int sockfd, ret;
- struct sockaddr_in servaddr;
- int n;
- char buf[512] ;
-
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if(sockfd < 0){
- perror("Socket error");
- return -1;
- }
-
- memset(&servaddr, 0, sizeof(struct sockaddr_in));
-
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(SERV_PORT);
- ret = inet_pton(AF_INET, SERVIPADDR, &servaddr.sin_addr);
- if(ret < 1){
- perror("Inet_pton error");
- return -1;
- }
-
- ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
- if(ret < 0){
- perror("Connect error");
- return -1;
- }
-
- close(sockfd);
- }
服务器程序编写完以后,为了让inetd能够调用我们的服务器程序,需要修改配置文件。
首先,修改/etc/services,增加如下:
mytcpser 30001/tcp # Used by BGM
其次,修改/etc/inetd.conf,增加如下:
mytcpser stream tcp nowait root /home/bgm/bgmtcpser bgmtcpser
这里的mytcpser需要和services文件中的第一个字段相同,其次/home/bgm/bgmtcpser为服务器程序所在的路径。
修改完配置文件后,将服务器程序bgmtcpser复制到/home/bgm目录下。
使用netstat 来查看是否inetd已经创建端口号为30001的监听套接字:
- [root@BGM /]#netstat -an | grep 30001
-
- tcp 0 0 0.0.0.0:30001 0.0.0.0:* LISTEN
然后我们执行客户程序cli,执行完以后我们查看/var/log/messages中的内容:
- Jul 23 13:13:42 BGM user.alert bgmtcpserv[2587]: Connecting from 192.168.0.200:40571
这里的"Connecting from。。。"正是由syslog函数打印的。