筑波大学 システム情報系 情報工学域 新城 靖 <yas@cs.tsukuba.ac.jp>
このページは、次の URL にあります。
https://www.coins.tsukuba.ac.jp/~syspro/2024/2024-07-24/index.html
あるいは、次のページから手繰っていくこともできます。
https://www.coins.tsukuba.ac.jp/~syspro/2024/
http://www.coins.tsukuba.ac.jp/~yas/
事例1: ファイルのアイコンをダブルクリックしたら開く(ファイルの内容が表示される)。
^C
(Control+C、シグナル)
FILE *file = fopen(filepath, "r"); ... fseek(file, 0, SEEK_END); filesize = ftell(file); fseek(file, 0, SEEK_SET);
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() で環境変数の値を読むだけのことが多い。 環境変数に値を代入するのは、シェルで行なうことが多い。
シェルによる環境変数の設定方法は、次のページを参考にしなさい。
次の環境変数は、覚える。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
$
簡単なことなら、コンパイラが検出して警告を発することもあるが、少し複雑
になると、検出できなくなる。
if( strcmp(0,".html") == 0 ) ...次のようなプログラムを書く人は、たくさんいる。
if( strcmp(strrchr(s,'.'),".html") == 0 ) ...このようなプログラムに対して、コンパイラは警告を出さない。
一般に、ポインタをreturnする関数が 0 を返すとどうなるかを考える。
$ who
yas pts/0 2024-07-22 16:23 (2001:2f8:3a:1711:98d4:7af1:835f:4132)
$ xrdp-who
yas 2024-07-22 17:45 (Xorg)
$
その他、w コマンド、uptime コマンド、
HM_Ref(`../2024-05-22/index.html#top-command',topコマンド)
も有用。
混んでいたら、別のホストに切り替える。 azalea1 以外のホストに切り替えるのは、きゃんと実力を付けていたら、できる。
Gateway とは、World Wide Web で使われている HTTP というプロトコルへの入 り口という意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることも画像データであることもある。
図? CGIの仕組み
CGI の利用例。クライアントからパラメタを受け取り、プログラムを実行し、 データを保存したり、クライアントに返す内容を変化させる。
情報科学類では Web サーバとして Apache HTTP Server を 用いている。
Apache では、CGI として扱うファイル名のパタンとして、次のようなものが よく使われる(設定により、これ以外も可能)。
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 の文書を出力している。
$ 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>
$
$ 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
(
https://www.coins.tsukuba.ac.jp/~syspro/2024/2024-07-24/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/2024/2024-07-24/cgi-hello.cgi HTTP/1.0
Host: www.coins.tsukuba.ac.jp
HTTP/1.1 200 OK
Date: Mon, 10 Jul 2024 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/2024/2024-07-24/cgi-hello.cgi HTTP/1.0
Host: www.coins.tsukuba.ac.jp
Content-Length: 0
HTTP/1.1 200 OK
Date: Fri, 24 Jun 2024 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.
$
<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/2024/2024-07-24/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/2024/2024-07-24/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 サーバは、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」として送られる。
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&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)
</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&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)
</PRE></BODY></HTML>
$
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() するための手続きである。
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=/~yas/coins/syspro-2024/2024-07-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=/~yas/coins/syspro-2024/2024-07-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 として変換している。
たとえば、"%40"
を
0x40 ('@
')
に戻している。
その他に、「'+'
」を「' '
」に変換している。
CGI のプログラムを作成する時、エラーがおきたとしても、ブラウザの画面にはエラー・メッセージは表示されない。 その時は、アクセスログやエラーログを見てデバッグする。
Web サーバは、ページがアクセスされる度に記録を残す。この記録をアクセス ログという。アクセスログは、情報科学類のサーバ www (www.coins.tsukuba.ac.jp) では、次の場所にある。
/var/log/httpd/access_log_*
/var/log/httpd/ssl_access_log_*
$ cd /var/log/httpd/
$ ls access_log_* | tail -1
$ ls -t access_log_* | head -1
以下の例では、現在増加しているログは
access_log_20240705
である。$ cd /var/log/httpd/
$ ls access_log_* | tail -1
access_log_20240705
$ ls -t access_log_* | head -1
access_log_20240705
$
以下は、アクセス・ログの内容の例である。見やすいように改行を入れているが
実際には1行が1つのページの転送を意味する。
2001:2f8:3a:1711::231:16 - - [22/Jul/2024:17:46:13 +0900] "GET /~syspro/2024/202 4-07-24/index.html HTTP/1.1" 200 70991 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_ 64; rv:128.0) Gecko/20100101 Firefox/128.0" 2001:2f8:3a:1711::231:16 - - [22/Jul/2024:17:46:13 +0900] "GET /~syspro/2024/202 4-07-24/images/www-cgi.png HTTP/1.1" 200 15499 "https://www.coins.tsukuba.ac.jp/ ~syspro/2024/2024-07-24/index.html" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv: 128.0) Gecko/20100101 Firefox/128.0" 2001:2f8:3a:1711::231:16 - - [22/Jul/2024:17:49:59 +0900] "GET /~syspro/2024/202 4-07-24/cgi-printarg.cgi?lastname=name1&firstname=name2&lang=C&email=who%40dom H TTP/1.1" 200 443 "https://www.coins.tsukuba.ac.jp/~syspro/2024/2024-07-24/index. html" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/ 128.0" 2001:2f8:3a:1711::231:16 - - [22/Jul/2024:17:50:28 +0900] "POST /~syspro/2024/20 24-07-24/cgi-printarg.cgi HTTP/1.1" 200 375 "https://www.coins.tsukuba.ac.jp/~sy spro/2024/2024-07-24/index.html" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:128 .0) Gecko/20100101 Firefox/128.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" combinedcombined という名前が付けられたログのフォーマットの意味は、以下の通りである。
アクセスログの他に、エラーが起きた時にもログが作られる。これを エラーログ という。エラーログは、情報科学類のサーバ 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)
せず、そのままクライアントに返す。
https://www.coins.tsukuba.ac.jp/~syspro/2024/,この講義のトップページ
に設置された「出席ボタン」を押しなさい。
その画面に表示された IP アドレスを調べなさい。
その IP アドレスは、Web サーバが認識した Web ブラウザが動いているコンピュータの 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 パッケージをインストールしなさい。
$ ip address $ ip a $ ip a show dev インタフェース名
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\ユーザ名>
$ hostname
azalea16
$
$ hostname
violet03
$
hostname コマンドは、DNS で使う長いドメイン名を表示することもあるが、短
い名前や、それとは関係ない名前を表示することもある。
$ 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 アドレスを調べなさい。
次の CGI プログラムをコマンドラインから実行しなさい。
cgi-opval.c
]
$ 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=inc'
$ ./cgi-opval.cgi
Content-Type: text/html
<HTML><HEAD></HEAD><BODY><PRE>
op: [inc]
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) では動作しないことがある。
https://www.coins.tsukuba.ac.jp/~syspro/2024/2024-07-24/cgi-opval.cgi
たとえば、次のような記述を含む HTML ファイルを作成し、 ~/public_html/ 以下に置く。
<FORM ACTION="cgi-opval.cgi" method="get"> <INPUT type="radio" name="op" value="inc"> Up <BR> <INPUT type="radio" name="op" value="set"> Set <INPUT type="text" name="val"> <BR> <INPUT type="submit"> <INPUT type="reset"> </FORM>
file-counter.c
]
このプログラムは、ファイルが存在しない場合、 0 が保存されているとみなす。 そのような状態で、get サブコマンドを実行すると、0 が表示されるが、ファイルは作成されない。 ファイルは、inc や set で値を保存した時に作成される。
実行した結果、カウンタ値を保存するファイルが作成されることを確認しなさい。 ファイルの大きさと内容を 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 $
次のような次のようなパラメタを取る CGI のプログラムを C 言語で作成しなさい。
ヒント:
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 )
[独自]
"name"
の時、
この関数は、
"name=value"
というパラメタを
見つけ、"value"
を返す。
見つからなかったら NULL
を返す。
この関数の使い方は、ライブラリ関数 getenv() と似ているので、
それも参考にしなさい。
$ export REQUEST_METHOD=GET
$ export QUERY_STRING='op=inc&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 の範囲で扱えないものを不正としても良い。オーバーフロー 等を考慮しなくて良い。
$ cc cgi-counter.c
$ mkdir ~/public_html/syspro-cgi-examples
$ cp a.out ~/public_html/syspro-cgi-examples/cgi-counter.cgi
http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/cgi-counter.cgi?op=inc
http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/cgi-counter.cgi?op=set&val=100
$ emacs ~/public_html/syspro-cgi-examples/calc.html
http://www.coins.tsukuba.ac.jp/~ログイン名/syspro-cgi-examples/calc.html
ロックがきちんと動作していることを示しなさい。そのためには、ロックして getchar() 等でキーボードからの指示で(アンロックして)終了するプログラム を作成する方法がある。プログラムが終了すると自動的にアンロックされる。 次のプログラムを参考にしてもよい。
注意: 2023年4月-2028年3月、coins の環境では、www サーバからNFS経由でアクセス しているファイルに対して次のどの方法でも ロックが働く。
なお、ファイルにデータを保持するカウンタの file-counter.cは、inc や set の時にカウンタ値を保存するファイルを 2 回 開いている。この方法では、単純にカウンタ値を保存するファイルをロックの 対象することはできない。ファイルを閉じた時には、ロックが解除されるから である。この問題を解決するには、次のような方法が考えられる。
CGI プログラムが、どのユーザの権限で動作しているかを調べるプログラムを つくりなさい。CGI として実行した時とシェルから実行した時の結果を比較し なさい。
ヒント:CGI のプログラムから getuid(), getgid(), getgroups() システム・ コールを実行する。uid や gid を文字列で表示したいなら、getpwuid(), getpwuid_r(), getgrgid(), getgrgid_r() を用いる。
getuid(), getgid(), getgroups() システム・コールを用いる代わりにid コマ ンド、whoami コマンド、groups コマンド等を実行する方法もある。
永続的なハッシュ表を作成する方法として、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 言語、その他の言語でも利用できる。
その時点での投票結果は、即座に表示することでも良い。また、1人で複数回 投票可能として良いし、ユーザ認証した結果を用いて、1回だけしか投票を許 さないか、最後に投票した結果だけを採用することにしても良い。選択肢には ない候補に投票する機能を付加しても良い。
カウンタ値を保持する方法としては、 永続的ハッシュ表 を用いなさい。