筑波大学 システム情報系 情報工学域
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
	http://www.coins.tsukuba.ac.jp/~syspro/2013/2013-06-12
/index.html
あるいは、次のページから手繰っていくこともできます。
	http://www.coins.tsukuba.ac.jp/~syspro/2013/
	http://www.coins.tsukuba.ac.jp/~yas/
Gateway とは、WWW で使われている HTTP というプロトコルへの入り口という 意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることもイメージであることもある。
CGI の利用例
情報科学類には、2つのサーバ・プロセスが動作している。
CGI になるファイル名のパタンの例(設定により、これ以外も可能)。
/cgi-bin/name /dir/name.cgi
   1:	/*
   2:	        cgi-hello.c --簡単な CGI のプログラム
   3:	        ~yas/syspro/www/cgi-hello.c
   4:	        Created on: 2002/06/23 18:21:34
   5:	*/
   6:	
   7:	#include <stdlib.h>     /* exit() */
   8:	#include <stdio.h>      /* printf() */
   9:	
  10:	extern void print_header();
  11:	extern void print_content();
  12:	
  13:	main()
  14:	{
  15:	        print_header();
  16:	        print_content();
  17:	}
  18:	
  19:	void print_header()
  20:	{
  21:	        printf("Content-Type: text/html\n");
  22:	        printf("\n");
  23:	}
  24:	
  25:	void print_content()
  26:	{
  27:	        printf("<HTML><HEAD></HEAD><BODY>\n");
  28:	        printf("hello.\n");
  29:	        printf("</BODY></HTML>\n");
  30:	}
CGI のプログラムは、Apache の場合、標準ではHTTP のヘッダ(一番最初の 
「HTTP/1.0 200 OK」 ではなく、2行目以降)を標準出力に出力する所から始
まる。Content-Type: 行は、必ず付ける。空行がヘッダと本分の区切りである。
本文には、この場合は、HTML の文書を置いている。
$ mkdir ~/public_html/syspro-cgi-examples 
$ cd ~/public_html/syspro-cgi-examples/ 
$ cp ~yas/syspro/www/cgi-hello.c . 
$ cc cgi-hello.c -o cgi-hello.cgi 
$ ./cgi-hello.cgi  
Content-Type: text/html
<HTML><HEAD></HEAD><BODY>
hello.
</BODY></HTML>
$ 
CGI のプログラムの場合、拡張子を .cgi にする。
CGI のプログラムを、シェルから実行することもできる。
引数の与え方については、後述する。
~ユーザ名/syspro-cgi-examples/cgi-hello.cgi
syspro-cgi-examples は、上の mkdir と合わせる。
mkdir せずに  ~/public_html/ 直下の場合には、不要。
(
設置してない人は、講義のページにある
http://www.coins.tsukuba.ac.jp/~syspro/2013/2013-06-12/cgi-hello.cgi でも良い。
)
telnet では、次のようにして実行する。
$ telnet www.coins.tsukuba.ac.jp 80 
Trying 130.158.86.1...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~ユーザ名/syspro-cgi-examples/cgi-hello.cgi HTTP/1.0
GET の代りに POST でもよい。
$ telnet www.coins.tsukuba.ac.jp 80 
Trying 130.158.86.1...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~ユーザ名/syspro-cgi-examples/cgi-hello.cgi HTTP/1.0
Content-Length: 0
$ 
この講義のページに設置された cgi-hello.cgi の実行例。
$ telnet www.coins.tsukuba.ac.jp 80 
Trying 130.158.86.1...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~syspro/2013/2013-06-12/cgi-hello.cgi HTTP/1.0
HTTP/1.1 200 OK
Date: Tue, 11 Jun 2013 11:59:35 GMT
Server: Apache
Content-Length: 48
Connection: close
Content-Type: text/html
<HTML><HEAD></HEAD><BODY>
hello.
</BODY></HTML>
Connection closed by foreign host.
$ 
$ telnet www.coins.tsukuba.ac.jp 80 
Trying 130.158.86.1...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
POST /~syspro/2013/2013-06-12/cgi-hello.cgi HTTP/1.0
Content-Length: 0
HTTP/1.1 200 OK
Date: Tue, 11 Jun 2013 12:01:45 GMT
Server: Apache
Content-Length: 48
Connection: close
Content-Type: text/html
<HTML><HEAD></HEAD><BODY>
hello.
</BODY></HTML>
Connection closed by foreign host.
$ 
<H2><A NAME="cgi-example-get">CGI の GET メソッドを使う例</A></H2>
<FORM ACTION="cgi-printarg.cgi" method="get">
    <P>
    姓: <INPUT type="text" name="lastname">
    名: <INPUT type="text" name="firstname"><BR>
    <INPUT type="radio" name="sex" value="Male"> 男 <BR>
    <INPUT type="radio" name="sex" value="Female"> 女 <BR>
    <INPUT type="radio" name="sex" value="others"> その他 <BR>
    電子メール: <INPUT type="text" name="email"><BR>
    <INPUT type="submit" value="send"> <INPUT type="reset">
    </P>
</FORM>
<H2><A NAME="cgi-example-post">CGI の POST メソッドを使う例</A></H2>
<FORM ACTION="cgi-printarg.cgi" method="post">
    <P>
    姓: <INPUT type="text" name="lastname">
    名: <INPUT type="text" name="firstname"><BR>
    <INPUT type="radio" name="sex" value="Male"> 男 <BR>
    <INPUT type="radio" name="sex" value="Female"> 女 <BR>
    <INPUT type="radio" name="sex" value="others"> その他 <BR>
    電子メール: <INPUT type="text" name="email"><BR>
    <INPUT type="submit" value="send"> <INPUT type="reset">
    </P>
</FORM>
<FORM>と</FORM>で括られた部
分が1つのフォームになる。(1つのページに複数のフォームを置くことがで
きる。)
<INPUT>の部分が、ブラウザからサーバに送られるデー
タを表わす。
「<INPUT type="submit">」のボタンが押されると、
ブラウザは、指定されたメソッドを使ってサーバにデータを送る。
method="get"の場合、Web ブラウザは、
普通のファイルを指定する時と同じように、URL に含めて送る。
GET /~syspro/2013/2013-06-12/cgi-printarg.cgi?lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp HTTP/1.0
URL の長さの上限で制限されるので、長いデータは送れない。
メソッドとして POST が使われた時、Web ブラウザは、データ
をHTTP の本文(ヘッダではない部分、空行の後)に入れて送る。
POST /~syspro/2013/2013-06-12/cgi-printarg.cgi HTTP/1.0
Content-Length: 91
lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp
WWW サーバは、HTTP で GET や POST により要求を受けとる。WWW サーバは、 URL の部分を解析し、CGI として認識すると、指定されたプログラムを実行す る。実行された CGI プログラムは、 環境変数と 標準入力 からデータを受け取ることができる。
CGI で利用できる主な環境変数を以下に示す。
| 環境変数名 | 内容 | 
|---|---|
REQUEST_METHOD | 	データを送るのに使われたメソッド。POST か GET。 | 
QUERY_STRING | 	URLの指定で「?」以下に指定された文字列。 | 
SCRIPT_NAME | 	プログラムの名前。 | 
CONTENT_LENGTH | 	POSTメソッドが使われた時、標準入力から読み込めるデータのバイト数 | 
&」、または、
「;」
で区切られた
「フィールド名=値」の並びになっている。
例:
lastname=姓&firstname=名&sex=Male&email=who@u-ust.ac.jp
lastname=姓;firstname=名;sex=Male;email=who@u-ust.ac.jp
GETメソッドが使われた場合、CGI のプログラムは、環境変数
QUERY_STRING からデータを得る。
POSTメソッドが使われた場合、CGI のプログラムは、データを標準入力から受
け取る。データのバイト数は、環境変数CONTENT_LENGTH に指定
されている。
送られてくるパラメタの中に漢字、
「&」、
「;」、
その他
URLで使えない文字が含まれていた場合、
「%hh( hh は2桁の16進数)」という形になっている。
たとえば、「&」は、
「%26」として送られる。
これを元にもどすにはcgiparse というプログラム、
Ruby の CGI::unescape() や pack('H*')、
perl の命令 hex() が使われる。
GET の場合、環境変数 REQUEST_METHOD に GETと設定し、環境変数 QUERY_STRING にパラメタを設定する。
$ cp ~yas/syspro/www/cgi-printarg.c . 
$ cc cgi-printarg.c -o cgi-printarg.cgi  
$ ./cgi-printarg.cgi 
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
REQUEST_METHOD=(null)
SCRIPT_NAME=(null)
QUERY_STRING=(null)
CONTENT_LENGTH=(null)
query_string:
(null)
Error while parsing query string
</PRE></BODY></HTML>
$ export REQUEST_METHOD=GET 
$ export QUERY_STRING='lastname=name1&firstname=name2&sex=Male&email=who@dom' 
$ ./cgi-printarg.cgi 
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
REQUEST_METHOD=GET
SCRIPT_NAME=(null)
QUERY_STRING=lastname=name1&firstname=name2&sex=Male&email=who@dom
CONTENT_LENGTH=(null)
query_string:
lastname=name1&firstname=name2&sex=Male&email=who@dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: sex=Male(sex=Male)
qv[3]: email=who@dom(email=who@dom)
</PRE></BODY></HTML>
$ 
POST の場合、まず、データをファイルに保存しておく。そのバイト数を wc 
コマンドなどで調べておく。環境変数 REQUEST_METHOD に POST と設定する。
環境変数 CONTENT_LENGTH にデータファイルの大きさを設定する(ここでは最
後の改行を取り除いたバイト数を指定している)。
$ unset QUERY_STRING 
$ export REQUEST_METHOD=POST 
$ echo 'lastname=name1&firstname=name2&sex=Male&email=who@dom' > data 
$ wc data  
       1       1      54 data
$ export CONTENT_LENGTH=53 
$ ./cgi-printarg.cgi < data  
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
REQUEST_METHOD=POST
SCRIPT_NAME=(null)
QUERY_STRING=(null)
CONTENT_LENGTH=53
query_string:
lastname=name1&firstname=name2&sex=Male&email=who@dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: sex=Male(sex=Male)
qv[3]: email=who@dom(email=who@dom)
</PRE></BODY></HTML>
$ 
   1:	
   2:	/*
   3:	        cgi-printarg.c -- CGI プログラムに対する引数を表示するプログラム
   4:	        ~yas/syspro/www/cgi-printarg.c
   5:	        Created on: 2002/06/23 18:21:34
   6:	*/
   7:	
   8:	#include <stdlib.h>     /* getenv(), malloc() */
   9:	#include <stdio.h>      /* printf() */
  10:	#include <string.h>     /* strlen() */
  11:	
  12:	extern void print_header(void);
  13:	extern void print_content(void);
  14:	extern char *get_query_string();
  15:	extern char *read_query_string();
  16:	extern void safe_printenv( char *name );
  17:	extern void safe_print_string( char *str );
  18:	extern char *html_escape( char *str );
  19:	extern char *decode_hex( char *str );
  20:	int string_split( char *str, char del, int *countp, char ***vecp  );
  21:	void free_string_vector( int qc, char **vec );
  22:	int countchr( char *s, char c );
  23:	
  24:	main()
  25:	{
  26:	        print_header();
  27:	        print_content();
  28:	}
  29:	
  30:	void print_header()
  31:	{
  32:	        printf("Content-Type: text/html\n");
  33:	        printf("\n");
  34:	}
  35:	
  36:	void print_content()
  37:	{
  38:	    char  *query_string ;
  39:	    char **qv ;
  40:	    int    qc ;
  41:	    int    i ;
  42:	
  43:	        printf("<HTML><HEAD></HEAD><BODY><PRE>\n");
  44:	
  45:	        safe_printenv("REQUEST_METHOD");
  46:	        safe_printenv("SCRIPT_NAME");
  47:	        safe_printenv("QUERY_STRING");
  48:	        safe_printenv("CONTENT_LENGTH");
  49:	
  50:	        query_string = get_query_string();
  51:	        printf("query_string:\n");
  52:	        safe_print_string( query_string ); printf("\n");
  53:	
  54:	        if(  string_split( query_string,'&',&qc, &qv ) < 0 )
  55:	        {
  56:	            printf("Error while parsing query string\n");
  57:	            printf("</PRE></BODY></HTML>\n");
  58:	            if( query_string )
  59:	               free( query_string );
  60:	            exit( -1 );
  61:	        }
  62:	        for( i=0 ; i<qc ; i++ )
  63:	        {
  64:	            char *decoded ;
  65:	            printf("qv[%d]: ",i);
  66:	            safe_print_string(qv[i]);
  67:	            decoded = decode_hex( qv[i] );
  68:	            printf("("); safe_print_string( decoded ); printf(")\n");
  69:	            if( decoded )
  70:	                free( decoded );
  71:	        }
  72:	
  73:	        printf("</PRE></BODY></HTML>\n");
  74:	        free_string_vector( qc, qv );
  75:	        if( query_string )
  76:	            free( query_string );
  77:	}
  78:	
これは、標準の CGI プログラムな
ので、print_header()では、HTTP のヘッダのうち、Content-Type: 行だけを
出力している。
print_content() では、本文を出力している。
safe_printenv() で、環境変数 REQUEST_METHOD, SCRIPT_NAME, QUERY_STRING, CONTENT_LENGTH の内容を標準出力に出力している。
get_query_string() は、クライアントから送られてきたパラメタ(query string)を読込み、文字列として返す関数である。この結果を、 safe_print_string() で標準出力に出力している。
strint_split() で query string を区切り文字「&」で分解している。 この関数は、練習問題(703) HTTPサーバの要求解析 で使ったものと同じものである。 分解した結果は、main() の引数である int argc, char *argv[] と同じ形になっ ている。qc が、argc, qv が argv に対応する。
for ループでは、qv[] をsafe_print_string() で標準出力に出力している。
さらに、decode_hex() で、%hh
(%hhは16進数)の形式を、バイトに元に戻すものであ
る。
free_string_vector() は、string_split() で malloc() したものを free() するための手続きである。
  79:	char *get_query_string()
  80:	{
  81:	    char *request_method, *query_string;
  82:	        request_method = getenv("REQUEST_METHOD");
  83:	        if( request_method == 0 )
  84:	            return( 0 );
  85:	        else if( strcmp(request_method,"GET") == 0 )
  86:	        {
  87:	            query_string = getenv("QUERY_STRING");
  88:	            if( query_string == 0 )
  89:	                return( 0 );
  90:	            else
  91:	                return( strdup(query_string) );
  92:	        }
  93:	        else if( strcmp(request_method,"POST") == 0 )
  94:	        {
  95:	            return( read_query_string() );
  96:	        }
  97:	        else
  98:	        {
  99:	            printf("Unknown method: ");
 100:	            safe_print_string( request_method );
 101:	            printf("\n");
 102:	            return( 0 );
 103:	        }
 104:	}
 105:	
get_query_string() は、クライアントから送られてきたパラメタを、1つの
文字列として受け取るものである。GET メソッドの場合()は、単に環境変数 
REQUEST_METHOD を読めばよい。ただし、文字列の長さの上限がきつい。
strdup() は、文字列をコピーするものである。使い終わったら、free() で解 放する。
POST メソッドの場合は、read_query_string() で続きの処理を行う。
 106:	char *read_query_string()
 107:	{
 108:	    int   clen ;
 109:	    char *content_length ;
 110:	    char *buf ;
 111:	
 112:	        content_length = getenv("CONTENT_LENGTH");
 113:	        if( content_length == 0 )
 114:	        {
 115:	            return( 0 );
 116:	        }
 117:	        else
 118:	        {
 119:	            clen = strtol( content_length,0,10 );
 120:	            buf = malloc( clen + 1 );
 121:	            if( buf == 0 )
 122:	            {
 123:	                printf("read_query_string(): no memory\n");
 124:	                exit( -1 );
 125:	            }
 126:	            if( read(0,buf,clen) != clen )
 127:	            {
 128:	                printf("read error.\n");
 129:	                exit( -1 );
 130:	            }
 131:	            buf[clen] = 0 ;
 132:	            return( buf );
 133:	        }
 134:	}
 135:	
CONTENT_LENGTH には、10進数でバイト数が含まれている。
REQUEST_METHOD=GET SCRIPT_NAME=/~syspro/2013/2013-06-12/cgi-printarg.cgi QUERY_STRING=lastname=name1&firstname=name2&sex=Male&email=who%40dom CONTENT_LENGTH=(null) query_string: lastname=name1&firstname=name2&sex=Male&email=who%40dom qv[0]: lastname=name1(lastname=name1) qv[1]: firstname=name2(firstname=name2) qv[2]: sex=Male(sex=Male) qv[3]: email=who%40dom(email=who@dom)
REQUEST_METHOD=POST SCRIPT_NAME=/~syspro/2013/2013-06-12/cgi-printarg.cgi QUERY_STRING= CONTENT_LENGTH=55 query_string: lastname=name1&firstname=name2&sex=Male&email=who%40dom qv[0]: lastname=name1(lastname=name1) qv[1]: firstname=name2(firstname=name2) qv[2]: sex=Male(sex=Male) qv[3]: email=who%40dom(email=who@dom)
safe_print_string() は、html_escape() を使って文字列を安全なもの にして表示する。
html_escape()(来週説明)は、 クライアントから送られたような信頼できない文字列を クライアントに送り返す時に、不用意に JavaScript 等がクライアント側の ブラウザで動作しないようにするための関数である。 たとえば、<SCRIPT> のような文字列がクライアントから送られて きたとしても、クライアントには「<SCRIPT>」と 返すだけで、スクリプトは実行されない。
%hh(2桁の16進数)」を元のバイトに
戻すものである。isxdigit() は、16進数の文字として正しいかを調べてい
る。strtol() で基底を 16 として変換している。
/var/log/apache2/public/access*
/var/log/apache2/public/v6access*
/var/log/apache2/secure/access*
/var/log/apache2/secure/v6access*
$ cd /var/log/apache2/public 
$ ls    access* | tail -1 
$ ls -t access* | head -1 
$ 
以下の例では、現在増加しているログは
access_2012-06-09-12_44_44.log である。
$ cd /var/log/apache2/public 
$ ls    access* | tail -1 
access_2012-06-09-12_44_44.log
$ ls -t access* | head -1 
access_2012-06-09-12_44_44.log
$ 
以下は、アクセス・ログの内容の例である。
130.158.83.140 - - [18/Jun/2012:14:55:40 +0900] "GET /~syspro/2013/2013-06-12/index.html HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1" 130.158.83.140 - - [18/Jun/2012:14:55:40 +0900] "GET /~syspro/2013/2013-06-12/images/www-cgi.png HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1"1行が1つのページの転送を意味する。左端は、クライアントの IP アドレス である。どのような要求が来たか、クライアントのIPアドレスがわかる。
アクセスログの他に、エラーが起きた時にもログが作られる。これを エラーログ という。エラーログは、情報科学類のサーバ www (www.coins.tsukuba.ac.jp) では、次の場所にある。
/var/log/apache2/public/error*
/var/log/apache2/public/v6error*
/var/log/apache2/secure/error*
/var/log/apache2/secure/v6error*
その他に、次のようなログを取ることもある。
agent_log
referer_log
suexec_log (coins では /var/log/apache2/suexec_log)
rw-rw-rw-や 777
rwxrwxrwx にする)。このため、そのコンピュータにログインで
き人ならば、そのファイルの名前を知っていれば、データを取り出したり内容
を破壊したりできる。
suEXEC を使う方法の場合、長所と短所は逆になる。
rw-------や
700 rwx------ にする)。
Status:という行を含めることで、ステータ
ス行を記述することもできる。Status:がない場合、Location: 
というヘッダがあれば、302 Found (redirect)、それ以外の場合は、200
OKが返される。それ以外のヘッダも、Apache が自動的に生成して返す。
ステータス行も含めて、完全なヘッダを CGI で作成して返すこともできる。 この方法を、Apache では、nph (non-parsed headers) と呼んでいる。
Apache で、nph を使うには、nph- で始まるファイル名を用いる。こ
れを使うと、Apache は、CGI プログラムが作成したヘッダを一切解釈(parse)
せず、そのままクライアントに返す。
WWW ブラウザや telnet を使って得た結果と /var/www/htdocs/ 以下のファイ ルが同一であることをwww に ssh でログインして確認しなさい。
ssh コマンドを使ったことがない人は、 コンピュータリテラシの資料 を参考にして、ssh の使い方を学びなさい。 fingerprint を使って SSHにおけるホストの認証 を行いなさい。
cosmos10:~ yas$ ssh www
Password: パスワード(画面には表示されない)
Last login: Sun Jun 17 21:37:41 2012 from www.coins.tsukuba.ac.jp
www:~ yas$ cd /var/www/htdocs/
www:htdocs yas$ lv index.html
www:htdocs yas$ 
ssh で遠隔ログインする前後に、プロンプトのホスト名の部分が変化すること
に注意しなさい。
同様に https でアクセスできるページについても調べなさい。 トップのディレクトリは、/var/www/secure_htdocs/ 以下にある。
/var/log/apache2/public/access_log.* というファイルにためられる。
これを、次のようにして観察しなさい。
$ cd /var/log/apache2/public/ 
$ ls    access* | tail -1 
access_2013-06-10-07_59_20.log
$ ls -t access* | head -1 
access_2013-06-10-07_59_20.log
$ lv    access_2013-06-10-07_59_20.log 
$ head  access_2013-06-10-07_59_20.log 
$ tail  access_2013-06-10-07_59_20.log 
$ 
この例では、access_2013-06-10-07_59_20.log が最新のものである。
$ tail -f access_2013-06-10-07_59_20.log 
http://www.coins.tsukuba.ac.jp/や自分の WWW ページを開く。
次の CGI プログラムに対して、フォームを定義し、何かデータを送ってみなさい。
http://www.coins.tsukuba.ac.jp/~syspro/2013/2013-06-12/cgi-printarg.cgi
たとえば、次のような記述を含む HTML ファイルを作成し、 ~/public_html/ 以下に置く。
<FORM ACTION="http://www.coins.tsukuba.ac.jp/~syspro/2013/2013-06-12/cgi-printarg.cgi" method="get">
    arg1: <INPUT type="text" name="arg1">
    arg2: <INPUT type="text" name="arg2">
    <INPUT type="submit">
    <INPUT type="reset">
</FORM>
また、method="post" についても同様に行ってみなさい。
ヒント:
     char *arg1_s,*arg2_s;
     arg1_s = getparam(qc,qv,"arg1");
     arg2_s = getparam(qc,qv,"arg2");
getparam() は、次のように自分で定義する。
char *getparam( int qc, char *qv[], char *name /*変数名*/ )
{
   int i;
	for( i=0; i<qc; i++ ) {
	    qv[i] の中で "変数名=値" となっているものを探す。
	    見つければ "値" の部分を返す。
	}
	見つからなければ、NULL を返す。
}
ここで、"arg1arg2=10" のようなパラメタがある時に、"arg1" や "arg2" で引っ
かからないようにしなさい。同じ変数名で複数のパラメタが指定場合、この課
題ではどのパラメタを返してもよい。
$ export REQUEST_METHOD=GET 
$ export QUERY_STRING='arg1=10&arg2=10' 
$ ./a.out 
$ 
なお、一度設定した環境変数の値は、シェルを終了するまで残っている。
a.out を実行するたびに設定する必要はない。
$ mkdir ~/public_html/syspro-cgi-examples 
$ cp a.out ~/public_html/syspro-cgi-examples/calc.cgi 
$ open 'http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.cgi?arg1=10&arg2=20' 
$ emacs ~/public_html/syspro-cgi-examples/calc.html 
$ open http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.html 
$ cal 
     June 2013
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
$ 
CGI プログラムを実行した月のカレンダーを表示しなさい。固定した表示する だけなら、CGI の意味はない。余計な表示は省略すること。
この課題では、system() や popen() を用いてはならない。
$ ./a.out 
Content-Type: text/plain
     June 2013
Su Mo Tu We Th Fr Sa
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
$ 
$ mkdir ~/public_html/syspro-cgi-examples 
$ cp a.out ~/public_html/syspro-cgi-examples/show-cal-one.cgi 
$ open http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc-one.cgi 
<FORM ACTION="http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc-one.cgi" method="get"> <INPUT type="submit"> </FORM>
ヒント:
     char *year_s,*month_s;
     year_s = getparam("year");
     month_s = getparam("month");
getparam() は、次のように自分で定義する。
char *getparam( int qc, char *qv[], char *name /*変数名*/ )
{
   int i;
	for( i=0; i<qc; i++ ) {
	    qv[i] の中で "変数名=値" となっているものを探す。
	    見つければ "値" の部分を返す。
	}
	見つからなければ、NULL を返す。
}
ここで、"arg1arg2=10" のようなパラメタがある時に、"arg1" や "arg2" で引っ
かからないようにしなさい。同じ変数名で複数のパラメタが指定場合、この課
題ではどのパラメタを返してもよい。
$ export REQUEST_METHOD=GET 
$ export QUERY_STRING='year=2012&month=6' 
$ ./a.out 
$ 
なお、一度設定した環境変数の値は、シェルを終了するまで残っている。
a.out を実行するたびに設定する必要はない。
余裕があれば、そのプログラムを CGI として動作させなさい。
$ mkdir ~/public_html/syspro-cgi-examples 
$ cp a.out ~/public_html/syspro-cgi-examples/show-cal.cgi 
$ open 'http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.cgi?year=2012&month=6' 
注意: CGI のパラメタは、最初は、文字列で受け取り、最終的に cal コマンドには文 字列で渡す。この時に、年月日としては不適切なものがクライアントから渡さ れる可能性がある。不適切なものが渡された場合には、エラー・メッセージを 表示しなさい。また、cal コマンドは実行してはならない。
CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムを つくりなさい。CGI として実行した時とシェルから実行した時の結果を比較し なさい。
ヒント:CGI のプログラムから getuid(), getgid(), getgroups() システム・ コールを実行する。uid や gid を文字列で表示したいなら、getpwuid(), getpwuid_r(), getgrgid(), getgrgid_r() を用いる。
getuid(), getgid(), getgroups() システム・コールを用いる代わりにid コマ ンドを実行する方法もある。
ヒント:
main()
{
	ファイルから整数を読み出す(ファイルがなければ0とする)。
	1 加える。
	標準出力(画面)に整数値を表示する。
	ファイルに整数を保存する。
}
Content-Type: は、text/plain にする。
Content-Type: を text/html にする方法もある。
ただし、本文は、HTML として作成する必要がある。
~/public_html 以下にコピーされるこ
とを想定して、整数を保存するファイルを修正する。
必要ならば、初期状態のファイルを作成して、環境に合わせて
モードを適切なものにする(常に chmod 666 は誤り)。
.cgi として、~/public_html 以下にコピーする。
ロックがきちんと動作していることを示しなさい。そのためには、ロックして getchar() 等でキーボードからの指示で(アンロックして)終了するプログラム を作成する方法がある。プログラムが終了すると自動的にアンロックされる。 次のプログラムを参考にしてもよい。
注意: 2010年4月-2014年3月、coins の環境では、www サーバからNFS経由でアクセス しているファイルに対して次のどの方法でも ロックが働く。
永続的なハッシュ表を作成する方法として、dbm ライブラリを用いる方法があ る。これを利用してみなさい。ライブラリ関数としては、dbm_open(), dbm_store(), dbm_fetch(), dbm_delete(), dbm_close()がある。詳しくは、 man dbm を見なさい。