nagios 自定义插件乱码问题【已解决】


环境:
插件脚本使用utf编码、系统环境为utf8,在shell中单独运行插件返回结果正常(无乱码);
问题:
nagios捕获插件输出后,在页面展示为乱码;

nagios 乱码

Azusa酱 11 years ago

【解决方法】
(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         }
琉璃人形师 answered 11 years ago

Your Answer