守护进程使用syslog函数打印信息(基于ARM-Linux)

本文将描述在使用inetd守护进程时,如何通过syslog函数打印消息到日志文件。

为什么需要这样做呢?根据《UNIX网络编程 卷1:套接字联网API》一书第13章的描述:由于守护进程没有控制终端,它们不能把消fprintf到stderr上。

从守护进程中登记消息的常用技巧就是调用syslog函数。

而sysolog函数需要syslogd服务的支持。因此在编译busybox时需要使能syslogd。



Linux系统启动后,需要打开syslogd服务。先来看看syslogd的命令选项:

[cpp]  view plain  copy
  1. [root@BGM /]#syslogd -h  
  2. syslogd: invalid option -- 'h'  
  3. BusyBox v1.16.0 (2013-04-15 16:07:27 CST) multi-call binary.  
  4.   
  5. Usage: syslogd [OPTIONS]  
  6.   
  7. System logging utility.  
  8. Note that this version of syslogd ignores /etc/syslog.conf.  
  9.   
  10. Options:  
  11.         -n              Run in foreground  
  12.         -O FILE         Log to given file (default:/var/log/messages)  
  13.         -l n            Set local log level  
  14.         -S              Smaller logging output  
  15.         -s SIZE         Max size (KB) before rotate (default:200KB, 0=off)  
  16.         -b N            N rotated logs to keep (default:1, max=99, 0=purge)  
  17.         -R HOST[:PORT]  Log to IP or hostname on PORT (default PORT=514/UDP)  
  18.         -L              Log locally and via network (default is network only if -R)  
  19.         -D              Drop duplicates  
  20.         -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小结编写测试用例,

程序如下:

服务器程序:

[cpp]  view plain  copy
  1. int main(void)  
  2. {  
  3.     socklen_t len;  
  4.     struct sockaddr *cliaddr;  
  5.     char buf[MAX_TCPSERV_BUF];  
  6.   
  7.     int ret, tmp;  
  8.   
  9.     openlog("bgmtcpserv", LOG_PID, 0);  
  10.   
  11.     dc = malloc(sizeof(struct data_content));  
  12.     if(!dc){  
  13.         perror("Unable to malloc data_content ");  
  14.         exit -1;  
  15.     }  
  16.   
  17.     cliaddr = malloc(sizeof(struct sockaddr_storage));  
  18.     if(!cliaddr){  
  19.         perror("Unable to malloc sockaddr_storage ");  
  20.         exit -1;  
  21.     }  
  22.   
  23.     len = sizeof(struct sockaddr_storage);  
  24.     if (getpeername(0, cliaddr, &len) == -1){  
  25.         perror("Getpeername error ");  
  26.         exit -1;  
  27.     }  
  28.       
  29.     syslog(LOG_USER|LOG_ALERT, "Connecting from %s\n", Sock_ntop(cliaddr,len));  
  30.       
  31.     close(0);   
  32.     closelog();  
  33.     exit(0);  
  34. }  
  35. /* include sock_ntop */  
  36. char *  
  37. sock_ntop(const struct sockaddr *sa, socklen_t salen)  
  38. {  
  39.     char        portstr[8];  
  40.     static char str[128];        /* Unix domain is largest */  
  41.   
  42.     switch (sa->sa_family) {  
  43.     case AF_INET: {  
  44.         struct sockaddr_in    *sin = (struct sockaddr_in *) sa;  
  45.   
  46.         if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL)  
  47.             return(NULL);  
  48.         if (ntohs(sin->sin_port) != 0) {  
  49.             snprintf(portstr, sizeof(portstr), ":%d", ntohs(sin->sin_port));  
  50.             strcat(str, portstr);  
  51.         }  
  52.         return(str);  
  53.     }  
  54. /* end sock_ntop */  
  55.   
  56. #ifdef    IPV6  
  57.     case AF_INET6: {  
  58.         struct sockaddr_in6    *sin6 = (struct sockaddr_in6 *) sa;  
  59.   
  60.         str[0] = '[';  
  61.         if (inet_ntop(AF_INET6, &sin6->sin6_addr, str + 1, sizeof(str) - 1) == NULL)  
  62.             return(NULL);  
  63.         if (ntohs(sin6->sin6_port) != 0) {  
  64.             snprintf(portstr, sizeof(portstr), "]:%d", ntohs(sin6->sin6_port));  
  65.             strcat(str, portstr);  
  66.             return(str);  
  67.         }  
  68.         return (str + 1);  
  69.     }  
  70. #endif  
  71. /* 
  72. #ifdef    AF_UNIX 
  73.     case AF_UNIX: { 
  74.         struct sockaddr_un    *unp = (struct sockaddr_un *) sa; 
  75.  
  76.              OK to have no pathname bound to the socket: happens on 
  77.                every connect() unless client calls bind() first.  
  78.         if (unp->sun_path[0] == 0) 
  79.             strcpy(str, "(no pathname bound)"); 
  80.         else 
  81.             snprintf(str, sizeof(str), "%s", unp->sun_path); 
  82.         return(str); 
  83.     } 
  84. #endif*/  
  85.   
  86. // #ifdef    HAVE_SOCKADDR_DL_STRUCT  
  87. //     case AF_LINK: {  
  88. //         struct sockaddr_dl    *sdl = (struct sockaddr_dl *) sa;  
  89.   
  90. //         if (sdl->sdl_nlen > 0)  
  91. //             snprintf(str, sizeof(str), "%*s (index %d)",  
  92. //                      sdl->sdl_nlen, &sdl->sdl_data[0], sdl->sdl_index);  
  93. //         else  
  94. //             snprintf(str, sizeof(str), "AF_LINK, index=%d", sdl->sdl_index);  
  95. //         return(str);  
  96. //     }  
  97. // #endif  
  98.     default:  
  99.         snprintf(str, sizeof(str), "sock_ntop: unknown AF_xxx: %d, len %d",  
  100.                  sa->sa_family, salen);  
  101.         return(str);  
  102.     }  
  103.     return (NULL);  
  104. }  
  105.   
  106. char *  
  107. Sock_ntop(const struct sockaddr *sa, socklen_t salen)  
  108. {  
  109.     char    *ptr;  
  110.   
  111.     if ( (ptr = sock_ntop(sa, salen)) == NULL)  
  112.         perror("sock_ntop error");    /* inet_ntop() sets errno */  
  113.     return(ptr);  
  114. }  

客户端程序:

[cpp]  view plain  copy
  1. #define SERV_PORT 30001  
  2. #define SERVIPADDR "192.168.0.200"  
  3.   
  4. ssize_t                     /* Read "n" bytes from a descriptor. */  
  5. readn(int fd, void *vptr, size_t n)  
  6. {  
  7.     size_t  nleft;  
  8.     ssize_t nread;  
  9.     char    *ptr;  
  10.   
  11.     ptr = vptr;  
  12.     nleft = n;  
  13.     while (nleft > 0) {  
  14.         if ( (nread = read(fd, ptr, nleft)) < 0) {  
  15.             if (errno == EINTR)  
  16.                 nread = 0;      /* and call read() again */  
  17.             else  
  18.                 return(-1);  
  19.         } else if (nread == 0)  
  20.             break;              /* EOF */  
  21.   
  22.         nleft -= nread;  
  23.         ptr   += nread;  
  24.     }  
  25.     return(n - nleft);      /* return >= 0 */  
  26. }  
  27.   
  28. int main(void)  
  29. {  
  30.     int sockfd, ret;  
  31.     struct sockaddr_in  servaddr;  
  32.     int n;  
  33.     char buf[512] ;  
  34.       
  35.     sockfd = socket(AF_INET, SOCK_STREAM, 0);  
  36.     if(sockfd < 0){  
  37.         perror("Socket error");  
  38.         return -1;  
  39.     }  
  40.       
  41.     memset(&servaddr, 0, sizeof(struct sockaddr_in));  
  42.     //bzero(&servaddr, sizeof(servaddr));  
  43.     servaddr.sin_family = AF_INET;  
  44.     servaddr.sin_port = htons(SERV_PORT);  
  45.     ret = inet_pton(AF_INET, SERVIPADDR, &servaddr.sin_addr);  
  46.     if(ret < 1){  
  47.         perror("Inet_pton error");  
  48.         return -1;  
  49.     }  
  50.   
  51.     ret = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));  
  52.     if(ret < 0){  
  53.         perror("Connect error");  
  54.         return -1;  
  55.     }  
  56.   
  57.     close(sockfd);  
  58. }  

服务器程序编写完以后,为了让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的监听套接字:

[cpp]  view plain  copy
  1. [root@BGM /]#netstat -an | grep 30001  
  2.   
  3. tcp        0      0 0.0.0.0:30001           0.0.0.0:*               LISTEN     

然后我们执行客户程序cli,执行完以后我们查看/var/log/messages中的内容:
[cpp]  view plain  copy
  1. Jul 23 13:13:42 BGM user.alert bgmtcpserv[2587]: Connecting from 192.168.0.200:40571  

这里的"Connecting from。。。"正是由syslog函数打印的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值