筑波大学 システム情報系 情報工学域
新城 靖
<yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~syspro/2020/2020-06-24
/index.html
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~syspro/2020/
http://www.coins.tsukuba.ac.jp/~yas/
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
Segmentation fault: 11
$
if( strcmp(0,".html") == 0 ) ...
次のようなプログラムを書く人は、たくさんいる。
if( strcmp(strrchr(s,'.'),".html") == 0 ) ...
一般に、ポインタをreturnする関数が 0 を返すとどうなるかを考える。
Gateway とは、World Wide Web で使われている HTTP というプロトコルへの入 り口という意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることも画像データであることもある。
図? CGIの仕組み
CGI の利用例。クライアントからパラメタを受け取り、プログラムを実行し、 データを保存したり、クライアントに返す内容を変化させる。
情報科学類には、2つのサーバ・プロセスが動作している。
CGI になるファイル名のパタンの例(設定により、これ以外も可能)。
/cgi-bin/name /dir/name.cgi
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 の文書を出力している。
$ ssh www.coins.tsukuba.ac.jp
(プロンプトに含まれているホスト名が変化したことに注意する。)
$ mkdir ~/public_html/htdocs/syspro-cgi-examples
$ cd ~/public_html/htdocs/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 のプログラムを、シェルから実行することもできる。
引数の与え方については、後述する。
http://www.coins.tsukuba.ac.jp/~ユーザ名/syspro-cgi-examples/cgi-hello.cgi
~/public_html/htdocs/ 以下の場合は、http:// 。
~/public_html/secure_htdocs/ 以下の場合は、https:// 。
syspro-cgi-examples は、上の mkdir と合わせる。
telnet では、次のようにして実行する。
$ telnet www.coins.tsukuba.ac.jp 80
Trying 130.158.231.114...
Connected to viola5.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 130.158.231.114...
Connected to viola5.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/2020/2020-06-24/cgi-hello.cgi
)
の実行例。
$ telnet www.coins.tsukuba.ac.jp 80
Trying 2001:2f8:3a:1711::231:114...
Connected to www.coins.tsukuba.ac.jp.
Escape character is '^]'.
GET /~syspro/2020/2020-06-24/cgi-hello.cgi HTTP/1.0
Host: www.coins.tsukuba.ac.jp
HTTP/1.1 200 OK
Date: Mon, 17 Jun 2020 06:49:00 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/2020/2020-06-24/cgi-hello.cgi HTTP/1.0
Host: www.coins.tsukuba.ac.jp
Content-Length: 0
HTTP/1.1 200 OK
Date: Mon, 17 Jun 2020 06:53:27 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 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>
<FORM>と</FORM>で括られた部
分が1つのフォームになる。(1つのページに複数のフォームを置くことがで
きる。)
<INPUT>の部分が、ブラウザからサーバに送られるデー
タを表わす。
「<INPUT type="submit">」のボタンが押されると、
ブラウザは、指定されたメソッドを使ってサーバにデータを送る。
method="get"の場合、Web ブラウザは、
普通のファイルを指定する時と同じように、URL に含めて送る。
GET /~syspro/2020/2020-06-24/cgi-printarg.cgi?lastname=name1&firstname=name2&lang=C&email=who@dom HTTP/1.0←↓
Host: www.coins.tsukuba.ac.jp←↓
←↓
URL の長さの上限で制限されるので、長いデータは送れない。
メソッドとして POST が使われた時、Web ブラウザは、データ
をHTTP の本文(ヘッダではない部分、空行の後)に入れて送る。
POST /~syspro/2020/2020-06-24/cgi-printarg.cgi HTTP/1.0←↓
Host: www.coins.tsukuba.ac.jp←↓
Content-Length: 91←↓
←↓
lastname=name1&firstname=name2&lang=C&email=who@u-ust.ac.jp←↓
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@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)
No query string.
</PRE></BODY></HTML>
$ export REQUEST_METHOD=GET
$ export QUERY_STRING='lastname=name1&firstname=name2&lang=C&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&lang=C&email=who@dom
CONTENT_LENGTH=(null)
query_string=lastname=name1&firstname=name2&lang=C&email=who@dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
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&lang=C&email=who@dom' > data
$ wc data
1 1 52 data
$ export CONTENT_LENGTH=51
$ ./cgi-printarg.cgi < data
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
REQUEST_METHOD=POST
SCRIPT_NAME=(null)
QUERY_STRING=(null)
CONTENT_LENGTH=51
query_string=lastname=name1&firstname=name2&lang=C&email=who@dom
qv[0]: lastname=name1(lastname=name1)
qv[1]: firstname=name2(firstname=name2)
qv[2]: lang=C(lang=C)
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: */
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進数)の形式を、バイトに元に戻すものであ
る。
free_string_vector() は、string_split() で malloc() したものを free() するための手続きである。
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() で続きの処理を行う。
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進数でバイト数が含まれている。
REQUEST_METHOD=GET SCRIPT_NAME=/~syspro/2020/2020-06-24/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=/~syspro/2020/2020-06-24/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)
safe_print_string() は、html_escape() を使って文字列を安全なもの にして表示する。
html_escape()(次回説明)は、 クライアントから送られたような信頼できない文字列を クライアントに送り返す時に、不用意に JavaScript 等がクライアント側の ブラウザで動作しないようにするための関数である。 たとえば、<SCRIPT> のような文字列がクライアントから送られて きたとしても、クライアントには「<SCRIPT>」と 返すだけで、スクリプトは実行されない。
%hh(2桁の16進数)」を元のバイトに
戻すものである。isxdigit() は、16進数の文字として正しいかを調べてい
る。strtol() で基底を 16 として変換している。
その他に、「'+'」を「' '」に変換している。
/var/log/httpd/access*
/var/log/httpd/ssl_access*
$ cd /var/log/httpd/
$ ls access* | tail -1
$ ls -t access* | head -1
以下の例では、現在増加しているログは
access_log_20200621 である。$ cd /var/log/httpd/
$ ls access* | tail -1
access_log_20200621
$ ls -t access* | head -1
access_log_20200621
$
以下は、アクセス・ログの内容の例である。見やすいように改行を入れているが
実際には1行が1つのページの転送を意味する。
2001:2f8:3a:1711::230:30 - - [22/Jun/2020:12:06:32 +0900] "GET /~syspro/2020/202 0-06-17/index.html HTTP/1.1" 200 38350 "http://www.coins.tsukuba.ac.jp/~syspro/2 020/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:77.0) Gecko/20100101 Fir efox/77.0" 2001:2f8:3a:1711::230:30 - - [22/Jun/2020:12:06:32 +0900] "GET /~syspro/2020/202 0-06-17/images/tcpip-server-fork-1.png HTTP/1.1" 200 7204 "http://www.coins.tsuk uba.ac.jp/~syspro/2020/2020-06-17/index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:77.0) Gecko/20100101 Firefox/77.0" 2001:2f8:3a:1711::230:30 - - [22/Jun/2020:12:21:35 +0900] "GET /~syspro/2020/202 0-06-24/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/2020/2020-06-24/index.h tml" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:77.0) Gecko/20100101 Fire fox/77.0" 2001:2f8:3a:1711::230:30 - - [22/Jun/2020:12:21:56 +0900] "POST /~syspro/2020/20 20-06-24/cgi-printarg.cgi HTTP/1.1" 200 375 "http://www.coins.tsukuba.ac.jp/~sys pro/2020/2020-06-24/index.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; r v:77.0) Gecko/20100101 Firefox/77.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) では、次の場所にある。
/var/log/httpd/error_log_*
/var/log/httpd/ssl_error_log_*
その他に、次のようなログを取ることもある。
ssl_request_log (coins では
/var/log/httpd/ssl_request_log_*)
/var/log/httpd/suexec.log 等 (coins では /var/log/secure)
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)
せず、そのままクライアントに返す。
ifconfig コマンドを使って実習室の自分が利用している iMac の 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 系のオペレー ティング・システムで利用できる。コマンドが見つからない時には、次の ようにコマンドを絶対パス名で打ってみなさい。
$ /sbin/ifconfig -a
あるいは、man ifconfig でマニュアルを表示して、探しなさい。
$ hostname
aloe30.local
$
$ hostname
viola5
$
hostname コマンドは、DNS で使う長いドメイン名を表示することもあるが、短
い名前や、それとは関係ない名前を表示することもある。
$ host aloe30
aloe30.coins.tsukuba.ac.jp has address 130.158.230.30
aloe30.coins.tsukuba.ac.jp has IPv6 address 2001:2f8:3a:1711::230:30
$ host www.coins.tsukuba.ac.jp
www.coins.tsukuba.ac.jp is an alias for viola5.coins.tsukuba.ac.jp.
viola5.coins.tsukuba.ac.jp has address 130.158.231.114
viola5.coins.tsukuba.ac.jp has IPv6 address 2001:2f8:3a:1711::231:114
$
次のホストについて、IP アドレスを調べなさい。
Fingerprint が、 coinsシステムの主なSSHサーバの公開鍵のハッシュ値 と一致していることを確認してから yes と打つこと。偽物のホストに接続してパスワードを打ってはならない。
aloe30:~ s1754321$ ssh www
The authenticity of host 'www (2001:2f8:3a:1711::231:114)' can't be established.
ECDSA key fingerprint is SHA256:PwCxoyGybzg/5ahMZamq0vCzn+6WPgpsyPZxPfr9E6k.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'www,2001:2f8:3a:1711::231:114' (ECDSA) to the list of known hosts.
s1754321@www's password:
Last failed login: Mon Jun 17 17:04:02 JST 2020 from aloe30.coins.tsukuba.ac.jp on ssh:notty
Last login: Mon Jun 17 16:03:54 2020 from 2001:2f8:3a:2300::1:8657
viola5:~ s1754321$
ssh で遠隔ログインする前後に、プロンプトのホスト名の部分が変化すること
に注意しなさい。(この例では、aloe30 から viola5 に変化している。)
$ cd /var/www/htdocs/
$ ls
<省略>
$ cat index.html
<省略>
$ cd /var/www/secure_htdocs/
$ ls
<省略>
$ cat index.html
cat: index.html: そのようなファイルやディレクトリはありません
$ cat index.php
<省略>
/var/log/httpd/access* というファイルにためられる。
これを、次のコマンドを利用しながら観察しなさい。
$ hostname
viola5
$ cd /var/log/httpd/
$ pwd
/var/log/httpd
$
access* の「*」は、
シェルの
ファイル名置換
の意味である。
$ ls
(多くのファイルが表示される)
$ ls access*
(まだ多くのファイルが表示される)
$ ls access* | tail -1
access_log_20200621
$ ls -t access* | head -1
access_log_20200621
$
この例では、access_log_20200621 が最新のものである。
$ lv access上のlsの結果で置き換える
たとえば、上の例では、次のように打つ。
$ lv access_log_20200621
$ tail access上のlsの結果で置き換える
(表示がなされる。)
(しばらく待つ。)
$ tail access上のlsの結果で置き換える
(表示がなされる。)
(しばらく待つ。)
$ tail access上のlsの結果で置き換える
(表示がなされる。)
(しばらく間隔を空けてから)数回 tail コマンドを実行すると、その度に行
が増えているはずである。
たとえば、上の例では、次のように打つ。
$ tail access_log_20200621
$ tail access_log_20200621
$ tail access_log_20200621
https://www.coins.tsukuba.ac.jp/~syspro/2020/report/present.cgi,出席ボタン」
を押すと、そこに表示されるので、それを利用する方法がある。
(出席ボタンは、何度押しても問題がない。授業時間外に押しても問題はない。
最終的に、授業時間内のものだけ集計される。)
NAT を利用していないコンピュータ(実習室のコンピュータ含む)では、
練習問題 ifconfigコマンド
で調べる方法がある。
$ grep IPアドレス アクセスログのファイル名
たとえば、IP アドレスが 2001:2f8:3a:1711::230:30 であれば、次のように打つ。
$ grep 2001:2f8:3a:1711::230:30 アクセスログのファイル名
たとえば、IP アドレスが 130.158.230.30 であれば、次のように打つ。
$ grep 130.158.230.30 アクセスログのファイル名
ここで「アクセスログのファイル名」は、
練習問題 アクセスログの観察
で調べなさい。
たとえば、その練習問題の例では、次のように打つ。
$ grep 2001:2f8:3a:1711::230:30 access_log_20200621
$ tail -f アクセスログのファイル名
ここで「アクセスログのファイル名」は、
練習問題 学類 Web サーバのアクセス・ログの観察
で調べなさい。
たとえば、その練習問題の例では、次のように打つ。
$ tail -f access_log_20200621
-f オプションを指定すると、tail コマンドは終了することなく新しく追加さ
れた行を表示する。この tail コマンドは、自動的には終了しないので、終わっ
たら、
^C (Control+C) で強制終了させる。
$ cd /var/log/httpd/
ssl_access*
$ ls
(多くのファイルが表示される)
$ ls ssl_access*
(まだ多くのファイルが表示される)
$ ls ssl_access* | tail -1
(1つだけ表示される)
$ ls -t ssl_access* | head -1
(1つだけ表示される)
$
次の CGI プログラムをコマンドラインから実行しなさい。
$ cp ~yas/syspro/www/cgi-arg1arg2.c .
$ cc cgi-arg1arg2.c -o cgi-arg1arg2.cgi
$ ./cgi-arg1arg2.cgi
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
Error while parsing query string
</PRE></BODY></HTML>
$ export REQUEST_METHOD=GET
$ export QUERY_STRING='arg1=10&arg2=20'
$ ./cgi-arg1arg2.cgi
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
arg1: [10]
arg2: [20]
</PRE></BODY></HTML>
$
次のようにパラメタを設定して実行してみなさい。
余裕があれば、REQUEST_METHOD=POST についても、同様に実行してみなさい。
余裕があれば、フォームを定義し、何かデータを送ってみなさい。 coins で CGI を実行する場合、CGI の実行形式を Web サーバ (http://www.coins.tsukuba.ac.jp) に ssh でログインした上で作成すること。 iMac macOS で作成した実行形式は、Web サーバ Linux では動作しない。
http://www.coins.tsukuba.ac.jp/~syspro/2020/2020-06-24/cgi-arg1arg2.cgi
たとえば、次のような記述を含む HTML ファイルを作成し、 ~/public_html/htdocs/ 以下に置く。
<FORM ACTION="http://www.coins.tsukuba.ac.jp/~syspro/2020/2020-06-24/cgi-arg1arg2.cgi" method="get">
arg1: <INPUT type="text" name="arg1">
arg2: <INPUT type="text" name="arg2">
<INPUT type="submit">
<INPUT type="reset">
</FORM>
2つのデータを受け取り、その結果を加えた数を表示するようなCGI のプログ ラムを作成しなさい。
ヒント:
char *arg1_s,*arg2_s;
arg1_s = getparam(qc,qv,"arg1");
arg2_s = getparam(qc,qv,"arg2");
getparam() は、次のような関数であり、
cgi-arg1arg2.cgiのソースコード
に含まれている。
char *getparam(int qc, char *qv[], char * varname ) [独自]
"name"の時、
この関数は、
"name=value" というパラメタを
見つけ、"value"を返す。
見つからなかったら NULL を返す。
この関数の使い方は、ライブラリ関数 getenv() と似ているので、
それも参考にしなさい。
$ export REQUEST_METHOD=GET
$ export QUERY_STRING='arg1=10&arg2=20'
$ ./a.out
Content-Type: text/html
<HTML><HEAD></HEAD><BODY>
30
</BODY></HTML>
$
なお、一度設定した環境変数の値は、シェルを終了するまで残っている。
a.out を実行するたびに設定する必要はない。
レポートには、引数が足りかったり、不正な場合にはエラー・メッセージを表 示して終了することも示しなさい。
$ cc cgi-add.c
$ mkdir ~/public_html/htdocs/syspro-cgi-examples
$ cp a.out ~/public_html/htdocs/syspro-cgi-examples/calc.cgi
$ open 'http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.cgi?arg1=10&arg2=20'
$ emacs ~/public_html/htdocs/syspro-cgi-examples/calc.html
$ open http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.html
$ cal
June 2020
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 の意味はない。余計な表示は省略すること。
$ ./a.out
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
June 2020
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
</PRE></BODY></HTML>
$
$ mkdir ~/public_html/htdocs/syspro-cgi-examples
$ cp a.out ~/public_html/htdocs/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>
$ cal 8 2020
August 2020
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 31
$
この機能を利用して、Web ブラウザから送られた年月のカレンダーを表示する
CGI プログラムを作成しなさい。
char *year_s,*month_s;
year_s = getparam(qc,qv,"year");
month_s = getparam(qc,qv,"month");
getparam() は、
cgi-arg1arg2.cgiのソースコード
に含まれている。
$ export REQUEST_METHOD=GET
$ export QUERY_STRING='year=2020&month=8'
$ ./a.out
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
August 2020
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 31
</PRE></BODY></HTML>
$
なお、一度設定した環境変数の値は、シェルを終了するまで残っている。
a.out を実行するたびに設定する必要はない。
レポートには、次の結果を含めなさい。
$ mkdir ~/public_html/htdocs/syspro-cgi-examples
$ cp a.out ~/public_html/htdocs/syspro-cgi-examples/show-cal.cgi
$ open 'http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.cgi?year=2020&month=8' ![[←]](../icons/screen-return.gif)
この課題では、フォームを含む HTML ファイルを作成し、 CGIとして実行し、その結果をレポート含めなさい。 その結果としては、 アクセスログ を含めなさい。
注意: CGI のパラメタは、最初は、文字列で受け取り、最終的に cal コマンドには文 字列で渡す。この時に、年月日としては不適切なものがクライアントから渡さ れる可能性がある。不適切なものが渡された場合には、エラー・メッセージを 表示しなさい。また、不正なパラメタで cal コマンドは実行しないようにしなさい。
CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムを つくりなさい。CGI として実行した時とシェルから実行した時の結果を比較し なさい。
ヒント:CGI のプログラムから getuid(), getgid(), getgroups() システム・ コールを実行する。uid や gid を文字列で表示したいなら、getpwuid(), getpwuid_r(), getgrgid(), getgrgid_r() を用いる。
getuid(), getgid(), getgroups() システム・コールを用いる代わりにid コマ ンド、whoami コマンド、groups コマンド等を実行する方法もある。
ヒント:
main()
{
ファイルから整数を読み出す(ファイルがなければ0とする)。
1 加える。
標準出力(画面)に整数値を表示する。
ファイルに整数を保存する。
}
Content-Type: は、text/plain にする。
Content-Type: を text/html にする方法もある。
ただし、本文は、HTML として作成する必要がある。
~/public_html/htdocs 以下にコピーされるこ
とを想定して、整数を保存するファイルを修正する。
必要ならば、初期状態のファイルを作成して、環境に合わせて
モードを適切なものにする(常に chmod 666 は誤り)。
.cgi として、~/public_html/htdocs 以下にコピーする。
ロックがきちんと動作していることを示しなさい。そのためには、ロックして getchar() 等でキーボードからの指示で(アンロックして)終了するプログラム を作成する方法がある。プログラムが終了すると自動的にアンロックされる。 次のプログラムを参考にしてもよい。
注意: 2019年4月-2023年3月、coins の環境では、www サーバからNFS経由でアクセス しているファイルに対して次のどの方法でも ロックが働く。
永続的なハッシュ表を作成する方法として、dbm ライブラリを用いる方法があ る。これを利用してみなさい。ライブラリ関数としては、dbm_open(), dbm_store(), dbm_fetch(), dbm_delete(), dbm_close()がある。詳しくは、 man dbm_open を見なさい。
Java 言語の場合、dbm ライブラリではなく、その他の埋め込み型の key-value store を使ってもよい。
この課題では、フォームを含む HTML ファイルを作成し、 CGIとして実行し、その結果をレポート含めなさい。 その結果としては、 アクセスログ を含めなさい。
この課題では、フォームを含む HTML ファイルを作成し、 CGIとして実行し、その結果をレポート含めなさい。 その結果としては、 アクセスログ を含めなさい。
この課題では、ロックが働いていることを示すために、CGI のプログラムとは別にファイ ルのロックだけを行なうプログラムを作成しなさい。そして、次のようなことを行いなさ い。