nagios 自定义插件乱码问题【已解决】
环境:
插件脚本使用utf编码、系统环境为utf8,在shell中单独运行插件返回结果正常(无乱码);
问题:
nagios捕获插件输出后,在页面展示为乱码;
Answers
【解决方法】
(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 }