ほとんどのサーバーには、既知のインターネットポート番号または UNIX ファミリ名でアクセスします。例 2-6 は、リモートログインサーバーのメインループを示しています。
main(argc, argv) int argc; char *argv[]; { int f; struct sockaddr_in6 from; struct sockaddr_in6 sin; struct servent *sp; sp = getservbyname("login", "tcp"); if (sp == (struct servent *) NULL) { fprintf(stderr, "rlogind: tcp/login: unknown service"); exit(1); } ... #ifndef DEBUG /* サーバーを制御端末から分離する。*/ ... #endif sin.sin6_port = sp->s_port; /* 限られたポート */ sin.sin6_addr.s6_addr = in6addr_any; ... f = socket(AF_INET6, SOCK_STREAM, 0); ... if (bind( f, (struct sockaddr *) &sin, sizeof sin ) == -1) { ... } ... listen(f, 5); while (TRUE) { int g, len = sizeof from; g = accept(f, (struct sockaddr *) &from, &len); if (g == -1) { if (errno != EINTR) syslog(LOG_ERR, "rlogind: accept: %m"); continue; } if (fork() == 0) { close(f); doit(g, &from); } close(g); } exit(0); }
例 2-7 は、サーバーがそのサービス定義を取得する方法を示しています。
sp = getservbyname("login", "tcp"); if (sp == (struct servent *) NULL) { fprintf(stderr, "rlogind: tcp/login: unknown service¥n"); exit(1); }
getservbyname(3SOCKET) の結果は、プログラムがサービス要求を待機するインターネットポートを定義するためにあとで使用されます。標準のポート番号の一部は、/usr/include/netinet/in.h に入っています。
例 2-8 は、オペレーションの非 DEBUG モードで、サーバーがその呼び出し側の制御端末との関連付けを解除する方法を示しています。
(void) close(0); (void) close(1); (void) close(2); (void) open("/", O_RDONLY); (void) dup2(0, 1); (void) dup2(0, 2); setsid();
このコードは、サーバーが制御端末のプロセスグループからのシグナルを受信することを防ぎます。関連付けを解除したあと、サーバーはエラーレポートを端末に送信できないため、syslog(3C) を使用してエラーをログに記録する必要があります。
サーバーは次にソケットを作成し、サービス要求を待機します。bind(3SOCKET) が使用されると、サーバーは指定された位置で待機します (限られたポート番号で待機するリモートログインサーバーはスーパーユーザーとして動作する)。
例 2-9 は、ループの本体を示しています。
while(TRUE) { int g, len = sizeof(from); if (g = accept(f, (struct sockaddr *) &from, &len) == -1) { if (errno != EINTR) syslog(LOG_ERR, "rlogind: accept: %m"); continue; } if (fork() == 0) { /* 子 */ close(f); doit(g, &from); } close(g); /* 親 */ }
accept(3SOCKET) は、クライアントがサービスを要求するまでメッセージをブロックします。accept(3SOCKET) は、SIGCHLD のようなシグナルで割り込みを受ける場合、失敗を示すメッセージを返します。accept(3SOCKET) からの戻り値がチェックされ、エラーが発生している場合は syslog(3C) によってエラーが記録されます。
続いてサーバーは子プロセスの fork(2) を行い、リモートログインプロトコル処理の本体を呼び出します。接続要求を待ち行列に入れるために親プロセスが使用するソケットは、子プロセスで閉じられます。accept(3SOCKET) が作成するソケットは、親プロセスで閉じられます。クライアントのアドレスは、クライアントの認証を行うためにサーバーアプリケーションの doit() ルーチンに渡されます。このルーチンは、クライアントと共に実際のアプリケーションプロトコルを実行します。