ONC+ Developer's Guide

Program Compilation

The program shown in Example 5-1 assumes that the NIS+ principal running this application has permission to create directory objects in the local domain. The program is compiled:

yourhost% cc -o example.c example -lnsl

It is invoked:

yourhost% example [dir]

where dir is the NIS+ directory in which the program creates all the NIS+ objects. Specifying no directory argument causes the objects to be created in the parent directory of the local domain. Note that for the call to nis_lookup(), a space and the name of the local domain are appended to the string that names the directory. The argument is the name of the NIS+ directory in which to create the NIS+ objects. The principal running this program should have create permission in the directory.


Example 5-1 NIS+ Program Main 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());
	/*
	 * Lookup the directory object for the local domain for two
reasons:
	 * 1.To get a template of a nis_object.
	 * 2.To reuse some of the information contained in the
directory
	 * object returned. We could have declared a static
nis_object, but
	 * since we need to change very little, it is easier to make
the
	 * changes and not initialize the nis_object structure.
	 */

	pres = nis_lookup (local_domain, 0);
	if (pres->status != NIS_SUCCESS) {
		nis_perror (pres->status, "unable to lookup local
directory");
		exit (1);
	}
	/*
	 * re-use most of the fields in the parent directory object -
save
	 * pointers to the fields that are being changed so that we can
	 * free the original object and avoid dangling pointer
references.
	 */
	dirobj = NIS_RES_OBJECT (pres);
	saved_name = dirobj->DI_data.do_name;
	saved_owner = dirobj->zo_owner;
	saved_grp = dirobj->zo_group;

	/*
	 * set the new name, group, owner and new access rights for the
	 * foo domain.
	 */
	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;

	/*
	 * Access rights in NIS+ are stored in a u_long with the
highest
	 * order bytes reserved for the "nobody" category, the next
eight
	 * bytes reserved for the owner, followed by group and world.
In
	 * this example we are giving access to the directory based
on the
	 * "----rmcdrmcd----" access right pattern.
	 */
	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);

	/*
	 * Save the number of servers the parent directory object had
so
	 * that we can restore this value before calling
nis_freeresult()
	 * later and avoid memory leaks.
	 */
	saved_num_servers = dirobj-
>DI_data.do_servers.do_servers_len;

	/* We want only one server to serve this directory */
	dirobj->DI_data.do_servers.do_servers_len = 1;

	dir_create (dir_name, dirobj);

	/* create the groups_dir and org_dir directories under foo. */
	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);

	/*
	 * Now create NIS+ entry objects in the table that was just
created
	 */
	stuff_table (table_name);

	/* Display what we stuffed */
	list_objs(dir_name, table_name, grp_name);

	/* Clean out everything we created. */
	cleanup (local_princip, grp_name, table_name, dir_name,
dirobj);

	/*
	 * Restore the saved pointers from the original pres structure
 	 * so that we can free up the associated memory and have no
	 * memory leaks.
	 */
	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);
}

Example 5-2 shows the routine is called by main() to create directory objects.


Example 5-2 NIS+ Routine to Create Directory Objects

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);

	/*
	 * NOTE: you need to do a nis_mkdir to create the table to
store the
	 * contents of the directory you are creating.
	 */
	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);
	}
}

This routine is called by main() to create the group object. Since nis_creategroup() works only on group objects, the "groups_dir" literal is not needed in the group name.


Example 5-3 NIS+ Routine to Create Group Objects

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);
	}
}

The routine shown in Example 5-3 is called by main() to create a table object laid out as shown in Table 5-3.

Table 5-3 NIS+ Table Objects

 

Column1 

Column2 

Name: 

id 

name 

Attributes: 

Searchable, Text 

Searchable, Text 

Access Rights 

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

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

The TA_SEARCHABLE constant indicates to the service that the column is searchable. Only TEXT (the default) columns are searchable. TA_CASE indicates to the service that the column value is to be treated in a case-insensitive manner during searches.


Example 5-4 NIS+ Routine to Create Table Objects

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

void
tbl_create (dirobj, table_name)
	nis_object 	*dirobj;					/* need to use some of the fields */
	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;    /* macro defined in
nis.h  */
	tblobj.zo_data.zo_type = TABLE_OBJ;   /* enumerated type in
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;								  /* ALWAYS ! */
	tblobj.TA_data.ta_cols.ta_cols_val = tbl_cols;

/*
 * Use a fully qualified table name i.e. the "org_dir" literal
should
 * be embedded in the table name. This is necessary because
nis_add
 * operates on all types of NIS+ objects and needs the full path
name
 * if a table is created.
 */
	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);
}

The routine shown in Example 5-5 is called by main() to add entry objects to the table object. Two entries are added to the table object. Note that the column width in both entries is set to include the NULL character for a string terminator.


Example 5-5 NIS+ Routine to Add Objects to Table

#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");

	/*
	 * Look up the table object first since the entries being added
	 * should have the same owner, group owner and access rights as
	 * the table they go in.
	 */
	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;
	
	/* Free cres, so that it can be reused. */
	(void) nis_freeresult (cres);

	entdata.zo_data.zo_type = ENTRY_OBJ; /* enumerated type in
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);
	}
}

The routine shown in Example 5-6 is the print function for the nis_list() call. When list_objs() calls nis_list(), a pointer to print_info() is one of the calling arguments. Each time the service calls this function, it prints the contents of the entry object. The return value indicates to the library to call with the next entry from the table.


Example 5-6 NIS+ Routine for nis_list Call

int
print_info (name, entry, cbdata)
	nis_name			name;			/* Unused */
	nis_object			*entry;			/* The NIS+ entry object */
	void			*cbdata;			/* Unused */
{
	static u_int				firsttime = 1;
	entry_col				*tmp;		/* only to make source more readable */
	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 is a macro that returns the value of a specific
	 * column value of a specified entry.
	 */
	printf("\t%s\t\t\t%s\n", ENTRY_VAL (entry, 0),
								ENTRY_VAL (entry, 1));
	return (0); /* always ask for more */
}

The routine shown in Example 5-7 is called by main() to list the contents of the group, table and directory objects. The routine demonstrates the use of callbacks also. It retrieves and displays the membership of the group. The group membership list is not stored as the contents of the object. So, it is queried through the nis_lookup() instead of the nis_list() call. You must use the "groups_dir" form of the group name since nis_lookup() works on all types of NIS+ objects.


Example 5-7 NIS+ Routine to List Objects

void
list_objs(dir_name, table_name, grp_name)
         nis_name   dir_name, table_name, grp_name;
{
	group_obj			*tmp; 	/* only to make source more readable */
	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);

	/*
	 * Display the contents of the foo domain without using
callbacks.
	 */
	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);

	/*
	 * List the contents of the table we created using the callback
form
	 * of 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);

	/*
	 * List only one entry from the table we created. We will
	 * use indexed names to do this retrieval.
	 */

	printf("\n Entry corresponding to id 1 is:\n");
	/*
	 * The name of the column is usually extracted from the table
	 * object, which would have to be retrieved first.
	 */
	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);
}

The routine in Example 5-8 is called by cleanup() to remove a directory object from the namespace. It also informs the servers serving the directory about this deletion. Notice that the memory containing result structure, pointed to by cres, must be freed after the result has been tested.


Example 5-8 NIS+ Routine to Remove Directory Objects

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);
		}
	}
}

This routine, Example 5-9, is called by main() to delete all the objects that were created in this example. Note the use of the REM_MULTIPLE flag in the call to nis_remove_entry(). All entries must be deleted from a table before the table itself can be deleted.


Example 5-9 NIS+ Routine to Remove All Objects

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);
	}

	/*
	 * Delete the admins group. We do not use the "groups_dir" form
	 * of the group name since this API is applicable to groups
only.
	 * It automatically embeds the groups_dir literal in the name
of 
	 * the group.
	 */
	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);

	/* delete the groups_dir, org_dir and foo  directory objects.
*/
	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);
}

Running the program displays on the screen, as shown in Figure 5-3.

Figure 5-3 NIS+ Program Execution

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%

As a debugging aid, the same operations are performed by the following command sequence. The first command:

% niscat -o `domainname`

displays the name of the master server. Substitute the master server name where the variable master appears below.

% nismkdir -m master foo.`domainname`.
 
 # Create the org_dir.foo subdirectory with the specified master
 % nismkdir -m master org_dir.foo.`domainname`.
 # Create the groups_dir.foo subdirectory with the specified master
 % nismkdir -m master groups_dir.foo.`domainname`.
 # Create the "admins" group
 % nisgrpadm -c admins.foo.`domainname`.
 
 # Add yourself as a member of this group
 % nisgrpadm -a admins.foo.`domainname`. `nisdefaults -p`
 
 # Create a test_table with two columns : Id and Name
 % nistbladm -c test_data id=SI Name=SI \
 test_table.org_dir.foo.`domainname`
 
 # Add one entry to that table.
 % nistbladm -a id=1 Name=John test_table.org_dir.foo.`domainname`.
 # Add another entry to that table.
 % nistbladm -a id=2 Name=Mary test_table.org_dir.foo.`domainname`.
 
 # List the members of the group admins
 % nisgrpadm -l admins.foo.`domainname`.
 # List the contents of the foo directory
 % nisls foo.`domainname`.
 # List the contents of the test_table along with its header
 % niscat -h test_table.org_dir.foo.`domainname`.
 
 # Get the entry from the test_table where id = 1
 % nismatch id=1 test_table.org_dir.foo.`domainname`.
 
 # Delete all we created.
 # First, delete yourself from the admins group
 % nisgrpadm -r admins.foo.`domainname`. `nisdefaults -p`
 # Delete the admins group
 % nisgrpadm -d admins.foo.`domainname`.
 # Delete all the entries from the test_table
 % nistbladm -r "[],test_table.org_dir.foo.`domainname`."
 # Delete the test_table itself.
 % nistbladm -d test_table.org_dir.foo.`domainname`.
 # Delete all three directories that we created
 % nisrmdir groups_dir.foo.`domainname`.
 % nisrmdir org_dir.foo.`domainname`.
 % nisrmdir foo.`domainname`.