今回は、前回のブログ『Nagiosのステータスマップの文字化け対策』の続き。
Nagiosのステータスマップの文字化けの根本原因の話だ。
前回の対策結果から、/usr/local/src/nagios-3.1.0/cgi/statusmap.c の1979行目にあるescape_string() 関数に問題があるのは明らかだ。
escape_string() 関数は、/usr/local/src/nagios-3.1.0/cgi/cgiutils.c で定義されている。
ステータスマップの文字化けの根本原因は、この関数でUTF-8 に対応していないためだ。
escape_string() 関数の問題箇所
下のソースがescape_string() 関数だ。
この関数は、渡された文字列(char *input)を1バイトずつ数値参照型(&#数値;)にエンコードする。
但し、以下の文字はエンコードしない。
●数字(0~9)、アルファベット(A~Z、a~z) ※1562~1563行目
●特定の半角文字(スペース’ ‘、ハイフン’-‘、ドット’.’、アンダーバー’_’、コロン’:’、) ※1566~1567行目
つまり、UTF-8の日本語はマルチバイト文字であるにもかかわらず、escape_string() 関数に渡された途端に1バイトずつぶつ切りにエンコードされてしまうのだ。
当然、文字化けしてしまう。
これが、Nagiosのステータスマップの文字化けの根本原因だ。
この問題箇所が以下のソースの赤い部分だ。(1570~1577行目)
1540 char * escape_string(char *input){
1541 int len,output_len;
1542 int x,y;
1543 char temp_expansion[10];
1544
1545 /* we need up to six times the space to do the conversion */
1546 len=(int)strlen(input);
1547 output_len=len*6;
1548 if((encoded_html_string=(char *)malloc(output_len+1))==NULL)
1549 return "";
1550
1551 strcpy(encoded_html_string,"");
1552
1553 for(x=0,y=0;x<=len;x++){
1554
1555 /* end of string */
1556 if((char)input[x]==(char)'\x0'){
1557 encoded_html_string[y]='\x0';
1558 break;
1559 }
1560
1561 /* alpha-numeric characters don't get encoded */
1562 else if(((char)input[x]>='0' && (char)input[x]<='9') || ((char)input[x]>='A' && (char)input[x]<='Z') || ((char)input[x]>=(char)'a' && (char)input[x]<=(char)'z'))
1563 encoded_html_string[y++]=input[x];
1564
1565 /* spaces, hyphens, periods, underscores and colons don't get encoded */
1566 else if(((char)input[x]==(char)' ') || ((char)input[x]==(char)'-') || ((char)input[x]==(char)'.') || ((char)input[x]==(char)'_') || ((char)input[x]==(char)':'))
1567 encoded_html_string[y++]=input[x];
1568
1569 /* for simplicity, all other chars represented by their numeric value */
1570 else{
1571 encoded_html_string[y]='\x0';
1572 sprintf(temp_expansion,"%d;",(unsigned char)input[x]);
1573 if((int)strlen(encoded_html_string)<(output_len-strlen(temp_expansion))){
1574 strcat(encoded_html_string,temp_expansion);
1575 y+=strlen(temp_expansion);
1576 }
1577 }
1578 }
1579
1580 encoded_html_string[y++]='\x0';
1581
1582 return encoded_html_string;
1583 }
|
UTF-8の判別方法について
文字化けを直したいならescape_string() 関数の問題の箇所をUTF-8のマルチバイト対応に書き換えればいいのだが、そもそもUTF-8の判別はどうしたらいいいのだろうか?
恥ずかしい話だが、自分自身、この問題にぶつからなければUTF-8の日本語は単純に3バイトだと思っていた。
実はUTF-8は、1~4バイトの可変文字コードで、その判別は1バイト目の先頭の数ビットを調べればわかるのだ。
UTF-8の判別方法は以下の通りだ。
1バイト:最初の1バイトの1ビット目が0
2バイト:最初の1バイトの1~3ビット目が110
3バイト:最初の1バイトの1~4ビット目が1110
4バイト:最初の1バイトの1~5ビット目が11110
また、後続バイトの最初の2ビットは10となっている。
図1:UTF-8の判別方法
UTF-8を数値参照型へエンコードしないようにする
UTF-8の判別方法がわかったので、次は判別できたUTF-8を数値参照型(&#数値;)にエンコードしないようにする処理を追加する。
今回は以下のように、1569~1588行目に追加してみた。(青い部分)
1540 char * escape_string(char *input){
1541 int len,output_len;
1542 int x,y;
1543 char temp_expansion[10];
1544
1545 /* we need up to six times the space to do the conversion */
1546 len=(int)strlen(input);
1547 output_len=len*6;
1548 if((encoded_html_string=(char *)malloc(output_len+1))==NULL)
1549 return "";
1550
1551 strcpy(encoded_html_string,"");
1552
1553 for(x=0,y=0;x<=len;x++){
1554
1555 /* end of string */
1556 if((char)input[x]==(char)'\x0'){
1557 encoded_html_string[y]='\x0';
1558 break;
1559 }
1560
1561 /* alpha-numeric characters don't get encoded */
1562 else if(((char)input[x]>='0' && (char)input[x]<='9') || ((char)input[x]>='A' && (char)input[x]<='Z') || ((char)input[x]>=(char)'a' && (char)input[x]<=(char)'z'))
1563 encoded_html_string[y++]=input[x];
1564
1565 /* spaces, hyphens, periods, underscores and colons don't get encoded */
1566 else if(((char)input[x]==(char)' ') || ((char)input[x]==(char)'-') || ((char)input[x]==(char)'.') || ((char)input[x]==(char)'_') || ((char)input[x]==(char)':'))
1567 encoded_html_string[y++]=input[x];
1568
1569 /* UTF-8 2byte */
1570 else if((((unsigned char)input[x] & 0xE0)==0xC0)&&(((unsigned char)input[x+1] & 0xC0)==0x80)){
1571 encoded_html_string[y++]=(char)input[x++];
1572 encoded_html_string[y++]=(char)input[x];
1573 }
1574
1575 /* UTF-8 3byte */
1576 else if((((unsigned char)input[x] & 0xF0)==0xE0)&&(((unsigned char)input[x+1] & 0xC0)==0x80)&&(((unsigned char)input[x+2] & 0xC0)==0x80)){
1577 encoded_html_string[y++]=(char)input[x++];
1578 encoded_html_string[y++]=(char)input[x++];
1579 encoded_html_string[y++]=(char)input[x];
1580 }
1581
1582 /* UTF-8 4byte */
1583 else if((((unsigned char)input[x] & 0xF8)==0xF0)&&(((unsigned char)input[x+1] & 0xC0)==0x80)&&(((unsigned char)input[x+2] & 0xC0)==0x80)&&(((unsigned char)input[x+3] & 0xC0)==0x80)){
1584 encoded_html_string[y++]=(char)input[x++];
1585 encoded_html_string[y++]=(char)input[x++];
1586 encoded_html_string[y++]=(char)input[x++];
1587 encoded_html_string[y++]=(char)input[x];
1588 }
1589
1590 /* for simplicity, all other chars represented by their numeric value */
1591 else{
1592 encoded_html_string[y]='\x0';
1593 sprintf(temp_expansion,"%d;",(unsigned char)input[x]);
1594 if((int)strlen(encoded_html_string)<(output_len-strlen(temp_expansion))){
1595 strcat(encoded_html_string,temp_expansion);
1596 y+=strlen(temp_expansion);
1597 }
1598 }
1599 }
1600
1601 encoded_html_string[y++]='\x0';
1602
1603 return encoded_html_string;
1604 }
|
CGI の再インストール
今回は、cgiutils.c のescape_string() 関数を書き換えたので、前回のブログ『Nagiosのステータスマップの文字化け対策』で書き換えたstatusmap.c の1979行目も元に戻しておく必要がある。
よって、今回はcgiutils.c とstatusmap.c の両方をmake及びインストールを行う必要がある。
# cd /usr/local/src/nagios-3.1.0/cgi
# make
# make install
|
今回は、特に載せないが、ステータスマップの文字化けが解消されていることを確認した。
Categories: Nagios