Thursday, October 17, 2013

NIS programming

Working with NIS services at the system administration level is fine. In addition to all the built-in NIS maps and the flexibility of custom NIS maps, the standard arsenal of interface commands (ypwhich(1), ypmatch(1), ypcat(1), ypset(1M)) are pretty much sufficient for most daily tasks.

Nevertheless, there may be cases where interfacing with NIS services at programming level is a great and useful complement, easily incorporating some non-standard and centralized information into distributed and/or replicated applications.

Let's demonstrate with a couple of examples.
That should suffice to convey the main ideas.

Example 1:

This example mimics the functionality of ypmatch built-in command.

#include <cstdlib>
#include
<iostream>

#include <rpcsvc/ypclnt.h
>

#include "pointer.hxx"

void error_msg( char const * context, int code )
{
    std::cout
        << "Context: "
        << context
        << std::endl;

    std::cout
        << "Message: "
        << ::yperr_string( code )
        << "."
        << std::endl;
}

inline void std_c_free( void * p ) throw()
{
    ::free( p );
}

int main()
{
    int code = 0;
    char * domain = "";

    if ( ( code = ::yp_get_default_domain( & domain ) ) == 0 )
    {

        // Consider the previous custom NIS map example.
        // Hard-coded key for demonstration purposes.
        char map[] = "phonebook";
        char key[] = "user1";

 
        char * value = "";
        int length = 0;


        // This call is decoupled from the following if
        // just for the readability of this example.
        code = ::yp_match
        (
            domain, 

            map,
            key,

            sizeof(key) - 1,    // '\0' trim.
            & value, 

            & length
        );


        if ( code == 0 )
        {

            // Use a simple smart pointer
            // for automatic custom destruction.
            pointer< char, std_c_free > v_ptr( value );

            v_ptr[ length ] = '.';   
// '\n' override.
 

            std::cout
               
<< "Domain " << domain
                << ", map " << map
                << ", key " << key
                << ", value " << v_ptr
                <<
std::endl;
        }
        else
            error_msg( "yp_match()", code );
    }
    else
        error_msg( "yp_get_default_domain()", code );

    return EXIT_SUCCESS;
}


Example 2:

This example mimics the functionality of the ypcat built-in command.
As depicted on the man pages, this calls are using the UPD protocol.
This implies that the results may not be always accurate.

#include <cstdlib>
#include <iostream>
 
#include
<rpcsvc/ypclnt.h>

#include "pointer.hxx"


// Omitted stuff common to example 1...

int main()
{
    int code = 0;
    char * domain = "";

    if ( ( code = ::yp_get_default_domain( & domain ) ) == 0 )
    {


        // Consider the previous custom NIS map example.
        char map[] = "phonebook";

        char * key = "";
        int k_length = 0;

        char * value = "";
        int v_length = 0;


        // This call is decoupled from the following if         
        // just for the readability of this example. 
        code = ::yp_first
        (
            domain,
            map,
            & key,
            & k_length,
            & value,
            & v_length
        );
       
        if ( code == 0 )
        {
            do
            {

                // Use a simple smart pointer
                
// for automatic custom destruction.
                pointer< char, std_c_free > k_ptr( key );
                pointer
< char, std_c_free > v_ptr( value );
               
                
// Trim standard '\n' "garbage".
                k_ptr[ k_length ] = '\0';
                v_ptr[ v_length ] = '\0';
               
                std::cout
 
                    << k_ptr 
                    << ": " 
                    << v_ptr  
                    << std::endl;

                // This call is decoupled from the following if
                // just for the readability of this example. 
                code = ::yp_next
                (
                    domain,
                    map,
                    key,
                    k_length,
                    & key,
                    & k_length,
                    & value,
                    & v_length
                );

                if ( code != 0 )
                {
                    if ( code != YPERR_NOMORE )
                        message( "yp_next()", code );

                    break;
                }
            }
            while ( true );
        }
        else
            error_msg( "yp_first()", code );
    }
    else
        error_msg( "yp_get_default_domain()", code );

    return EXIT_SUCCESS;
}



Example 3:

This example also mimics the functionality of the ypcat built-in command.
As depicted on the man pages, this calls are using the TCP protocol.
This implies that the results should be always accurate.

#include <cstdlib>
#include <iostream>
 
#include
<rpcsvc/ypclnt.h>
#include <rpcsvc/yp_prot.h>

#include "pointer.hxx"


// Omitted stuff common to example 1...

// NIS callback support
 
extern "C" typedef int ( *callback ) ();

extern "C"
int pair 


    int status, 
    char const * const k, int k_length, 
    char const * const v, int v_length, 
    void
)
{
    if ( status == YP_TRUE )
    {

        //
        // k and v are private to yp_all().
        // Copy as needed, for instance, with:
        //
        //   std::string key( k, k_length );
        //   std::string value( v, v_length );
        //

        std::cout.write( k, k_length );
        std::cout << ' ';
       
        std::cout.write( v, v_length );       
        std::cout << std::endl;
       
        return 0;
    }
   
    int code = 0;
   
    if ( ( code = ::ypprot_err( status ) ) !=  YPERR_NOMORE )
        error_msg( "ypall_callback()", code );
   
    return EXIT_FAILURE;
}

int main()
{
    int code = 0;
    char * domain = "";

    if ( ( code = ::yp_get_default_domain( & domain ) ) == 0 )
    {

         // Consider the previous custom NIS map example.
         char map[] = "phonebook";
       
        ::ypall_callback dumper = { (callback) pair, 0 };
       
        if ( ( code = ::yp_all( domain, map, & dumper ) ) != 0 )
            error_msg( "yp_all()", code );
    }
    else
        error_msg( "yp_get_default_domain()", code );

    return EXIT_SUCCESS;
}