システムプログラム(第9回): Web CGI プログラミング(1)

                                       筑波大学 システム情報系 情報工学域
                                       新城 靖
                                       <yas@cs.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2023/2023-07-26/index.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2023/
http://www.coins.tsukuba.ac.jp/~yas/

捕捉

psコマンド

ps コマンド、kill、grep コマンドは、必ず使えるようにすること。

プロセス数の上限

プロセス数の上限は、システム全体とユーザごとに設定できるのが普通。

Linux (Coins azalea, jelly, violet)

$ sysctl kernel.threads-max [←]
kernel.threads-max = 126245
$ ulimit -a | grep proc [←]
max user processes              (-u) 4096
$ []
macOS
$ sysctl kern.maxproc kern.maxprocperuid [←]
kern.maxproc: 16704
kern.maxprocperuid: 11136
$ ulimit -a | grep proc [←]
max user processes              (-u) 11136
$ []

環境変数

環境変数については、 この講義の前半 でも扱った。

普通のプログラムは、getenv() で環境変数の値を読むだけのことが多い。 環境変数に値を代入するのは、シェルで行なうことが多い。

シェルによる環境変数の設定方法は、次のページを参考にしなさい。

次の環境変数は、覚える。

小さなプログラムの利用

API の理解を深めるために、小さなプログラムを書いて挙動を確かめるとよい。
   1:	/*
   2:	  ~yas/syspro/string/strcmp-null.c --
   3:	  Created on: 2006/06/10 19:23:08
   4:	*/
   5:	
   6:	#include <stdio.h>      /*printf()*/
   7:	#include <string.h>     /* strcmp() */
   8:	
   9:	int
  10:	main( int argc, char *argv[], char *envp[] )
  11:	{
  12:	        int x ;
  13:	
  14:	        x = strcmp(0,".html");
  15:	        printf("%d\n", x );
  16:	}
$ make strcmp-null [←]
cc     strcmp-null.c   -o strcmp-null
strcmp-null.c: In function ‘main’:
strcmp-null.c:14:13: warning: argument 1 null where non-null expected [-Wnonnull]
   14 |         x = strcmp(0,".html");
      |             ^~~~~~
In file included from strcmp-null.c:7:
/usr/include/string.h:156:12: note: in a call to function ‘strcmp’ declared ‘nonnull’
  156 | extern int strcmp (const char *__s1, const char *__s2)
      |            ^~~~~~
$ ./strcmp-null [←]
Segmentation fault
$ []
簡単なことなら、コンパイラが検出して警告を発することもあるが、少し複雑 になると、検出できなくなる。

strrchr()が NULL を返す

次のようなプログラムを書く人はいない。
        if( strcmp(0,".html") == 0 ) ...
次のようなプログラムを書く人は、たくさんいる。
        if( strcmp(strrchr(s,'.'),".html") == 0 ) ...
このようなプログラムに対して、コンパイラは警告を出さない。

一般に、ポインタをreturnする関数が 0 を返すとどうなるかを考える。

今日の重要な話

Web CGI プログラミング

CGIの考え方

Web サーバは、普通は、ファイルに保存された HTML データや画像データを 読み込み、Web ブラウザに返する。 CGI (Common Gateway Interface) では、ファイルが読み込まれる代わりにプログラムが実行され、その実行結果 がブラウザに返される。

Gateway とは、World Wide Web で使われている HTTP というプロトコルへの入 り口という意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることも画像データであることもある。

クライアント、Web サーバ、CGIによるプロセス、ファイル
図? CGIの仕組み

CGI の利用例。クライアントからパラメタを受け取り、プログラムを実行し、 データを保存したり、クライアントに返す内容を変化させる。

CGIの設定(Apache)

情報科学類では Web サーバとして Apache HTTP Server を 用いている。

Apache では、CGI として扱うファイル名のパタンとして、次のようなものが よく使われる(設定により、これ以外も可能)。

cgi-hello.c

cgi-hello.c は、クライアントに HTML で hello. と返すCGI のプログラムで ある。

cgi-hello.cプログラム全体

[cgi-hello.c]
   1:	/*
   2:	  cgi-hello.c --簡単な CGI のプログラム
   3:	  ~yas/syspro/www/cgi-hello.c
   4:	*/
   5:	
   6:	#include <stdlib.h>     /* exit() */
   7:	#include <stdio.h>      /* printf() */
   8:	
   9:	extern void print_header();
  10:	extern void print_content();
  11:	
  12:	int
  13:	main()
  14:	{
  15:	        print_header();
  16:	        print_content();
  17:	}
  18:	
  19:	void
  20:	print_header()
  21:	{
  22:	        printf("Content-Type: text/html\n");
  23:	        printf("\n");
  24:	}
  25:	
  26:	void
  27:	print_content()
  28:	{
  29:	        printf("<HTML><HEAD></HEAD><BODY>\n");
  30:	        printf("hello.\n");
  31:	        printf("</BODY></HTML>\n");
  32:	}
CGI のプログラムは、Apache の場合、標準ではHTTP のヘッダ(一番最初の 「HTTP/1.0 200 OK」 ではなく、2行目以降)を標準出力に出力する所から始 まる。Content-Type: 行は、必ず付ける。空行がヘッダと本分の区切りである。 ここまで行末は、\r\n ではなく \n でよい。 本文には、この場合は、HTML の文書を出力している。

cgi-hello.cの端末からの実行

CGI の例題のプログラムを、端末で実行する。 自分のコンピュータの端末で動かすことも可能。 拡張子は、慣例として .cgi にする。
$ cp ~yas/syspro/www/cgi-hello.c . [←]
(scp s20XXXXX@www.coins.tsukuba.ac.jp:~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-hello.cを、CoinsのWebサーバでCGIとして実行する

CGI の例題のプログラムを CoinsのWebサーバでCGIとして実行することもできる。 https:// でアクセスして実行する時には、Web サーバ(Linux)にログインして てからコンパイルしなさい。
$ ssh www.coins.tsukuba.ac.jp [←]
(プロンプトに含まれているホスト名が変化したことに注意する。)
$ 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 のプログラムを、シェルから実行することもできる。 引数の与え方については、後述する。

詳しくは、補講の 情報科学類での個人のホーム・ページ を見なさい。

Web ブラウザでは、次の URL を開く。

http://www.coins.tsukuba.ac.jp/~ユーザ名/syspro-cgi-examples/cgi-hello.cgi

http:// の代りに https:// も可。

telnet (http:) では、次のようにして実行する。

$ telnet www.coins.tsukuba.ac.jp 80 [←]
Trying 2001:2f8:3a:1711::231:86...
Connected to violet03.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~ユーザ名/syspro-cgi-examples/cgi-hello.cgi HTTP/1.0[←]
Host: www.coins.tsukuba.ac.jp[←]
[←]
GET の代りに POST でもよい。
$ telnet www.coins.tsukuba.ac.jp 80 [←]
Trying 2001:2f8:3a:1711::231:86...
Connected to violet03.coins.tsukuba.ac.jp.
Escape character is '^]'.
POST /~ユーザ名/syspro-cgi-examples/cgi-hello.cgi HTTP/1.0[←]
Host: www.coins.tsukuba.ac.jp[←]
Content-Length: 0[←]
[←]
この講義のページに設置された cgi-hello.cgi ( http://www.coins.tsukuba.ac.jp/~syspro/2023/2023-07-26/cgi-hello.cgi ) の実行例。
$ telnet www.coins.tsukuba.ac.jp 80 [←]
Trying 2001:2f8:3a:1711::231:86...
Connected to violet03.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~syspro/2023/2023-07-26/cgi-hello.cgi HTTP/1.0[←]
Host: www.coins.tsukuba.ac.jp[←]
[←]
HTTP/1.1 200 OK
Date: Mon, 10 Jul 2023 05:56:43 GMT
Server: Apache
Connection: close
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
hello.
</BODY></HTML>
Connection closed by foreign host.
$ []
POST の場合。
$ telnet www.coins.tsukuba.ac.jp 80 [←]
Trying 2001:2f8:3a:1711::231:114...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
POST /~syspro/2023/2023-07-26/cgi-hello.cgi HTTP/1.0[←]
Host: www.coins.tsukuba.ac.jp[←]
Content-Length: 0[←]
[←]
HTTP/1.1 200 OK
Date: Fri, 24 Jun 2023 02:33:17 GMT
Server: Apache
Content-Length: 48
Connection: close
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
hello.
</BODY></HTML>
Connection closed by foreign host.
$ []

CGIとフォーム

ブラウザからサーバに情報を送ってほしい時には、 HTML の フォーム(form)という機能を使う。

HTML記述例

<H2><A id="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="lang" value="C"> C言語 <BR>
    <INPUT type="radio" name="lang" value="Java"> Java言語 <BR>
    <INPUT type="radio" name="lang" value="others"> その他 <BR>
    電子メール: <INPUT type="text" name="email"><BR>
    <INPUT type="submit" value="send"> <INPUT type="reset">
    </P>
</FORM>

<H2><A id="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="lang" value="C"> C言語 <BR>
    <INPUT type="radio" name="lang" value="Java"> Java言語 <BR>
    <INPUT type="radio" name="lang" value="others"> その他 <BR>
    電子メール: <INPUT type="text" name="email"><BR>
    <INPUT type="submit" value="send"> <INPUT type="reset">
    </P>
</FORM>

表示例

CGI の GET メソッドを使う例

姓: 名:
C言語
Java言語
その他
電子メール:

CGI の POST メソッドを使う例

姓: 名:
C言語
Java言語
その他
電子メール:

<FORM></FORM>で括られた部 分が1つのフォームになる。(1つのページに複数のフォームを置くことがで きる。)

<INPUT>の部分が、ブラウザからサーバに送られるデー タを表わす。

<INPUT type="submit">」のボタンが押されると、 ブラウザは、指定されたメソッドを使ってサーバにデータを送る。

HTTPにより送られるデータの形式

method="get"の場合、Web ブラウザは、 普通のファイルを指定する時と同じように、URL に含めて送る。
GET /~syspro/2023/2023-07-26/cgi-printarg.cgi?lastname=name1&firstname=name2&lang=C&email=who%40dom HTTP/1.0←↓
Host: www.coins.tsukuba.ac.jp←↓
←↓
URL の長さの上限で制限されるので、長いデータは送れない。

メソッドとして POST が使われた時、Web ブラウザは、データ をHTTP の本文(ヘッダではない部分、空行の後)に入れて送る。

POST /~syspro/2023/2023-07-26/cgi-printarg.cgi HTTP/1.0←↓
Host: www.coins.tsukuba.ac.jp←↓
Content-Length: 53←↓
←↓
lastname=name1&firstname=name2&lang=C&email=who%40dom

Web サーバからCGIプログラムへのパラメタの受け渡し

Web サーバは、HTTP で GET や POST により要求を受けとる。Web サーバは、 URL の部分を解析し、CGI として認識すると、指定されたプログラムを実行す る。実行された CGI プログラムは、 環境変数標準入力 からデータを受け取ることができる。

CGI で利用できる主な環境変数を以下に示す。
環境変数名 内容
REQUEST_METHOD データを送るのに使われたメソッド。POST か GET。
QUERY_STRING URLの指定で「?」以下に指定された文字列。
SCRIPT_NAME プログラムの名前。
CONTENT_LENGTH POSTメソッドが使われた時、標準入力から読み込めるデータのバイト数
データは、「&」 で区切られた 「名前=」の並びになっている。

例:

lastname=name1&firstname=name2&lang=C&email=who%40dom
GETメソッドが使われた場合、CGI のプログラムは、環境変数 QUERY_STRING からデータを得る。 POSTメソッドが使われた場合、CGI のプログラムは、データを標準入力から受 け取る。データのバイト数は、環境変数CONTENT_LENGTH に指定 されている。

送られてくるパラメタの中に漢字、 「&」、 「;」、 その他 URLで使えない文字が含まれていた場合、 「%hh( hh は2桁の16進数)」という形になっている。 これを パーセント・エンコーディング という。 たとえば、「&」は、 「%26」として送られる。

cgi-printarg.c

次のプログラムは、CGI で実行された時に、その引数を表示するものである。 GET メソッド、および、POST メソッドの両方に対応している。なお、パラメタ の区切りとしては、「&」が使われることを期待している。

シェルからのCGIプログラムの実行

CGI のプログラムをシェルから実行してデバッグすることもできる。

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)
No query string.
</PRE></BODY></HTML>
$ export REQUEST_METHOD=GET [←]
$ export QUERY_STRING='lastname=name1&firstname=name2&lang=C&email=who%40dom' [←]
$ ./cgi-printarg.cgi [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY><PRE>
REQUEST_METHOD=GET
SCRIPT_NAME=(null)
QUERY_STRING=lastname=name1&amp;firstname=name2&amp;lang=C&amp;email=who%40dom
CONTENT_LENGTH=(null)
query_string=lastname=name1&amp;firstname=name2&amp;lang=C&amp;email=who%40dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
qv[3]: email=who%40dom(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&lang=C&email=who%40dom' > 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&amp;firstname=name2&amp;lang=C&amp;email=who%40dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
qv[3]: email=who%40dom(email=who@dom)
</PRE></BODY></HTML>
$ []

cgi-printarg.cのmain()

[cgi-printarg.c]
   1:	
   2:	/*
   3:	  cgi-printarg.c -- CGI プログラムに対する引数を表示するプログラム
   4:	  ~yas/syspro/www/cgi-printarg.c
   5:	*/
   6:	
   7:	#include <stdlib.h>     /* getenv(), malloc() */
   8:	#include <stdio.h>      /* printf() */
   9:	#include <string.h>     /* strlen() */
  10:	#include <sys/types.h>  /* read() */
  11:	#include <sys/uio.h>    /* read() */
  12:	#include <unistd.h>     /* read() */
  13:	#include <ctype.h>      /* isdigit() */
  14:	
  15:	extern void  print_header(void);
  16:	extern void  print_content(void);
  17:	extern char *get_query_string();
  18:	extern char *read_query_string();
  19:	extern void  safe_printenv( char *name );
  20:	extern void  safe_print_string( char *str );
  21:	extern char *html_escape( char *str );
  22:	extern char *decode_url( char *str );
  23:	extern char *getparam( int qc, char *qv[], char *name );
  24:	
  25:	extern int string_split( char *str, char del, int *countp, char ***vecp  );
  26:	extern void free_string_vector( int qc, char **vec );
  27:	extern int countchr( char *s, char c );
  28:	
  29:	int
  30:	main()
  31:	{
  32:	        print_header();
  33:	        print_content();
  34:	}
  35:	
  36:	void
  37:	print_header()
  38:	{
  39:	        printf("Content-Type: text/html\n");
  40:	        printf("\n");
  41:	}
  42:	
  43:	void
  44:	print_content()
  45:	{
  46:	        char  *query_string ;
  47:	        int    qc ;
  48:	        char **qv ;
  49:	        int    i ;
  50:	
  51:	        printf("<HTML><HEAD></HEAD><BODY><PRE>\n");
  52:	
  53:	        safe_printenv("REQUEST_METHOD");
  54:	        safe_printenv("SCRIPT_NAME");
  55:	        safe_printenv("QUERY_STRING");
  56:	        safe_printenv("CONTENT_LENGTH");
  57:	
  58:	        query_string = get_query_string();
  59:	        printf("query_string=");
  60:	        safe_print_string( query_string ); printf("\n");
  61:	
  62:	        if( query_string == NULL )
  63:	        {
  64:	                printf("No query string.\n");
  65:	        }
  66:	        else if( string_split( query_string,'&',&qc, &qv ) < 0 )
  67:	        {
  68:	                printf("Error while parsing query string\n");
  69:	        }
  70:	        else
  71:	        {
  72:	                for( i=0 ; i<qc ; i++ )
  73:	                {
  74:	                        char *decoded ;
  75:	                        printf("qv[%d]: ",i);
  76:	                        safe_print_string(qv[i]);
  77:	                        decoded = decode_url( qv[i] );
  78:	                        printf("("); safe_print_string( decoded ); printf(")\n");
  79:	                        if( decoded )
  80:	                                free( decoded );
  81:	                }
  82:	                free_string_vector( qc, qv );
  83:	        }
  84:	
  85:	        printf("</PRE></BODY></HTML>\n");
  86:	        if( query_string )
  87:	                free( query_string );
  88:	}
  89:	
これは、標準の 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) string_split()とfree_string_vector()の利用 で使ったものと同じものである。 分解した結果は、main() の引数である int argc, char *argv[] と同じ形になっ ている。qc が、argc, qv が argv に対応する。

for ループでは、qv[] をsafe_print_string() で標準出力に出力している。 さらに、decode_url() で、%hh (%hhは16進数)の形式のバイト値を、元に戻すものであ る。 たとえば、who%40domの をwho@domに戻している。

free_string_vector() は、string_split() で malloc() したものを free() するための手続きである。

get_query_string()

get_query_string() は、CGI でプログラムが実行された時に パラメタが含まれた文字列を読み込むプログラムである。
  90:	char *
  91:	get_query_string()
  92:	{
  93:	        char *request_method, *query_string;
  94:	        request_method = getenv("REQUEST_METHOD");
  95:	        if( request_method == 0 )
  96:	                return( 0 );
  97:	        else if( strcmp(request_method,"GET") == 0 )
  98:	        {
  99:	                query_string = getenv("QUERY_STRING");
 100:	                if( query_string == 0 )
 101:	                        return( 0 );
 102:	                else
 103:	                        return( strdup(query_string) );
 104:	        }
 105:	        else if( strcmp(request_method,"POST") == 0 )
 106:	        {
 107:	                return( read_query_string() );
 108:	        }
 109:	        else
 110:	        {
 111:	                printf("Unknown method: ");
 112:	                safe_print_string( request_method );
 113:	                printf("\n");
 114:	                return( 0 );
 115:	        }
 116:	}
 117:	
get_query_string() は、クライアントから送られてきたパラメタを、1つの 文字列として受け取るものである。GET メソッドの場合は、単に環境変数 REQUEST_METHOD を読めばよい。ただし、文字列の長さの上限がきつい。

strdup() は、文字列をコピーするものである。使い終わったら、free() で解 放する。

POST メソッドの場合は、read_query_string() で続きの処理を行う。

read_query_string()

read_query_string() は、CGI でPOSTメソッドが使われた時にパラメタを標準 入力から読み込む。
 118:	char *
 119:	read_query_string()
 120:	{
 121:	        int   clen ;
 122:	        char *content_length ;
 123:	        char *buf ;
 124:	
 125:	        content_length = getenv("CONTENT_LENGTH");
 126:	        if( content_length == 0 )
 127:	        {
 128:	                return( 0 );
 129:	        }
 130:	        else
 131:	        {
 132:	                clen = strtol( content_length,0,10 );
 133:	                buf = malloc( clen + 1 );
 134:	                if( buf == 0 )
 135:	                {
 136:	                        printf("read_query_string(): no memory\n");
 137:	                        exit( -1 );
 138:	                }
 139:	                if( read(0,buf,clen) != clen )
 140:	                {
 141:	                        printf("read error.\n");
 142:	                        exit( -1 );
 143:	                }
 144:	                buf[clen] = 0 ;
 145:	                return( buf );
 146:	        }
 147:	}
 148:	
CONTENT_LENGTH には、10進数でバイト数が含まれている。

Webブラウザの表示例

REQUEST_METHOD=GET
SCRIPT_NAME=/~yas/coins/syspro-2023/2023-07-26/cgi-printarg.cgi
QUERY_STRING=lastname=name1&firstname=name2&lang=C&email=who%40dom
CONTENT_LENGTH=(null)
query_string=lastname=name1&firstname=name2&lang=C&email=who%40dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
qv[3]: email=who%40dom(email=who@dom)

REQUEST_METHOD=POST
SCRIPT_NAME=/~yas/coins/syspro-2023/2023-07-26/cgi-printarg.cgi
QUERY_STRING=
CONTENT_LENGTH=53
query_string=lastname=name1&firstname=name2&lang=C&email=who%40dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
qv[3]: email=who%40dom(email=who@dom)

cgi-printarg.cgiの内部関数

以下の内容は、飛ばしてよい。

safe_printenv()、safe_print_string()、html_escape()

safe_printenv() は、環境変数をgetenv() で検索し、その値を safe_print_string() で標準出力に出力している。

safe_print_string() は、html_escape() を使って文字列を安全なもの にして表示する。

html_escape()(次回説明)は、 クライアントから送られたような信頼できない文字列を クライアントに送り返す時に、不用意に JavaScript 等がクライアント側の ブラウザで動作しないようにするための関数である。 たとえば、<SCRIPT> のような文字列がクライアントから送られて きたとしても、クライアントには「&lt;SCRIPT&gt;」と 返すだけで、スクリプトは実行されない。

decode_url()

decode_url() は、「%hh(2桁の16進数)」を元のバイトに 戻すものである。isxdigit() は、16進数の文字として正しいかを調べてい る。strtol() で基底を 16 として変換している。 たとえば、"%40"を 0x40 ('@') に戻している。 その他に、「'+'」を「' '」に変換している。

Apacheの機能

アクセスログとエラーログ

CGI のプログラムを作成する時、エラーがおきたとしても、ブラウザの画面にはエラー・メッセージは表示されない。 その時は、アクセスログやエラーログを見てデバッグする。

Web サーバは、ページがアクセスされる度に記録を残す。この記録をアクセス ログという。アクセスログは、情報科学類のサーバ www (www.coins.tsukuba.ac.jp) では、次の場所にある。

http
/var/log/httpd/access_log_*
https
/var/log/httpd/ssl_access_log_*
この中で、数字が一番大きなもの、または、日付が新しいものが、 現在増加しているログである。
$ cd /var/log/httpd/ [←]
$ ls    access_log_* | tail -1 [←]
$ ls -t access_log_* | head -1 [←]
以下の例では、現在増加しているログは access_log_20230705 である。
$ cd /var/log/httpd/ [←]
$ ls access_log_*     | tail -1 [←]
access_log_20230705
$ ls  -t access_log_* | head  -1 [←]
access_log_20230705
$ []
以下は、アクセス・ログの内容の例である。見やすいように改行を入れているが 実際には1行が1つのページの転送を意味する。

2001:2f8:3a:1711::231:16 - - [11/Jul/2023:10:07:31 +0900] "GET /~syspro/2023/202
3-07-26/index.html HTTP/1.1" 200 68295 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_
64; rv:109.0) Gecko/20100101 Firefox/115.0"

2001:2f8:3a:1711::231:16 - - [11/Jul/2023:10:07:31 +0900] "GET /~syspro/2023/202
3-07-26/images/www-cgi.png HTTP/1.1" 200 15499 "http://www.coins.tsukuba.ac.jp/~
syspro/2023/2023-07-26/index.html" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:1
09.0) Gecko/20100101 Firefox/115.0"

2001:2f8:3a:1711::231:16 - - [11/Jul/2023:10:09:13 +0900] "GET /~syspro/2023/202
3-07-26/cgi-printarg.cgi?lastname=name1&firstname=name2&lang=C&email=who%40dom H
TTP/1.1" 200 443 "http://www.coins.tsukuba.ac.jp/~syspro/2023/2023-07-26/index.h
tml" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/1
15.0"

2001:2f8:3a:1711::231:16 - - [11/Jul/2023:10:09:38 +0900] "POST /~syspro/2023/20
23-07-26/cgi-printarg.cgi HTTP/1.1" 200 375 "http://www.coins.tsukuba.ac.jp/~sys
pro/2023/2023-07-26/index.html" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.
0) Gecko/20100101 Firefox/115.0"
このログは、設定ファイル /etc/httpd/conf/httpd.conf にある CustomLog 等の設定による。
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "|/usr/sbin/rotatelogs -l /etc/httpd/logs_http/access_log_%Y%m%d 20M" combined
combined という名前が付けられたログのフォーマットの意味は、以下の通りである。

アクセスログの他に、エラーが起きた時にもログが作られる。これを エラーログ という。エラーログは、情報科学類のサーバ www (www.coins.tsukuba.ac.jp) では、次の場所にある。

http
/var/log/httpd/error_log_*
https
/var/log/httpd/ssl_error_log_*
CGI のプログラムをデバッグする時には、このエラーログを見て原因を探る。

その他に、次のようなログを取ることもある。

ssl_request_log (coins では /var/log/httpd/ssl_request_log_*)
SSL のプロトコル等
/var/log/httpd/suexec.log 等 (coins では /var/log/secure)
suExec を使っていた時のログ
自分が単に Web ブラウザを使っているだけでも、さまざまな情報をサーバに 送っているとに注意しなさい。

CGI プログラムを実行するプロセスの権限

シェルからプロセスを生成しプログラムを実行する時には、その新しいプロセ スの権限(ユーザ属性、getuid() の結果)は、シェルのものからコピーされ る。すなわち、新しいプロセスはシェルと同じユーザの権限で動作する。CGI の場合、遠隔の Web ブラウザのユーザは、その Web サーバにログインする権 限はないが、CGI のプログラムを実行することができる。そのプログラムを、 どのユーザの権限で実行する方法としては、大きく次の2つ方法がある。 どちらの方式にも一長一短がある。Web サーバ・プロセスの権限で動作させた 場合、次のような長所と短所がある。

suEXEC を使う方法の場合、長所と短所は逆になる。

ステータス行も含むヘッダ(Apache non-parsed headers)

Apache では、標準では、サーバが最初のステータス行「HTTP/1.1 200 OK」を Apache が作成している。Status:という行を含めることで、ステータ ス行を記述することもできる。Status:がない場合、Location: というヘッダがあれば、302 Found (redirect)、それ以外の場合は、200 OKが返される。それ以外のヘッダも、Apache が自動的に生成して返す。

ステータス行も含めて、完全なヘッダを CGI で作成して返すこともできる。 この方法を、Apache では、nph (non-parsed headers) と呼んでいる。

Apache で、nph を使うには、nph- で始まるファイル名を用いる。こ れを使うと、Apache は、CGI プログラムが作成したヘッダを一切解釈(parse) せず、そのままクライアントに返す。

CGIでのその他の注意事項

ファイルのロック

練習問題と課題

練習問題(901) 「出席」ボタンによるIPアドレスの調査

https://www.coins.tsukuba.ac.jp/~syspro/2023/,この講義のトップページ に設置された「出席ボタン」を押しなさい。 その画面に表示された IP アドレスを調べなさい。 その IP アドレスは、Web サーバが認識した Web ブラウザが動いているコンピュータの IP アドレスである。 アドレス変換のため、以下の方法で調べたものと異なっていることがある。

なお、出席ボタンは、何度押しても問題がない。授業時間外に押しても問題はない。

練習問題(902) ifconfigコマンドによるIPアドレスの調査(macOS,Unix系)

ifconfig コマンドは、Unix 系のオペレーティング・シス テムでネットワーク・インタフェース(interface)の設定を行う(configure)を 行うコマンドである。ifconfig コマンドを利用すると、そのコンピュータのIP アドレスを 表示することができる。

ifconfig コマンドを使って、Coins自分がよく利用しているコンピュータの IP アドレス を調べなさい。

$ ifconfig -a [←]
$ ifconfig en0 [←]
macOS の場合、en0 や en1 の項目の inet の次に IPv4 のIP アドレスが表示 される。inet6 の次に、IPv6 の IPアドレスが表示される。IPv6 の IP アドレ スは、複数表示されることが一般的である。IPv4 の IP アドレスは、1 個のこ とが多いが、複数表示されることもある。 (en0 や en1 の項目は、システムによって異なる。eth0 や eth1 のような 名前のこともある。)

次の例では、IPv4 の IP アドレスは、inet に続く 130.158.230.30 である。IPv6 の IP アドレスは、inet6 に続 く 2001:2f8:3a:1711::230:30,やfe80::1410:bd29:d2af:b6ae である。(実行するコンピュータによって、異なる。)

$ ifconfig en0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        ether 38:f9:d3:02:71:66
        inet6 fe80::1410:bd29:d2af:b6ae%en0 prefixlen 64 secured scopeid 0x6
        inet6 2001:2f8:3a:1711::230:30 prefixlen 64
        inet 130.158.230.30 netmask 0xfffffe00 broadcast 130.158.231.255
        nd6 options=201<PERFORMNUD,DAD>
        media: autoselect
        status: active
$ 
次の例では、IPv6 のアドレスとして、多数の一時アドレス(temporary) が現 れている。Web サーバをアクセスする時には、クライアントはプライバシ保護 のためにこの一時アドレスを利用することがある。その場合、Web サーバのア クセスログを検索する時には、この一時アドレスも調べる必要がある。
$ ifconfig en0
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        options=10b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV>
        ether 68:5b:35:9b:51:f9
        inet6 fe80::1833:ea4f:2089:a339%en0 prefixlen 64 secured scopeid 0x5
        inet6 2001:2f8:3a:1711:182b:4524:1f08:7660 prefixlen 64 autoconf secured
        inet6 2001:2f8:3a:1711:91e2:c6f3:e3da:2f54 prefixlen 64 deprecated autoconf temporary
        inet 130.158.231.9 netmask 0xfffffe00 broadcast 130.158.231.255
        inet6 2001:2f8:3a:1711:59d9:d716:d9ba:df17 prefixlen 64 deprecated autoconf temporary
        inet6 2001:2f8:3a:1711:ed56:b02e:7a81:c9a2 prefixlen 64 deprecated autoconf temporary
        inet6 2001:2f8:3a:1711:6816:37bc:89b3:7d78 prefixlen 64 deprecated autoconf temporary
        inet6 2001:2f8:3a:1711:ad1c:ecea:66da:f5a0 prefixlen 64 deprecated autoconf temporary
        inet6 2001:2f8:3a:1711:f0ee:46a2:8efd:453c prefixlen 64 autoconf temporary
        nd6 options=201<PERFORMNUD,DAD>
        media: autoselect (1000baseT <full-duplex,flow-control,energy-efficient-ethernet>)
        status: active
$ 

ifconfig コマンドは、macOS だけでなく、Linux を含む Unix 系のオペレー ティング・システムで利用できる。コマンドが見つからない時には、次の ようにコマンドを絶対パス名で打ってみなさい。 あるいは、 ipコマンド を試しなさい。

$ /sbin/ifconfig -a [←]
他の場所にないか、man ifconfig でマニュアルを表示して、探しなさい。

Linux で、ifconfig コマンドがインストールされていない場合は、net-tools パッケージをインストールしなさい。

練習問題(903) ipコマンドによるIPアドレスの調査(Linux)

Linux では ip コマンドを使うと、そのコンピュータのIP アドレスを表示することができる。 次のようなコマンドを実行して確認しなさい。
$ ip address
$ ip a
$ ip a show dev インタフェース名

練習問題(904) ipconfigコマンドによるIPアドレスの調査(Windows)

Windows では ipconfig コマンドを使うと、そのコンピュータのIP アドレスを表示することができる。
PS C:\Users\ユーザ名> ipconfig

Windows IP 構成


イーサネット アダプター Ethernet0:

   接続固有の DNS サフィックス . . . . .: localdomain
   リンクローカル IPv6 アドレス. . . . .: fe80::f117:dd09:1481:188a%3
   IPv4 アドレス . . . . . . . . . . . .: 172.16.68.136
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 172.16.68.2
PS C:\Users\ユーザ名>

練習問題(905) hostnameコマンド

hostname コマンドを使って、自分が使っているコンピュータの名前を調べなさい。
$ hostname [←]
azalea16
$ []
$ hostname [←]
violet03
$ []
hostname コマンドは、DNS で使う長いドメイン名を表示することもあるが、短 い名前や、それとは関係ない名前を表示することもある。

練習問題(906) hostコマンド

host コマンドを使って、IPv4、および、IPv6 の IP アドレスを調べなさい。
$ host azalea16.coins.tsukuba.ac.jp [←]
azalea16.coins.tsukuba.ac.jp has address 130.158.231.16
azalea16.coins.tsukuba.ac.jp has IPv6 address 2001:2f8:3a:1711::231:16
$ host violet03.coins.tsukuba.ac.jp [←]
violet03.coins.tsukuba.ac.jp has address 130.158.231.86
violet03.coins.tsukuba.ac.jp has IPv6 address 2001:2f8:3a:1711::231:86
$ host www.coins.tsukuba.ac.jp [←]
www.coins.tsukuba.ac.jp is an alias for violet03.coins.tsukuba.ac.jp.
violet03.coins.tsukuba.ac.jp has address 130.158.231.86
violet03.coins.tsukuba.ac.jp has IPv6 address 2001:2f8:3a:1711::231:86
$ []
次のホストについて、IP アドレスを調べなさい。

練習問題(907) Web サーバ上のデータの観察

補講の練習問題 Web サーバ上のデータの観察 を行いなさい。

練習問題(908) アクセスログの観察

次の補講の練習問題を行いなさい。

練習問題(909) cgi-opval.cgi

次の CGI プログラムをコマンドラインから実行しなさい。

$ cp ~yas/syspro/www/cgi-opval.c . [←]
$ cc cgi-opval.c -o cgi-opval.cgi [←]
$ ./cgi-opval.cgi [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY><PRE>
No query string.
</PRE></BODY></HTML>
$ export REQUEST_METHOD=GET [←]
$ export QUERY_STRING='op=up' [←]
$ ./cgi-opval.cgi [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY><PRE>
op: [up]
val: [(null)]
</PRE></BODY></HTML>
$ export QUERY_STRING='op=set&val=100' [←]
$ ./cgi-opval.cgi [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY><PRE>
op: [set]
val: [100]
</PRE></BODY></HTML>
$ []

次のようにパラメタを設定して実行してみなさい。

余裕があれば、REQUEST_METHOD=POST についても、同様に実行してみなさい。

余裕があれば、フォームを定義し、何かデータを送ってみなさい。 coins で CGI を実行する場合、CGI の実行形式を Web サーバ (http://www.coins.tsukuba.ac.jp) に ssh でログインした上で作成すること。 azalea, jelly (Ubuntu) で作成した実行形式は、Web サーバ (Red Hat) では動作しないことがある。

http://www.coins.tsukuba.ac.jp/~syspro/2023/2023-07-26/cgi-opval.cgi

たとえば、次のような記述を含む HTML ファイルを作成し、 ~/public_html/ 以下に置く。

<FORM ACTION="cgi-opval.cgi" method="get">
    <INPUT type="radio" name="op" value="up"> Up <BR>
    <INPUT type="radio" name="op" value="set"> Set <INPUT type="text" name="val"> <BR>
    <INPUT type="submit">
    <INPUT type="reset">
</FORM>
Up
Set
また、余裕があれば、method="post" についても同様に行ってみなさい。

練習問題(910) ファイルにデータを保持するカウンタ

次のプログラムは、ファイルに 32 ビットの整数値を保持するようなカウンタ のプログラムである。ソースコードをコピーし、コンパイルして実行し、動作 を確認しなさい。 次のような引数を与えて実行してみなさい。 このプログラムは、 構造体の入出力 の手法を用いてカウンタの値をファイルに保持している。 ただし、構造体としては、要素が1つしかないので、構造体を定義しないで int をそのまま読み書きしている。 カウンタ値を保存するファイルの名前は、プログラムの中に定数として埋め込んである。

実行した結果、カウンタ値を保存するファイルが作成されることを確認しなさい。 ファイルの大きさと内容を od コマンドや hexdump コマンド等で確認しなさい。

$ ./file-counter get
5
$ ls -l file-counter-value.data
-rw-r--r-- 1 yas prof 4  7月  7 14:43 file-counter-value.data
$ od -xc file-counter-value.data
0000000    0005    0000
        005  \0  \0  \0
0000004
$ od -t d4 -t d1 file-counter-value.data
0000000                   5
           5    0    0    0
0000004
$ hexdump -C --format '"%x\n"' file-counter-value.data
00000000  05 00 00 00                                       |....|
5


00000004
$ 

練習問題(911) CGIによるカウンタ

この課題を解く前に必ず 練習問題 cgi-opval.cgi練習問題 ファイルにデータを保持するカウンタ を行いなさい。レポートには、その結果を含めなくて良い。

次のような次のようなパラメタを取る CGI のプログラムを C 言語で作成しなさい。

この CGI は、ファイルからカウンタ値を読み出し、op の値により、次のよう な動作を行う。 いずれの場合も、更新後のカウンタ値を表示し、 更新後のカウンタ値をファイルに保存する。

ヒント:

  1. CGI としては、練習問題 cgi-opval.cgi で示したようなフォームを想定する。 cgi-opval.c で、余計な表示をなくす。
  2. string_split() で分解した後、"op=" や "val=" があるパラメタを検索 する。
         char *op_s,*val_s;
         op = getparam(qc,qv,"op");
         val_s = getparam(qc,qv,"val");
    
    getparam() は、次のような関数であり、 cgi-opval.cgiのソースコード に含まれている。
    char *getparam(int qc, char *qv[], char * varname ) [独自]
    第1引数と第2引数は、 string_split() で作成したカウントと文字列のベクタである。 第3引数が"name"の時、 この関数は、 "name=value" というパラメタを 見つけ、"value"を返す。 見つからなかったら NULL を返す。 この関数の使い方は、ライブラリ関数 getenv() と似ているので、 それも参考にしなさい。
  3. 「不正」な引数が指定された場合には、エラー・メッセージを表示する。 ただし、"val=100xxx" のように、数の後ろに数字ではないものが含まれて いたとしても、それを無視してよいものとする。 opが"set"の場合には、valは必須とする。 opがそれ以外の場合には、valはあっても無くてもよい。 "val="と空文字列が指定された場合には、0と解釈してもよいし、エラーにしても良い。 val として得られた文字列を、strtol() 等で整数に変換する。
  4. opの値に関わらず、更新後のカウンタ値を表示する。
  5. カウンタ値を更新したら、必ずファイルに保存する。
作成したプログラムを、シェルから実行 して動作を確認する。
$ export REQUEST_METHOD=GET [←]
$ export QUERY_STRING='op=up&val=' [←]
$ ./a.out [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
1
</BODY></HTML>
$ ./a.out [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
2
</BODY></HTML>
$ ./a.out [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
3
</BODY></HTML>
$ QUERY_STRING='op=set&val=100' [←]
$ ./a.out [←]
Content-Type: text/html

<HTML><HEAD></HEAD><BODY>
100
</BODY></HTML>
$ []
なお、一度設定した環境変数の値は、シェルを終了するまで残っている。 a.out を実行するたびに設定する必要はない。 一度 export した環境変数は、設定のたびに export する必要はない。

この課題では、「正しい」「不正」の定義は、レポート提出者に任せる。プロ グラムで扱えるものを「正しい」として良い。どのようなものを正しい、不正 と考えたかを、レポートに記載しなさい。また、実行結果としては、正しいパ ラメタを与えた時と、不正なパラメタを与えた時の両方を含めなさい。

一般に、「正しい」は定義可能で、それ以外を不正とする。不正なものを網羅 的にチェックすることはできない。負の数は、正しいとしても不正としてもど ちらでも良い。int の範囲で扱えないものを不正としても良い。オーバーフロー 等を考慮しなくて良い。

2023/07/26追加。 この CGI プログラムは、 練習問題 ファイルにデータを保持するカウンタ とは異なり、init がない。 CGI で使うファイルは、あらかじめ別のプログラムで作成すると良い。 あるいは、ファイルが存在しない場合、初期値が0として扱っても良い。 あるいは、op=init で init の動作をさせても良い。

練習問題(912) CGIによるカウンタ(coins Web サーバへの設置)

練習問題 CGIによるカウンタ を coins の Web サーバ http://www.coins.tsukuba.ac.jp で CGI として動作させなさい。 まず、CGI の実行形式を Web サーバ に ssh でログインした上で作成すること。 azalea, jelly (Ubuntu) で作成した実行形式は、Web サーバ (Red Hat) では動作しないことがある。
  1. 実行形式(以下の例では a.out) を ~/public_html の下にコピーする。例:
    $ cc cgi-counter.c [←]
    $ mkdir ~/public_html/syspro-cgi-examples [←]
    $ cp a.out ~/public_html/syspro-cgi-examples/cgi-counter.cgi [←]
    
  2. Web ブラウザで次のような URL を開く。例:
    http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/cgi-counter?op=up
    http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/cgi-counter?op=set&val=100
    
  3. "arg1=10&arg2=20"のように送るようなフォームを含むHTML ファ イルを作成する。 例:
    $ emacs ~/public_html/syspro-cgi-examples/calc.html [←]
    
  4. Web ブラウザでフォームを含む HTML ファイルを開き、実行する。例:
    http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.html
    
レポートには、次の結果を含めなさい。

練習問題(913) CGIによるカウンタ(フォーム生成)

練習問題 CGIによるカウンタ(coins Web サーバへの設置) では、HTML でフォームを記述していた。 C 言語で記述された CGI プログラムによりフォームを生成する機能を付けなさい。 次のような機能を付加しなさい。

練習問題(914) CGIによるカウンタ(ロック付き)

練習問題 CGIによるカウンタ(coins Web サーバへの設置) で、ファイルに対するロックを行うようにしないさい。

ロックがきちんと動作していることを示しなさい。そのためには、ロックして getchar() 等でキーボードからの指示で(アンロックして)終了するプログラム を作成する方法がある。プログラムが終了すると自動的にアンロックされる。 次のプログラムを参考にしてもよい。

注意: 2023年4月-2028年3月、coins の環境では、www サーバからNFS経由でアクセス しているファイルに対して次のどの方法でも ロックが働く

他の環境では、NFS経由でのロックがうまく働かないこともあるので、使う前に 確認すること。2009年2月までの環境では、flock() や fcntl() が動作しなかっ た。

なお、ファイルにデータを保持するカウンタの file-counter.cは、up や set の時にカウンタ値を保存するファイルを 2 回 開いている。この方法では、単純にカウンタ値を保存するファイルをロックの 対象することはできない。ファイルを閉じた時には、ロックが解除されるから である。この問題を解決するには、次のような方法が考えられる。

練習問題(915) CGIプログラムの実行権限

CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムを つくりなさい。CGI として実行した時とシェルから実行した時の結果を比較し なさい。

ヒント:CGI のプログラムから getuid(), getgid(), getgroups() システム・ コールを実行する。uid や gid を文字列で表示したいなら、getpwuid(), getpwuid_r(), getgrgid(), getgrgid_r() を用いる。

getuid(), getgid(), getgroups() システム・コールを用いる代わりにid コマ ンド、whoami コマンド、groups コマンド等を実行する方法もある。

練習問題(916) ハッシュ表を使うライブラリ関数(C)

C言語でメモリ中のハッシュ表を使うライブラリ関数 hcreate(), hsearch(), hdestroy() について調べなさい。

練習問題(917) 永続的ハッシュ表

永続的なハッシュ表とは、メモリ中のハッシュ表と同様に、キーを指定して値 をアクセスするための機能を提供するが、データはファイルに保存されている ので、次に同じプログラムを実行した時も、そのまま残されている所が異なる。 具体的な内部のアルゴリズムとしては、ハッシュの他にB木もよく使われる。 一般的なデータベースの場合、いくつかの制約(integrity rule)を満たすこと が重要となるが、永続的ハッシュ表の場合には、特にそのような制約はない。

永続的なハッシュ表を作成する方法として、gdbm (The GNU database manager) ライブラリ を用いる方法があ る。これを利用してみなさい。ライブラリ関数としては、C言語では、gdbm_open(), gdbm_store(), gdbm_fetch(), gdbm_delete(), gdbm_close(), gdbm_firstkey(), gdbm_nextkey() がある。詳しくは、 man gdbm を見なさい。

gdbm ライブラリは、Ruby 言語, Python 言語、その他の言語でも利用できる。

練習問題(918) 永続的ハッシュ表にデータを保持するカウンタ、CGIプログラム

練習問題 ファイルにデータを保持するカウンタ で、カウンタ値を保持するのに通常のファイルではなく、永続的ハッシュ表を 用いるようにしなさい。 同様に、CGI のプログラムを、永続的ハッシュ表を用いて書き直しなさい。

練習問題(919) 投票プログラム

練習問題 CGIによるカウンタ では、単に1つの値をカウントしていただけであった。そうではなくて、複数 の候補の選択肢を表示し、その中から選択して、投票できるようにしなさい。

その時点での投票結果は、即座に表示することでも良い。また、1人で複数回 投票可能として良いし、ユーザ認証した結果を用いて、1回だけしか投票を許 さないか、最後に投票した結果だけを採用することにしても良い。選択肢には ない候補に投票する機能を付加しても良い。

カウンタ値を保持する方法としては、 永続的ハッシュ表 を用いなさい。


Last updated: 2023/07/26 10:12:20
Yasushi Shinjo / <yas@cs.tsukuba.ac.jp>