システム・プログラムI
電子・情報工学系
新城 靖
<yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/syspro1-1998/1998-06-16
あるいは、次のページから手繰っていくこともできます。
http://www.hlla.is.tsukuba.ac.jp/~yas/coins/
http://www.hlla.is.tsukuba.ac.jp/~yas/index-j.html
printf(), fprintf() では、出力先が画面なら、"\n" が現れるまでバッファ リングする(line buffered, _IOLBF)。バッファリングを抑止したい時には、 その都度必要な時に fflush() を呼ぶか、setvbuf(), setbuf() などで、バッ ファリングをしない(unbuffered)ように設定する。
HTTP のレベルで、クライアントとサーバの間を中継するプログラムを、HTTP Proxy と呼ぶ。HTTP Proxy は、クライアントから見ると、サーバに見え、サー バから見ると、クライアントに見える。
HTTP Proxy は、次のような目的に使われる。
echo-server-select.cでは、1つのプロセスで実行し
ている。
----------------------------------------------------------------------
:
2: /*
3: echo-server-select.c -- 受け取った文字列をそのまま返すサーバ(select版)
4: ~yas/syspro1/ipc/echo-server-select.c
5: $Header: /home/lab2/OS/yas/syspro1/ipc/RCS/echo-server-select.c,v 1.2 1998/06/15 11:37:19 yas Exp $
6: Start: 1997/06/09 19:53:33
7: */
8: #include <stdio.h>
9: #include <sys/types.h> /* socket(), time(), select() */
10: #include <sys/socket.h> /* socket() */
11: #include <netinet/in.h> /* struct sockaddr_in */
12: #include <unistd.h> /* select() */
13: #include <bstring.h> /* select() */
14: #include <sys/time.h> /* select() */
15:
16: extern void echo_server( int portno );
17: extern void echo_reply_select( int com );
18: extern void print_host_port( int portno );
19: extern void tcp_peeraddr_print( int com );
20: extern tcp_acc_port( int portno );
21: extern int writen( int fd, char *buf, int nbytes );
22:
23: main( int argc, char *argv[] )
24: {
25: int portno ;
26: if( argc >= 3 )
27: {
28: fprintf( stdout,"Usage: %s host port\n",argv[0] );
29: exit( -1 );
30: }
31: if( argc == 2 )
32: portno = atoi( argv[1] );
33: else
34: portno = getuid();
35: echo_reply_select( portno );
36: }
37:
38: void echo_reply_select( int portno )
39: {
40: int acc,com ;
41: fd_set readfds,readfds_save ;
42: int i,n ;
43:
44: acc = tcp_acc_port( portno );
45: if( acc<0 )
46: exit( -1 );
47: print_host_port( portno );
48:
49: FD_ZERO( &readfds_save );
50: FD_SET( acc,&readfds_save );
51: while( 1 )
52: {
53: readfds = readfds_save ;
54: n = select( FD_SETSIZE,&readfds,0,0,0 );
55: if( n <= 0 )
56: {
57: perror("select");
58: exit( 1 );
59: }
60: if( FD_ISSET(acc,&readfds) )
61: {
62: FD_CLR( acc,&readfds );
63: if( (com = accept( acc,0,0 )) < 0 )
64: {
65: perror("accept");
66: exit( -1 );
67: }
68: FD_SET( com, &readfds_save );
69: tcp_peeraddr_print( com );
70: }
71: for( i=0 ; i<FD_SETSIZE ; i++ )
72: {
73: if( FD_ISSET(i,&readfds) )
74: {
75: if( echo_reply_once( i )<=0 )
76: {
77: printf("[%d,%d] connection closed.\n",getpid(),i );
78: close( i );
79: FD_CLR( i,&readfds_save );
80: }
81: }
82: }
83: }
84: }
85:
86: int echo_reply_once( int com )
87: {
88: char buff[BUFSIZ] ;
89: int rcount ;
90: int wcount ;
91:
92: if( (rcount=read(com,buff,BUFSIZ)) > 0 )
93: {
94: if( (wcount=writen(com,buff,rcount))!= rcount )
95: {
96: perror("write");
97: exit( 1 );
98: }
99: printf("[%d,%d] ",getpid(),com );
100: fflush( stdout );
101: write( 1, buff, rcount );
102: }
103: return( rcount );
104: }
<以下省略>
----------------------------------------------------------------------
実行例。
サーバ側。サーバは、終了しないので、最後に、^C か Del を押して、割り込みを掛けて終了させる。
クライアント側(その1)。---------------------------------------------------------------------- % ./echo-server-selectrun telnet adonis1 1231 [1701,4] connection from 130.158.86.1:20822 [1701,4] 012 [1701,5] connection from 130.158.86.11:1479 [1701,5] abc [1701,5] def [1701,5] connection closed. [1701,4] 345 [1701,4] connection closed. ^C %
----------------------------------------------------------------------
クライアント側(その2)。---------------------------------------------------------------------- Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. 012012 345
345 ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
---------------------------------------------------------------------- % telnet adonis1 1231Trying 130.158.86.1... Connected to adonis1. Escape character is '^]'. abc
abc def
def ^] telnet> quit
Connection closed. %
----------------------------------------------------------------------
インターネットのセキュリティを高めるための道具として、TCP Wrapper があ る。TCP Wrapper は、クライアントの IP アドレスを調べて、接続を許可した り拒否したりする機能がある。そのような機能を持っていないプログラムに、 そのような機能を外付けで付け加えることができる。
次の場所にソースを置いておく。
~yas/syspro1/tcp_wrappers_7.6/
(~yas/syspro1/archives/tcp_wrappers_7.6.tar.gz)
TCP/IP のプログラムを書く時には、 sized_io ライブラリを利用すると便利である。 stream.c は、短いので目を通しておくとよい。
次の場所にソースを置いておく。
~yas/syspro1/sized_io/
(~yas/syspro1/archives/sized_io.tar.gz)
普通の割込み(ハードウェアによる割込み)は、オペレーティング・システム のカーネルにおいて利用されている。
ソフトウェア割込みとは、本来はオペレーティング・システムのカーネルしか 使えない割込みの機能を、ソフトウェアにより実現して、一般の利用者プログ ラム(プロセス)でも使えるようにしたものである。
UNIXでは、ソフトウェア割込みの機能は、シグナル(signal)という名前で実現 されている。シグナルとは、本来はプロセス間通信の一種で、ある事象が起き たことを他のプロセスに知らせることである。ここで伝わるのは、ある事象が 起きたかどうかだけで、引数などを付けることはできない。UNIXでは、プロセ ス間でシグナルにより通信をする他に、キーボードからシグナルを送ることも できる。これは、「ソフトウェア割込み」として、プロセス1つひとつに割込 みボタンが付いているようなものである。また、プログラムの中で例外 (exception)が起きた時にも、ハードウェアの割込みと同様に、ソフトウェ ア割込みが生じる。これも、他のシグナルと同じように受け取ることができる。
UNIXのソフトウェア割込み(シグナル)を使うには、次のようなことが必要で ある。
ソフトウェア割り込みは、1つのプログラムの中に制御の流れが1つしかない ようなプログラムの時に有効な方法である。最近のマルチスレッドのプログラ ムでは、シグナルの意味が不明確である。
----------------------------------------------------------------------
1: /*
2: signal-int.c -- SIGINT を3回受け付けて終了するプログラム。
3: ~yas/syspro1/proc/proc/signal-int.c
4: $Header: /home/lab2/OS/yas/syspro1/proc/RCS/signal-int.c,v 1.3 1998/06/15 12:24:36 yas Exp $
5: Start: 1997/05/26 18:38:38
6: */
7:
8: #include <stdio.h>
9: #include <signal.h>
10:
11: int sigint_count = 3 ;
12: void sigint_handler();
13:
14: main()
15: {
16: signal( SIGINT, &sigint_handler );
17: printf("main(): going into infinite loop, sigint_count == %d\n",
18: sigint_count);
19: while( 1 )
20: {
21: printf("main(): sigint_count == %d, pause() ....\n",
22: sigint_count );
23: pause();
24: printf("main(): return from pause(). sigint_count == %d\n",
25: sigint_count );
26: }
27: }
28:
29: void sigint_handler()
30: {
31: printf("sigint_handler():\n");
32: if( -- sigint_count <= 0 )
33: {
34: printf("sigint_handler(): exit() ... \n");
35: exit( 1 );
36: }
37: signal( SIGINT, &sigint_handler ); /* System V */
38: printf("sigint_handler(): sigint_count == %d\n",sigint_count);
39: }
----------------------------------------------------------------------
実行例。
stty で intr に相当するキーを3回押す。---------------------------------------------------------------------- % stty -aspeed 9600 baud; line = 1; 48 rows; 80 columns intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = ^@; old-swtch = ^Z; susp = ^Z lnext = ^V; werase = ^W; rprnt = ^R; flush = ^O; stop = ^S; start = ^Q; dsusp = ^Y parenb -parodd cs8 -cstopb hupcl cread -clocal -cnew_rtscts -loblk -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl -iuclc ixon -ixany -ixoff -imaxbel isig icanon iexten -xcase echo echoe echok echoke echoctl -echoprt -echonl -noflsh -flusho -pendin -tostop opost -olcuc onlcr -ocrnl -onocr -onlret -ofill -ofdel % ./signal-int
main(): going into infinite loop, sigint_count == 3 main(): sigint_count == 3, pause() .... ^C sigint_handler(): sigint_handler(): sigint_count == 2 main(): return from pause(). sigint_count == 2 main(): sigint_count == 2, pause() .... ^C sigint_handler(): sigint_handler(): sigint_count == 1 main(): return from pause(). sigint_count == 1 main(): sigint_count == 1, pause() .... ^C sigint_handler(): sigint_handler(): exit() ... %
----------------------------------------------------------------------
関数間でも有効な goto がある。
----------------------------------------------------------------------
1: /*
2: setjmp-longjmp.c -- setjmp() と longjmp() のテスト
3: ~yas/syspro1/proc/setjmp-longjmp.c
4: $Header: /home/lab2/OS/yas/syspro1/proc/RCS/setjmp-longjmp.c,v 1.2 1998/06/15 12:33:57 yas Exp $
5: Start: 1997/05/26 20:26:27
6: */
7:
8: #include <setjmp.h>
9:
10: extern void f(void);
11: extern void g(void);
12: extern void h(void);
13:
14: jmp_buf main_env ;
15:
16: main()
17: {
18: int x ;
19: if( (x=setjmp(main_env)) == 0 )
20: {
21: printf("setjmp(), first time. x == %d\n",x );
22: f();
23: }
24: else
25: {
26: printf("return from longjmp(), x == %d \n",x );
27: }
28: }
29:
30: void f()
31: {
32: printf("f() called.\n");
33: g();
34: printf("f() return.\n");
35: }
36:
37: void g()
38: {
39: printf("g() called.\n");
40: h();
41: printf("h() return.\n");
42: }
43:
44: void h()
45: {
46: printf("h() called.\n");
47: printf("longjmp( main_env,10 )\n");
48: longjmp( main_env,10 );
49: printf("h() return.\n");
50: }
----------------------------------------------------------------------
実行例。
---------------------------------------------------------------------- % ./setjmp-longjmpsetjmp(), first time. x == 0 f() called. g() called. h() called. longjmp( main_env,10 ) return from longjmp(), x == 10 %
----------------------------------------------------------------------
finger サーバを作成しなさい。これは、受け取った文字列を引数にして、 finger コマンドを実行するとよい。そして、実行結果を、接続先に返すよう にするとよい。
finger コマンドと finger サーバの間は、パイプで接続しなさい。今までの 例題で利用した方法を組み合わせることで実現することができるはずである (pipe(),fork(),dup(),close(),execve()など)。popen() ライブラリ関数を 利用してもよい。
http サーバを作成しなさい。これは、受け取ったファイル名のファイルを開 いて、内容を読み込み、それを接続先に返すようにするとよい。
この課題では、特にセキュリティに気をつけなさい。必ず次の条件を満たすよ うなサーバを作りなさい。
~/public_html)以下のファイル
しかアクセスできないようにすること。
.. が含まれていたら、アクセスを拒否すること。
---------------------- Content-Type: 拡張子 ---------------------- text/html .html text/plain .txt image/gif .gif image/jpeg .jpeg ----------------------
HTTP proxy 作りなさい。次の機能を、1つ以上付けなさい。
signal-int.c のプロ グラムを動かし、kill コマンドを使って SIGINT (kill -INT)でシグナルを送 りなさい。そして、キーボードから intr のキー^Cを打った時と動 作を比較しなさい。
ヒント:kterm を2つ開いて、片方でこのプログラムを動かし、片方で kill コマンドを動かす。kill 引数として必要な PID は、ps コマンドで調べる。
ヒント:そのシグナルを無視するように設定する。
cc -D_BSD_SIGNALS -o prog prog.cBSD 系では、一度登録したシグナルは、ハンドラの中で再登録する必要はない。 このことを調べなさい。
また、シグナル・ハンドラの中でシグナルを受けた時の動きを調べなさい。
くわしくは、man 3b signal 。
Subject: [syspro1/report6] 3proc-tcp
Subject: [syspro1/report6] finger-server
Subject: [syspro1/report6] http-server
Subject: [syspro1/report6] http-proxy