ONC+ 開発ガイド

パート III NIS+

Part 3 では NIS+ のための API について説明します。

第 5 章 NIS+ プログラミングガイド

この章は、NIS+ アプリケーションのプログラミングインタフェースの基礎について説明し、詳しいサンプルプログラムを示します。NIS+ API は、Solaris を使用するネットワーク用のアプリケーションを構築するプログラマを対象としています。NIS+ API には、アプリケーションをサポートするための基本的な機能が用意されています。

NIS+ ネットワークネームサービスが対象とするクライアント-サーバネットワークは、数個のサーバが 10 個のクライアントをサポートする程度の単純なローカルエリアネットワークから、全世界のさまざまなサイトに位置する 20 〜 100 個の専用サーバが 10,000 個ものマルチベンダクライアントをサポートし、さまざまな公衆ネットワークで連結された大規模なものまであります。

NIS+ の概要

ドメイン

NIS+ は、階層ドメインをサポートします。図 5-1 に、簡単な例を示します。

図 5-1 NIS+ のドメイン

Graphic

NIS+ の各ドメインは、組織の各部分のワークステーション、ユーザ、ネットワークサービスを記述するデータの集合です。NIS+ の各ドメインは、他のドメインとは独立に管理できます。この機能のおかげで、NIS+ は、小さなものから大きなものまでさまざまな規模のネットワークで使用できます。

サーバ

各ドメインはいくつかのサーバでサポートされています。このうち中心となるサーバをマスタサーバ、バックアップサーバをレプリカサーバといいます。マスタサーバとレプリカサーバの両方で NIS+ プログラムを実行します。オリジナルテーブルはマスタサーバが保管し、レプリカサーバはそのコピーを保存します。

NIS+ では、段階的にレプリカを更新します。最初にマスタサーバを変更すると、次にその変更が自動的にレプリカサーバに伝えられ、ネーム空間全体で有効となります。

テーブル

NIS+ では、情報がマップやゾーンファイルではなくテーブルに保存されます。NIS+ には 図 5-2 で示されている 16 種類の定義済みテーブル (システムテーブル) があります。

図 5-2 NIS+ のテーブル

Graphic

各テーブルにはそれぞれ違う種類の情報が保存されます。たとえば、Hosts テーブルには、ホスト名とインターネットアドレスのペアが保存され、Password テーブルにはネットワークユーザに関する情報が保存されます。

NIS+ テーブルは、次の 2 つの点で NIS マップから大きく改善されています。1 つは、NIS+ テーブルでは第 1 カラム (キーともいう) だけでなく任意のカラムをアクセスできる点です。このため、NIS の hosts.byname マップや hosts.byaddr マップのような重複マップが不要になりました。もう 1 つは、NIS+ テーブルの情報には、テーブルレベル、エントリレベル、カラムレベルの 3 つのレベルでアクセスできる点です。

NIS+ のセキュリティ

NIS+ のセキュリティモデルには、許可と認証の 2 つの機能があります。まず、ネーム空間上の各オブジェクトごとに、どのようなプリンシパルにどの種類の操作を許すかを指定します。これを許可といいます。ネーム空間へのアクセスが要求されると、NIS+ はその要求を出したプリンシパルを確認します。NIS+ は、要求の発信元を認識すると、その特定のプリンシパルに対する特定の操作をオブジェクトが認可しているか確かめます。NIS+ はこのように、認証情報とオブジェクトごとの許可情報に基づいて、アクセス要求を許すか拒否するかを決定します。

ネームサービスの切り換え

NIS+ はネームサービス切り換えとともに使用できます。ネームサービス切り換え (単に切り換えともいう) を使用すると、Solaris 2.x を使用するワークステーションで、複数のネットワーク情報サービスから情報を得ることができます。特に、ローカルファイル、/etc ファイル、NIS マップ、DNS ゾーンファイル、NIS+ テーブルから情報が得られます。ネームサービス切り換えを使用すると、単に情報源を選択するだけでなく、ワークステーションで情報の種類別に異なる情報源を使用できます。ネームサービスの設定は、/etc/nsswitch.conf ファイルで行います。

NIS+ の管理コマンド

NIS+ では、ネーム空間を管理するのに必要なコマンドがすべて提供されています。

表 5-1 にその要約を示します。

表 5-1 NIS+ のネーム空間管理コマンド

コマンド 

説明 

nischgrp

NIS+ オブジェクトのグループ所有者を変更する。 

nischmod

オブジェクトのアクセス権を変更する。 

nischown

NIS+ オブジェクトの所有者を変更する。 

nisgrpadm

NIS+ グループの作成と破棄、グループメンバリストの表示を行う。また、グループにメンバを追加または削除したり、グループメンバかどうかのテストを行う。 

niscat

NIS+ テーブルの内容を表示する。 

nisgrep

NIS+ テーブルのエントリを検索する。 

nisls

NIS+ ディレクトリの内容をリストする。 

nismatch

NIS+ テーブルのエントリを検索する。 

nisaddent

/etc ファイル、または、NIS マップの情報を NIS+ テーブルに追加する。

nistbladm

NIS+ テーブルの作成や削除を行う。また、NIS+ テーブルのエントリを追加、変更、削除する。 

nisaddcred

NIS+ プリンシパルの資格を作成し、それを Cred テーブルに保存する。 

nispasswd

NIS+ の Passwd テーブルのパスワード情報を変更する。 

nisupdkeys

NIS+ オブジェクトに保存されているパブリックキーを更新する。 

nisinit

NIS+ のクライアントまたはサーバを初期化する。 

nismkdir

NIS+ ディレクトリを作成し、そのマスタサーバとレプリカサーバを指定する。 

nisrmdir

ネーム空間から、NIS+ ディレクトリとその複製を削除する。 

nissetup

org_dirgroups_dir の 2 つのディレクトリを作成し、NIS+ ドメインに対する全種類の NIS+ テーブル (空のテーブル) を作成する。

rpc.nisd

NIS+ のサーバプロセス。 

nis_cachemgr

NIS+ クライアントの NIS+ キャッシュマネージャを起動する。 

nischttl

NIS+ オブジェクトの生存時間を変更する。 

nisdefaults

NIS+ オブジェクトのデフォルト値 (ドメイン名、グループ名、ワークステーション名、NIS+ プリンシパル名、アクセス権、ディレクトリの検索パス、生存時間) をリストする。 

nisln

2 つの NIS+ オブジェクト間のシンボリックリンクを作成する。 

nisrm

ディレクトリ以外の NIS+ オブジェクトをネーム空間から削除する。 

nisshowcache

NIS+ キャッシュマネージャが管理している NIS+ 共有キャッシュの内容をリストする。

NIS+ の API

NIS+ の アプリケーションプログラマーズインタフェース (API) は関数の集合です。アプリケーションでは、これらの関数を呼び出して NIS+ オブジェクトにアクセスしたり変更したりできます。NIS+ の API には 54 個の関数があり、それらは次の 9 つのグループに分類されます。

表 5-2 では、これらの関数をグループごとにまとめて簡単に説明します。グループ名は、NIS+ のマニュアルページのグループ名と同じです。

表 5-2 NIS+ の API 関数

関数 

説明 

nis_names()

オブジェクトの検索と操作。 

nis_lookup()

NIS+ オブジェクトのコピーを返す。リンクをたどることができる。エントリオブジェクトの検索はできないが、リンクがエントリオブジェクトを指している場合は、それを返すことができる。 

nis_add()

NIS+ オブジェクトをネーム空間に追加する。 

nis_remove()

NIS+ オブジェクトをネーム空間から削除する。 

nis_modify()

ネーム空間の NIS+ オブジェクトを変更する。 

nis_tables

テーブルの検索と更新を行う。 

nis_list()

NIS+ ネーム空間内のテーブルを検索して、検索条件に一致するエントリオブジェクトを返す。テーブル間のリンクと検索パスをたどることができる。 

nis_add_entry()

NIS+ テーブルにエントリオブジェクトを追加する。既存オブジェクトがあれば操作を中止するか上書きするかを指定できる。操作が正常終了した場合は、追加したオブジェクトのコピーが返される。 

nis_freeresult()

nis_result 構造体に割り当てられたメモリをすべて解放する。

nis_remove_entry()

NIS+ テーブルからエントリオブジェクトを削除する。削除するオブジェクトは、検索条件で指定するか、キャッシュされたオブジェクトコピーへのポインタで指定できる。検索条件で指定する場合は、その条件に一致するオブジェクトをすべて削除できる。したがって、検索条件を適切に指定すれは、テーブル内の全エントリを削除することもできる。操作が正常終了した場合は、削除したオブジェクトのコピーが返される。 

nis_modify_entry()

NIS+ テーブルのエントリオブジェクトを変更する。変更するオブジェクトは、検索条件で指定するか、キャッシュされたオブジェクトコピーへのポインタで指定する。 

nis_first_entry()

NIS+ テーブルの最初のエントリオブジェクトのコピーを返す 

nis_next_entry()

NIS+ テーブルの「次」のエントリオブジェクトのコピーを返す。この関数への呼び出しと呼び出しの間に、テーブルの更新やエントリの追加・変更が行われる可能性があるので、返されたエントリの順序が実際のテーブル内のエントリ順序と一致しない場合がある。 

nis_local_names()

現在のプロセスのデフォルト名を取り出す。 

nis_local_directory()

ワークステーションの NIS+ ドメイン名を返す。 

nis_local_host()

修飾子付きのワークステーション名を返す。修飾子付きのワークステーション名は、host-namedomain-nameという形式になる。

nis_local_group()

現在の NIS+ グループ名を返す。現在の NIS+ グループは、環境変数 NIS_GROUP で指定される。 

nis_local_principal()

NIS+ プリンシパル名 (そのプリンシパルの UID は呼び出し側プロセスに結合) を返す。 

nis_getnames()

特定のネームに対する可能な展開形のリストを返す。 

nis_freenames()

nis_getnames()が生成したリストが使用するメモリを解放する。

nis_groups()

グループ操作と許可。 

nis_ismember()

あるプリンシパルがグループのメンバかどうか調べる。 

nis_addmember()

グループにメンバを追加する。メンバはプリンシパル、グループ、ドメインのどれか。 

nis_removemember()

グループからメンバを削除する。 

nis_creategroup()

グループオブジェクトを作成する。 

nis_destroygroup()

グループオブジェクトを削除する。 

nis_verifygroup()

グループオブジェクトが存在しているかどうか調べる。 

nis_print_group_entry()

グループオブジェクトのメンバになっているプリンシパルをリストする。 

nis_server

NIS+ アプリケーションのための種々のサービス。 

nis_mkdir()

特定ホストの特定ディレクトリに対するサービスをサポートするためのデータベースを作成する。 

nis_rmdir()

ホストからディレクトリを削除する。 

nis_servstate()

NIS+ サーバの状態変数の設定と読み込みを行い、内部キャッシュをフラッシュする。 

nis_stats()

サーバのパフォーマンスに関する統計情報を取り出す。 

nis_getservlist()

特定のドメインをサポートするサーバのリストを返す。 

nis_freeservlist()

nis_getservlist() が返したサーバリストが使用するメモリを解放する。

nis_freetags()

nis_servstate()nis_stats() の戻り値が使用するメモリを解放する。

nis_db

NIS+ サーバとデータベースの間のインタフェース。NIS+ クライアントでは使用不可。 

db_first_entry()

指定したテーブルの最初のエントリのコピーを返す。 

db_next_entry()

指定したエントリの次のエントリのコピーを返す。 

db_reset_next_entry()

最初または次のエントリシーケンスを終了する。 

db_list_entries()

指定した属性に一致するエントリのコピーを返す。 

db_remove_entry()

指定した属性に一致するエントリをすべて削除する。 

db_add_entry()

指定した属性に一致するテーブルエントリを、指定したオブジェクトのコピーで置き換える。または、指定したオブジェクトをテーブルに追加する 

db_checkpoint()

テーブルの内容を再編成して、テーブルのアクセス効率を改善する。 

db_standby()

データベースマネージャに資源の解放を勧める。 

nis_error()

NIS+ のステータス値に対応するメッセージ文字列を取り出す関数。 

nis_sperrno()

メッセージ文字列定数へのポインタを返す。 

nis_perror()

メッセージ文字列定数を標準出力に表示する。 

nis_lerror()

メッセージ文字列定数を syslog に送信する。 

nis_sperror()

strdup() 使用するかまたはでコピーするために静的領域に割り当てられた文字列へのポインタを返す。

nis_admin

トランザクションのログを取る関数。サーバで使用。 

nis_ping

ディレクトリのマスタサーバが、ディレクトリのタイムスタンプを作成するのに使用する。この関数を実行すると、複製も強制的に更新される。 

nis_checkpoint()

ログデータを強制的にディスク上のテーブルに保存する。 

nis_subr

NIS+ のネームとオブジェクトの操作のための補助関数。 

nis_leaf_of()

NIS+ ネームの最初のラベルを返す。返されたネームには、末尾のピリオドは付いていない。 

nis_name_of()

ネームからドメイン関連のラベルをすべて削除して、固有のオブジェクト部分だけを返す。この関数に渡されたネームは、ローカルドメイン、または、その子ドメインでなければならない。それ以外の場合は NULL が返される。 

nis_domain_of()

オブジェクトが入っているドメイン名を返す。返されたドメイン名はピリオドで終わっている。 

nis_dir_cmp()

2 つの NIS+ 名を比較する。大文字小文字の違いは無視して比較し、ネームが同じか、派生関係にあるか、無関係かを返す。. 

nis_clone_object()

NIS+ オブジェクトの完全な複製を作成する。

nis_destroy_object()

nis_clone_object() で作成したオブジェクトを破棄する。

nis_print_object()

NIS+ オブジェクト構造体の内容を標準出力に表示する.

NIS+ サンプルプログラム

サンプルプログラムでは次のタスクを実行します。

サンプルプログラムは典型的なアプリケーションとはいえません。通常は、ディレクトリとテーブルの作成と削除はコマンド行インタフェースで行い、アプリケーションでは NIS+ エントリオブジェクトの操作だけを行います。

サポートされないマクロの使用

サンプルプログラムでは <rpcsvc/nis.h> ファイルで定義されているマクロを使用しています。ここで定義されているマクロは、サポートが保証されている正式な API ではなく、将来変更されたりなくなったりする可能性があります。ここではサンプルプログラムで使用方法を説明するために使用していますが、実際のプログラムで使用するときはユーザ自身の責任で使用してください。サンプルプログラムで使用しているマクロを次に示します。

サンプルプログラムで使用する関数

サンプルプログラムでは、次の NIS+ API 関数を C 言語で使用する方法を示します。

nis_add()nis_add_entry()nis_addmember()

nis_creategroup()nis_destroygroup()nis_domain_of()

nis_freeresult()nis_leaf_of()nis_list()

nis_local_directory()nis_local_principal()nis_lookup()

nis_mkdir()nis_perror()nis_remove()

nis_remove_entry()nis_removemember()

プログラムのコンパイル

例 5-1 が示すサンプルプログラムは、これを実行する NIS+ プリンシパルがローカルドメインにディレクトリオブジェクトを作成することを許可されているものとして書かれています。このプログラムをコンパイルするコマンドを次に示します。

yourhost% cc -o example.c example -lnsl

このプログラムを実行するコマンドを次に示します。

yourhost% example [dir]

ここで、dir には NIS+ ディレクトリを指定します。サンプルプログラムは、すべての NIS+ オブジェクトをそのディレクトリに作成します。引数 dir を指定しないと、ローカルドメインの親ディレクトリにオブジェクトが作成されます。nis_lookup() の呼び出しでは、ディレクトリを指定する文字列に空白とローカルドメイン名が追加されることに注意してください。引数で指定するのは、作成した NIS+ オブジェクトを入れるための NIS+ ディレクトリ名です。このプログラムを実行するプリンシパルは、そのディレクトリ内でのオブジェクト作成許可を持っている必要があります。


例 5-1 NIS+ のプログラム例 example.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <rpcsvc/nis.h>

#define			MAX_MESG_SIZE 512
#define			BUFFER_SIZE 64
#define			TABLE_TYPE "test_data"

main(argc, argv)
	int		argc;
	char		*argv[];
{
	char		*saved_grp, *saved_name, *saved_owner;
	char		dir_name[NIS_MAXNAMELEN];
	char		local_domain[NIS_MAXNAMELEN];
	char		local_princip [NIS_MAXNAMELEN];
	char		org_dir_name [NIS_MAXNAMELEN];
	char		grp_name [NIS_MAXNAMELEN];
	char		grp_dir_name [NIS_MAXNAMELEN];
	char		table_name [NIS_MAXNAMELEN];
	nis_object				*dirobj, entdata;
	nis_result				*pres;
	u_int		saved_num_servers;
	int		err;

	if (argc == 2)
		sprintf (local_domain, "%s.", argv[1]);
	else
		strcpy (local_domain, "");

	strcat (local_domain, (char *) nis_local_directory());
	strcpy (local_princip, (char *) nis_local_principal());
	/*
	 * 次の 2 つの目的で、ディレクトリオブジェクトからローカルドメインを探す。
	 * 1.nis_object のテンプレートを得る。
	 * 2.返されたディレクトリオブジェクトに含まれる情報を再利用する。
	 * static で宣言した nis_object があり、それをほとんどそのままで
	 * 使用できる場合は、nis_object 構造体を初期化するより、それを変更して
	 * 使用するほうが簡単である。
	 */

	pres = nis_lookup (local_domain, 0);
	if (pres->status != NIS_SUCCESS) {
		nis_perror (pres->status, "unable to lookup local
directory");
		exit (1);
	}
	/*
	 * 親ディレクトリオブジェクトのほとんどのフィールドを再利用 - 変更する
	 * フィールドへのポインタを保存しておき、元のオブジェクトを解放しても
	 * ポインタ参照が残らないようにする。
	 */
	dirobj = NIS_RES_OBJECT (pres);
	saved_name = dirobj->DI_data.do_name;
	saved_owner = dirobj--->zo_owner;
	saved_grp = dirobj---->zo_group;

	/*
	 * 新たなネーム、グループ、所有者、および、foo ドメインへの新たな
	 * アクセス権を設定する。
	 */
	sprintf (dir_name, "%s.%s", "foo", local_domain);
	sprintf (grp_name, "%s.%s", "admins", dir_name);
	dirobj->DI_data.do_name = dir_name;
	dirobj->zo_group = grp_name;
	dirobj->zo_owner = local_princip;

	/*
	 * NIS+ のアクセス権は u_long 型に保存される。最上位バイトは使用不可で、
	 * 次の 8 バイトは所有者に、続いて、グループ、ワールドの順に保存される。
	 * この例では、ディレクトリへのアクセス権は "----rmcdrmcd----" の
	 * パターンに従って与える。
	 */
	dirobj->zo_access = ((NIS_READ_ACC + NIS_MODIFY_ACC
						+ NIS_CREATE_ACC + NIS_DESTROY_ACC) << 16)
						| ((NIS_READ_ACC + NIS_MODIFY_ACC
						+ NIS_CREATE_ACC + NIS_DESTROY_ACC) << 8);

	/*
	 * 親ディレクトリオブジェクトが持っていたサーバ数を保存しておき、
	 * 後に nis_freeresult() を呼び出す前にこの値を元に戻して、
	 * メモリリークを避ける。
	 */
	saved_num_servers = dirobj-
	>DI_data.do_servers.do_servers_len;

	/* このディレクトリは 1 つのサーバだけで管理する。 */
	dirobj->DI_data.do_servers.do_servers_len = 1;

	dir_create (dir_name, dirobj);

	/* foo の下に groups_dir と org_dir という 2 つのディレクトリを作成する。 */
	sprintf (grp_dir_name, "groups_dir.%s", dir_name);
	dirobj->DI_data.do_name = grp_dir_name;
	dir_create (grp_dir_name, dirobj);

	sprintf (org_dir_name, "org_dir.%s", dir_name);
	dirobj->DI_data.do_name = org_dir_name;
	dir_create (org_dir_name, dirobj);

	grp_create (grp_name);

	printf ("¥nAdding principal %s to group %s ... ¥n",
				local_princip, grp_name);
	err = nis_addmember (local_princip, grp_name);
	if (err != NIS_SUCCESS) {
	 	nis_perror (err,
			"unable to add local principal to group.");
		exit (1);
	}

	sprintf (table_name, "test_table.org_dir.%s", dir_name);
	tbl_create (dirobj, table_name);

	/*
	 * 作成したばかりのテーブルに、NIS+ エントリオブジェクトを作成する。
	 */
	stuff_table (table_name);

	/* 作成したオブジェクトを表示する。 */
	list_objs(dir_name, table_name, grp_name);

	/* 作成したものすべてのクリーンアップ。 */
	cleanup (local_princip, grp_name, table_name, dir_name,
dirobj);

	/*
	 * オリジナルの pres 構造体から保存しておいたポインタを元に戻し、
	 * 関連メモリを解放してもメモリリークが起こらないようにする
	 */
	dirobj->DI_data.do_name = saved_name;
	dirobj->zo_group = saved_grp;
	dirobj->zo_owner = saved_owner;
	dirobj->DI_data.do_servers.do_servers_len =
	saved_num_servers;
	(void) nis_freeresult (pres);
}

例 5-2 が示すルーチンは main() から呼び出され、ディレクトリオブジェクトを作成します。


例 5-2 ディレクトリオブジェクトを作成する NIS+ ルーチン

void
dir_create (dir_name, dirobj)
	nis_name			dir_name;
	nis_object			*dirobj;
{
	nis_result 	*cres;
	nis_error			err;

	printf ("¥n Adding Directory %s to namespace ... ¥n",
	dir_name);
	cres = nis_add (dir_name, dirobj);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status, "unable to add directory foo.");
		exit (1);
	}

	(void) nis_freeresult (cres);

	/*
	 * 注: nis_mkdir を実行して、作成するディレクトリの内容を保存する
	 * テーブルを作成する必要がある。
	 */
	err = nis_mkdir (dir_name,
						dirobj->DI_data.do_servers.do_servers_val);
	if (err != NIS_SUCCESS) {
		(void) nis_remove (dir_name, 0);
		nis_perror (err,
				"unable to create table for  directory object foo.");
		exit (1);
	}
}

次のルーチンは main() から呼び出され、グループオブジェクトを作成します。nis_creategroup() はグループオブジェクトだけを対象としているので、グループ名には groups_dir というリテラルは必要ありません。


例 5-3 グループオブジェクトを作成する NIS+ ルーチン

void
grp_create (grp_name)
	nis_name			grp_name;
{
	nis_error			err;

	printf ("¥n Adding %s group to namespace ... ¥n", grp_name);
	err = nis_creategroup (grp_name, 0);
	if (err != NIS_SUCCESS) {
		nis_perror (err, "unable to create group.");
		exit (1);
	}
}

例 5-3 が示すルーチンは main() から呼び出され、テーブルオブジェクトを表 5-3 のようなレイアウトで作成します。

表 5-3 NIS+ テーブルオブジェクト

 

カラム 1 

カラム 2 

ネーム: 

id 

name 

属性: 

検索可能、テキスト 

検索可能、テキスト 

アクセス権 

----rmcdr---r---

----rmcdr---r---

定数 TA_SEARCHABLE を指定すると、サービスカラムを検索可能とします。TEXT カラム(デフォルト属性)だけが検索可能です。TA_CASE を指定すると、サービスは、カラムの値を検索するときに大文字小文字を区別しません。


例 5-4 テーブルオブジェクトを作成する NIS+ ルーチン

#define			TABLE_MAXCOLS 2
#define			TABLE_COLSEP ':'
#define			TABLE_PATH 0

void
tbl_create (dirobj, table_name)
	nis_object 	*dirobj;					/* フィールドによっては必要 */
	nis_name			table_name;
{
	nis_result				*cres;
	static nis_object					tblobj;
	static table_col					tbl_cols[TABLE_MAXCOLS] = {
		{"Id", TA_SEARCHABLE | TA_CASE, DEFAULT_RIGHTS},
		{"Name", TA_SEARCHABLE | TA_CASE, DEFAULT_RIGHTS}
	};

	tblobj.zo_owner = dirobj->zo_owner;
	tblobj.zo_group = dirobj->zo_group;
	tblobj.zo_access = DEFAULT_RIGHTS;    /* nis.h で定義されたマクロ */
	tblobj.zo_data.zo_type = TABLE_OBJ;   /* nis.h で定義された列挙型 */
	tblobj.TA_data.ta_type = TABLE_TYPE;
	tblobj.TA_data.ta_maxcol = TABLE_MAXCOLS;
	tblobj.TA_data.ta_sep = TABLE_COLSEP;
	tblobj.TA_data.ta_path = TABLE_PATH;
	tblobj.TA_data.ta_cols.ta_cols_len =
	tblobj.TA_data.ta_maxcol;							  /* 常にこの指定 ! */
	tblobj.TA_data.ta_cols.ta_cols_val = tbl_cols;

/*
 * 修飾子付きのテーブル名、すなわち、org_dir というリテラルが埋め込まれた
 * テーブル名を使用する。nis_add はあらゆるタイプの NIS+ オブジェクトを
 * 操作するからであり、テーブルが作成済みならばそのフルパス名も必要である。
 */
	printf ("¥n Creating table %s ... ¥n", table_name);
	cres = nis_add (table_name, &tblobj);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status, "unable to add table.");
		exit (1);
	}
	(void) nis_freeresult (cres);
}

例 5-5 が示すルーチンは main() から呼び出され、テーブルオブジェクトにエントリオブジェクトを追加します。2 つのエントリがテーブルオブジェクトに追加されます。どちらのエントリのカラム幅も、文字列のターミネータとしての NULL 文字を含む値で設定することに注意してください。


例 5-5 テーブルにオブジェクトを追加する NIS+ ルーチン

#define			MAXENTRIES 2
void
stuff_table(table_name)
	nis_name 	table_name;
{
	int			i;
	nis_object			entdata;
	nis_result			*cres;
	static entry_col ent_col_data[MAXENTRIES][TABLE_MAXCOLS] = {
			{0, 2, "1", 0, 5, "John"},
			{0, 2, "2", 0, 5, "Mary"}
	};

	printf ("¥n Adding entries to table ... ¥n");

	/*
	 * テーブルに追加するエントリは、テーブルと同じ所有者、グループ所有者、
	 * アクセス権を持たなければならないので、はじめにテーブルオブジェクトを
	 * 調べる。
	 */
	cres = nis_lookup (table_name, 0);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status, "Unable to lookup table");
		exit(1);
	}
	entdata.zo_owner = NIS_RES_OBJECT (cres)->zo_owner;
	entdata.zo_group = NIS_RES_OBJECT (cres)->zo_group;
	entdata.zo_access = NIS_RES_OBJECT (cres)->zo_access;
	
	/* cres を解放して再利用できるようにする。 */
	(void) nis_freeresult (cres);

	entdata.zo_data.zo_type = ENTRY_OBJ; /* nis.h で定義された列挙型 */
	entdata.EN_data.en_type = TABLE_TYPE;
	entdata.EN_data.en_cols.en_cols_len = TABLE_MAXCOLS;
	for (i = 0; i < MAXENTRIES; ++i) {
		entdata.EN_data.en_cols.en_cols_val = &ent_col_data[i][0];
		cres = nis_add_entry (table_name, &entdata, 0);
		if (cres->status != NIS_SUCCESS) {
			nis_perror (cres->status, "unable to add entry.");
			exit (1);
		}
		(void) nis_freeresult (cres);
	}
}

例 5-6 が示すルーチンは nis_list() の呼び出しで使用する印刷関数です。list_objs()nis_list() を呼び出すときに、引数の 1 つとして print_info() へのポインタを渡します。サービスが print_info() を呼び出すたびに、エントリオブジェクトの内容が印刷されます。戻り値は、ライブラリに対して、テーブルの次のエントリを呼び出すように指示します。


例 5-6 nis_list() を呼び出す NIS+ ルーチン

int
print_info (name, entry, cbdata)
	nis_name			name;			/* 未使用 */
	nis_object			*entry;			/* NIS+ エントリオブジェクト */
	void			*cbdata;			/* 未使用 */
{
	static u_int				firsttime = 1;
	entry_col				*tmp;		/* ソースを読みやすくするためだけに使用 */
	u_int				i, terminal;

	if (firsttime) {
		printf ("¥tId.¥t¥t¥tName¥n");
		printf ("¥t---¥t¥t¥t----¥n");
		firsttime = 0;
	}
	for (i = 0; i < entry->EN_data.en_cols.en_cols_len; ++i) {
		tmp = &entry->EN_data.en_cols.en_cols_val[i];
		terminal = tmp->ec_value.ec_value_len;
		tmp->ec_value.ec_value_val[terminal] = '¥0';
	}

	/*
	 * ENTRY_VAL は、指定したエントリの特定カラムの値を返すマクロ 
	 */
	printf("¥t%s¥t¥t¥t%s¥n", ENTRY_VAL (entry, 0),
								ENTRY_VAL (entry, 1));
	return (0); /* さらに呼出しを行う */
}

例 5-7 が示すルーチンは main() から呼び出され、グループ、テーブル、ディレクトリオブジェクトの内容をリストします。次のルーチンで、コールバックの使用方法を示します。グループのメンバを取り出して表示します。グループのメンバリストは、オブジェクトの中に保存されていないので、nis_list() ではなく、nis_lookup() で調べます。nis_lookup() は、グループだけでなくあらゆるタイプの NIS+ オブジェクトを扱うため、グループ名は groups_dir を付けた形式で指定しなければなりません。


例 5-7 オブジェクトをリストする NIS+ ルーチン

void
list_objs(dir_name, table_name, grp_name)
         nis_name   dir_name, table_name, grp_name;
{
	group_obj			*tmp; 	/* ソースを読みやすくするためだけに使用 */
	u_int			i;
	char			grp_obj_name [NIS_MAXNAMELEN];
	nis_result			*cres;
	char			index_name [BUFFER_SIZE];

	sprintf (grp_obj_name, "%s.groups_dir.%s",
				nis_leaf_of (grp_name), nis_domain_of (grp_name));
	printf ("¥nGroup %s membership is: ¥n", grp_name);

	cres = nis_lookup(grp_obj_name, 0);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status, "Unable to lookup group object.");
		exit(1);
	}
	tmp = &(NIS_RES_OBJECT(cres)->GR_data);
	for (i = 0; i < tmp->gr_members.gr_members_len; ++i)
		printf ("¥t %s¥n", tmp->gr_members.gr_members_val[i]);
	(void) nis_freeresult (cres);

	/*
	 * ドメイン foo の内容を、コールバックを使用しないで表示する。
	 */
	printf ("¥nContents of Directory %s are: ¥n", dir_name);
	cres = nis_list (dir_name, 0, 0, 0);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status,"Unable to list Contents of Directory foo.");
		exit(1);
	}
	for (i = 0; i < NIS_RES_NUMOBJ(cres); ++i)
		printf("¥t%s¥n", NIS_RES_OBJECT(cres)[i].zo_name);
	(void) nis_freeresult (cres);

	/*
	 * 作成したテーブルの内容を、nis_list()でコールバックを使用してリスト
	 * する。
	 */
	printf ("¥n Contents of Table %s are: ¥n", table_name);
	cres = nis_list (table_name, 0, print_info, 0);
	if(cres->status != NIS_CBRESULTS && cres->status !=
NIS_NOTFOUND){
		nis_perror (cres->status,
			"Listing entries using callback failed");
		exit(1);
	}
	(void) nis_freeresult (cres);

	/*
	 * 作成したテーブルの 1 エントリだけをリストする。エントリの取り出しには、
	 * インデックスの付いた名前を使用する。
	 */

	printf("¥n Entry corresponding to id 1 is:¥n");
	/*
	 * 通常、カラム名はテーブルオブジェクトから抽出できるので、 
	 * 始めにそれを取り出す。
	 */
	sprintf(index_name, "[Id=1],%s", table_name);
	cres = nis_list (index_name, 0, print_info, 0);
	if(cres->status != NIS_CBRESULTS && cres->status !=
NIS_NOTFOUND){
		nis_perror (cres->status,
		"Listing entry using indexed names and callback failed");
		exit(1);
	}
	(void) nis_freeresult (cres);
}

例 5-8 のルーチンは cleanup() から呼び出され、ネーム空間からディレクトリオブジェクトを削除します。また、このディレクトリを管理しているサーバにも、ネーム空間から削除したことを通知します。ポインタ cres が指している戻り値の構造体が入ったメモリは、戻り値が使用済みになってから解放しなければならないことに注意してください。


例 5-8 ディレクトリオブジェクトを削除する NIS+ ルーチン

void
dir_remove(dir_name, srv_list, numservers)
	nis_name			dir_name;
	nis_server			*srv_list;
	u_int			numservers;
{
	nis_result			*cres;
	nis_error			err;
	u_int			i;

	printf ("¥nRemoving %s directory object from namespace ...
	¥n",dir_name);
	cres = nis_remove (dir_name, 0);
	if (cres->status != NIS_SUCCESS) {
		nis_perror (cres->status, "unable to remove directory");
		exit (1);
	}
	(void) nis_freeresult (cres);

	for (i = 0; i < numservers; ++i) {
		err = nis_rmdir (dir_name, &srv_list[i]);
		if (err != NIS_SUCCESS) {
			nis_perror (err,
			"unable to remove server from directory");
			exit (1);
		}
	}
}

例 5-9 のルーチンは main() から呼び出され、このサンプルプログラムで作成したオブジェクトをすべて削除します。nis_remove_entry() を呼び出すときのフラグ REM_MULTIPLE の使用法に注意してください。テーブルを削除するときは、その前にテーブル内の全エントリを削除しておく必要があります。


例 5-9 オブジェクトをすべて削除する NIS+ ルーチン

void
cleanup(local_princip, grp_name, table_name, dir_name, dirobj)
	nis_name			local_princip, grp_name, table_name, dir_name;
	nis_object			*dirobj;
{
	char		grp_dir_name [NIS_MAXNAMELEN];
	char		org_dir_name [NIS_MAXNAMELEN];
	nis_error			err;
	nis_result			*cres;

	sprintf(grp_dir_name, "%s.%s", "groups_dir", dir_name);
	sprintf(org_dir_name, "%s.%s", "org_dir", dir_name);

	printf("¥n¥n¥nStarting to Clean up ... ¥n");
	printf("¥n¥nRemoving principal %s from group %s ¥n",
						local_princip, grp_name);
	err = nis_removemember (local_princip, grp_name);
	if (err != NIS_SUCCESS) {
		nis_perror (err,
		"unable to delete local principal from group.");
		exit (1);
	}

	/*
	 * admins グループを削除する。グループ削除の API は、グループだけを 
	 * 対象にしているので、グループ名に group_dir は不要。
	 * グループ名には、自動的にリテラル group_dir が埋め込まれる。  
	 */
	printf("¥nRemoving %s group from namespace ... ¥n",
	grp_name);
	err = nis_destroygroup (grp_name);
	if (err != NIS_SUCCESS) {
		nis_perror (err, "unable to delete group.");
		exit (1);
	}
	printf("¥n Deleting all entries from table %s ... ¥n",
table_name);
	cres = nis_remove_entry(table_name, 0, REM_MULTIPLE);
	switch (cres->status) {
		case NIS_SUCCESS:
		case NIS_NOTFOUND:
			break;
		default:
			nis_perror(cres->status, "Could not delete entries from
										table");
			exit(1);
	}
	(void) nis_freeresult (cres);

	printf("¥n Deleting table %s itself ... ¥n", table_name);
	cres = nis_remove(table_name, 0);
	if (cres->status != NIS_SUCCESS) {
		nis_perror(cres->status, "Could not delete table.");
	exit(1);
	}
	(void) nis_freeresult (cres);

	/* groups_dir、org_dir、foo の各ディレクトリオブジェクトを削除する。*/
	dir_remove (grp_dir_name,
						dirobj->DI_data.do_servers.do_servers_val,
						dirobj->DI_data.do_servers.do_servers_len);
	dir_remove (org_dir_name,
						dirobj->DI_data.do_servers.do_servers_val,
						dirobj->DI_data.do_servers.do_servers_len);
	dir_remove (dir_name,
						dirobj->DI_data.do_servers.do_servers_val,
						dirobj->DI_data.do_servers.do_servers_len);
}

このプログラムを実行すると、画面は 図 5-3 のように表示されます。

図 5-3 NIS+ プログラムの実行

myhost% domainname
 sun.com
 myhost% ./sample
 Adding Directory foo.sun.com. to namespace ...
 Adding Directory groups_dir.foo.sun.com. to namespace ...
 Adding Directory org_dir.foo.sun.com. to namespace ...
 Adding admins.foo.sun.com. group to namespace ...
 Adding principal myhost.sun.com. to group admins.foo.sun.com. ...
 Creating table test_table.org_dir.foo.sun.com. ...
 Adding entries to table ...
 Group admins.foo.sun.com. membership is:
         myhost.sun.com.
 Contents of Directory foo.sun.com. are:
         groups_dir
         org_dir

 Contents of Table test_table.org_dir.foo.sun.com. are:
         Id.                     Name
         ---                     ----
         1                       John
         2                       Mary

 Entry corresponding to id 1 is:
         1                       John

 Starting to Clean up ...

 Removing principal myhost.sun.com. from group admins.foo.sun.com.
 Removing admins.foo.sun.com. group from namespace ...
 Deleting all entries from table test_table.org_dir.foo.sun.com. ...
 Deleting table test_table.org_dir.foo.sun.com. itself ...
 Removing groups_dir.foo.sun.com. directory object from namespace ...
 Removing org_dir.foo.sun.com. directory object from namespace ...
 Removing foo.sun.com. directory object from namespace ...
 myhost%

デバッグのために、次の一連のコマンドを入力して、これと同じ操作を実行することもできます。最初のコマンド

% niscat -o domainname.

を実行すると、マスタサーバ名が表示されます。以下に示すコマンドを入力するときは、master のところを、表示されたマスタサーバ名に置き換えて入力します。

% nismkdir -m master foo.domainname.
# 指定されたマスタでサブディレクトリ org_dir.foo を作成します。
% nismkdir -m master org_dir.foo.domainname.
# 指定されたマスタでサブディレクトリ groups_dir.foo を作成します。
% nismkdir -m master groups_dir.foo.domainname.
# グループ admins を作成します。
% nisgrpadm -c admins.foo.domainname.
# 自分自身をこのグループのメンバとして追加します。
% nisgrpadm -a admins.foo.domainname. `nisdefaults -p`
# Id と Name の 2 つのカラムで test_table を作成します。
% nistbladm -c test_data id=SI Name=SI ¥
test_table.org_dir.foo.domainname.
# そのテーブルにエントリを 1 つ追加します。
% nistbladm -a id=1 Name=John test_table.org_dir.foo.domainname.
# そのテーブルに別のエントリを追加します。
% nistbladm -a id=2 Name=Mary test_table.org_dir.foo.domainname.
# グループ admins のメンバをリストする。
% nisgrpadm -l admins.foo.domainname.
# ディレクトリ foo の内容をリストする。
% nisls foo.domainname.
# test_table の内容とヘッダをリストする。
% niscat -h test_table.org_dir.foo.domainname.
# test_table から id が 1 のエントリを取り出す。
% nismatch id=1 test_table.org_dir.foo.domainname.
# 作成したものをすべて削除する。
# 初めに、グループ admins から自分自身を削除する。
% nisgrpadm -r admins.foo.domainname. `nisdefaults -p`
# グループ admins を削除する。
% nisgrpadm -d admins.foo.domainname.
# test_table からすべてのエントリを削除する。
% nistbladm -r "[],test_table.org_dir.foo.domainname."
# test_table を削除する。
% nistbladm -d test_table.org_dir.foo.domainname.
# 作成した 3 つのディレクトリをすべて削除する。
% nisrmdir groups_dir.foo.domainname.
% nisrmdir org_dir.foo.domainname.
% nisrmdir foo.domainname.