今回は、前回のブログ『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の判別方法
図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


Leave a Reply