【解决方法】
(1)修改 nagios 配置文件 nagios-x.y.z/etc/cgi.cfg:
- escape_html_tags=1
+ escape_html_tags=0
(2)确保web容器支持UTF-8(以apache为例),修改配置文件 /etc/apache2/conf.d/charset:
+ AddDefaultCharset UTF-8
重启 nagios 和 apache 即可。
【原因】
(1)首先,问题肯定不在插件上,因为在shell下运行我们的插件是没有问题的,所以初步定位在nagios本身的BUG;
(2)cgi名字是status.cgi,在对应的status.c里面找到展示services的部分(nagios 3.4.3):
1901 /* the rest of the columns... */
1902 printf("<td class='status%s'>%s</td>\n", status_class, status);
1903 printf("<td class='status%s' nowrap>%s</td>\n", status_bg_class, date_time);
1904 printf("<td class='status%s' nowrap>%s</td>\n", status_bg_class, state_duration);
1905 printf("<td class='status%s'>%d/%d</td>\n", status_bg_class, temp_status->current_attempt, temp_status->max_attempts);
1906 printf("<td class='status%s' valign='center'>", status_bg_class);
1907 printf("%s ", (temp_status->plugin_output == NULL) ? "" : html_encode(temp_status->plugin_output, TRUE));
1903 行是在打印 Last Check 的数据;
1904 行是在打印 Duration 的数据;
1905 行是在打印 Attempt 的数据;
1906~1907 行是在打印 Status Information 的数据;
我们需要注意的是1907行:为什么我们的插件输出被 html_encode 了?这个函数究竟做了什么?
(3)在 include/cgiutils.h 中找到 html_encode 函数:
454 char *html_encode(char *, int); /* encodes a string in HTML format (for what the user sees) */
(4)在 cgi/cgiutils.c 中找到 html_encode 的实现:
961 /* escapes a string used in HTML */
962 char * html_encode(char *input, int escape_newlines) {
...
977 /* end of string */
978 if((char)input[x] == (char)'\x0') {
979 encoded_html_string[y] = '\x0';
980 break;
981 }
...
983 /* alpha-numeric characters and spaces don't get encoded */
...
987 /* newlines turn to <BR> tags */
...
1026 /* for simplicity, all other chars represented by their numeric value */
简单看一下干了什么:初始化原字符串和目标字符串,983行过滤掉数字和空格,987行把 \n 转移成 <br>,然后,在1026行,我们终于看到那句让人喷血的话了:for simplicity, all other chars represented by their numeric value.
【几种解决办法】
大概思路我们已经有了,这是nagios的一个BUG,没有考虑到脚本输出是utf8的情况,从而捕获输出后进行了两次utf8转义,所以才会出现乱码,修改的方法就无非是这么几个了:
(1)修改配置,就是最上方的解决方法了;
(2)在源代码中干掉 html_encode 对捕获输出的转义,简单粗暴,本质上和(1)是一样的;
(3)如果我们真的需要对输出进行转义怎么办?那就自己 hack 一个 html_encode 函数,可以参考icinga的html_encode实现:
https://github.com/dnsmichi/icinga/bl...
注释写得很详尽,不多说。
(4)sourceforge上有一个nagios-3.2.3的中文版,里面是这样hack html_encode()的:
1505 /***** UTF-8 MultiByte *****/
1506 // 2 Byte charactor
1507 else if( ( (unsigned char)input[x] >= 0xC0 && (unsigned char)input[x] <= 0xDF ) &&
( (unsigned char)input[x+1] >= 0x80 && (unsigned char)input[x+1] <= 0xBF ) ){
1508 encoded_html_string[y++]=input[x++];
1509 encoded_html_string[y++]=input[x];
1510 }
1511 // 3 Byte charactor(BOM) : 0xEF 0xBB 0xBF
1512 else if ( ( (unsigned char)input[x] == 0xEF ) && ( (unsigned char)input[x+1] == 0x
BB ) && ( (unsigned char)input[x+2] == 0xBF ) ){
1513 encoded_html_string[y++]=input[x++];
1514 encoded_html_string[y++]=input[x++];
1515 encoded_html_string[y++]=input[x];
1516 }
1517 // 3 Byte charactor
1518 else if ( ( (unsigned char)input[x] >= 0xE0 ) && ( (unsigned char)input[x] <= 0xEF
) && ( (unsigned char)input[x+1] >= 0x80 ) && ( (unsigned char)input[x+1] <= 0xBF ) && (
(unsigned char)input[x+2] >= 0x80 ) && ( (unsigned char)input[x+2] <= 0xBF ) ){
1519 encoded_html_string[y++]=input[x++];
1520 encoded_html_string[y++]=input[x++];
1521 encoded_html_string[y++]=input[x];
1522 }
1523 // 4 Byte charactor
1524 else if ( ( (unsigned char)input[x] >= 0xF0 ) && ( (unsigned char)input[x] <= 0xF7
) && ( (unsigned char)input[x+1] >= 0x80 ) && ( (unsigned char)input[x+1] <= 0xBF ) && (
(unsigned char)input[x+2] >= 0x80 ) && ( (unsigned char)input[x+2] <= 0xBF ) && ( (unsigne
d char)input[x+3] >= 0x80 ) && ( (unsigned char)input[x+3] <= 0xBF ) ){
1525 encoded_html_string[y++]=input[x++];
1526 encoded_html_string[y++]=input[x++];
1527 encoded_html_string[y++]=input[x++];
1528 encoded_html_string[y++]=input[x];
1529 }