例 5-1 が示すサンプルプログラムは、これを実行する NIS+ プリンシパルがローカルドメインにディレクトリオブジェクトを作成することを許可されているものとして書かれています。このプログラムをコンパイルするコマンドを次に示します。
yourhost% cc -o example.c example -lnsl
このプログラムを実行するコマンドを次に示します。
yourhost% example [dir]
ここで、dir には NIS+ ディレクトリを指定します。サンプルプログラムは、すべての NIS+ オブジェクトをそのディレクトリに作成します。引数 dir を指定しないと、ローカルドメインの親ディレクトリにオブジェクトが作成されます。nis_lookup() の呼び出しでは、ディレクトリを指定する文字列に空白とローカルドメイン名が追加されることに注意してください。引数で指定するのは、作成した NIS+ オブジェクトを入れるための NIS+ ディレクトリ名です。このプログラムを実行するプリンシパルは、そのディレクトリ内でのオブジェクト作成許可を持っている必要があります。
#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() から呼び出され、ディレクトリオブジェクトを作成します。
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 というリテラルは必要ありません。
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 を指定すると、サービスは、カラムの値を検索するときに大文字小文字を区別しません。
#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 文字を含む値で設定することに注意してください。
#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() を呼び出すたびに、エントリオブジェクトの内容が印刷されます。戻り値は、ライブラリに対して、テーブルの次のエントリを呼び出すように指示します。
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 を付けた形式で指定しなければなりません。
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 が指している戻り値の構造体が入ったメモリは、戻り値が使用済みになってから解放しなければならないことに注意してください。
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 の使用法に注意してください。テーブルを削除するときは、その前にテーブル内の全エントリを削除しておく必要があります。
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 のように表示されます。
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.