Пример #1
0
/**
 * Main daemon routine
 */
int main( int argc, char **argv )
{
    int            c, i, option_index = 0;
    char          *bin = basename( argv[0] );

    int            rc;
    char           err_msg[4096];
    robinhood_config_t rh_config;
    int chgd = 0;
    char           badcfg[RBH_PATH_MAX];
    char           tag_name[256] = "";

    start_time = time( NULL );

    zero_options( &options );

    /* parse command line options */
    while ( ( c = getopt_long( argc, argv, SHORT_OPT_STRING, option_tab, &option_index ) ) != -1 )
    {
        switch ( c )
        {
        case 's':
            options.partial_scan = TRUE;
            rh_strncpy(options.partial_scan_path, optarg, RBH_PATH_MAX);
            /* clean final slash */
            if (FINAL_SLASH(options.partial_scan_path))
                REMOVE_FINAL_SLASH(options.partial_scan_path);
            break;

        case 'd':
            if (parse_diff_mask(optarg, &options.diff_mask, err_msg))
            {
                fprintf(stderr,
                        "Invalid argument for --diff: %s\n", err_msg);
                exit( 1 );
            }
            break;

        case 'a':
            if (optarg)
            {
                if (!strcasecmp(optarg,"fs"))
                    options.diff_arg.apply = APPLY_FS;
                else if (!strcasecmp(optarg,"db"))
                    options.diff_arg.apply = APPLY_DB;
                else
                {
                    fprintf(stderr, "Invalid argument for --apply: '%s' (fs or db expected)\n",
                            optarg);
                    exit( 1 );
                }
            }
            else
                options.diff_arg.apply = APPLY_DB;
            break;

        case 'D':
                options.flags |= FLAG_DRY_RUN;
            break;

        case 'f':
            rh_strncpy(options.config_file, optarg, MAX_OPT_LEN);
            break;
#ifdef _HSM_LITE
        case 'b':
            options.diff_arg.recov_from_backend  = 1;
            break;
#endif
#ifdef _HAVE_FID /* only for lustre 2.x */
        case 'o':
            rh_strncpy(options.output_dir, optarg, MAX_OPT_LEN);
            break;
#endif
        case 'l':
            options.force_log_level = TRUE;
            options.log_level = str2debuglevel( optarg );
            if ( options.log_level == -1 )
            {
                fprintf( stderr,
                         "Unsupported log level '%s'. CRIT, MAJOR, EVENT, VERB, DEBUG or FULL expected.\n",
                         optarg );
                exit( 1 );
            }
            break;
        case 'h':
            display_help( bin );
            exit( 0 );
            break;
        case 'V':
            display_version( bin );
            exit( 0 );
            break;
        case ':':
        case '?':
        default:
            fprintf(stderr,"Run '%s --help' for more details.\n", bin);
            exit( 1 );
            break;
        }
    }

    /* check there is no extra arguments */
    if ( optind != argc )
    {
        fprintf( stderr, "Error: unexpected argument on command line: %s\n", argv[optind] );
        exit( 1 );
    }

    /* Initialize global tools */
#ifdef _LUSTRE
    if ( ( rc = Lustre_Init(  ) ) )
    {
        fprintf( stderr, "Error %d initializing liblustreapi\n", rc );
        exit( 1 );
    }
#endif

    /* Initilize uidgid cache */
    if ( InitUidGid_Cache(  ) )
    {
        fprintf( stderr, "Error initializing uid/gid cache\n" );
        exit( 1 );
    }

    /* get default config file, if not specified */
    if (SearchConfig(options.config_file, options.config_file, &chgd,
                      badcfg, MAX_OPT_LEN) != 0)
    {
        fprintf(stderr, "No config file (or too many) found matching %s\n", badcfg);
        exit(2);
    }
    else if (chgd)
    {
        fprintf(stderr, "Using config file '%s'.\n", options.config_file );
    }

    if ( ReadRobinhoodConfig( MODULE_MASK_FS_SCAN | MODULE_MASK_ENTRY_PROCESSOR,
                              options.config_file, err_msg,
                              &rh_config, FALSE ) )
    {
        fprintf( stderr, "Error reading configuration file '%s': %s\n",
                 options.config_file, err_msg );
        exit( 1 );
    }
    process_config_file = options.config_file;

    /* set global configuration */
    global_config = rh_config.global_config;

    /* set policies info */
    policies = rh_config.policies;

    if (options.force_log_level)
        rh_config.log_config.debug_level = options.log_level;
    else
        rh_config.log_config.debug_level = LVL_CRIT; /* no event message */

    /* Set logging to stderr */
    strcpy( rh_config.log_config.log_file, "stderr" );
    strcpy( rh_config.log_config.report_file, "stderr" );
    strcpy( rh_config.log_config.alert_file, "stderr" );

    /* Initialize logging */
    rc = InitializeLogs( bin, &rh_config.log_config );
    if ( rc )
    {
        fprintf( stderr, "Error opening log files: rc=%d, errno=%d: %s\n",
                 rc, errno, strerror( errno ) );
        exit( rc );
    }

    /* Initialize filesystem access */
    rc = InitFS();
    if (rc)
        exit(rc);

#ifdef _HSM_LITE
    rc = Backend_Start( &rh_config.backend_config, options.flags );
    if ( rc )
    {
        DisplayLog( LVL_CRIT, DIFF_TAG, "Error initializing backend" );
        exit( 1 );
    }
#endif

    /* Initialize list manager */
    rc = ListMgr_Init( &rh_config.lmgr_config, FALSE );
    if ( rc )
    {
        DisplayLog( LVL_CRIT, DIFF_TAG, "Error %d initializing list manager", rc );
        exit( rc );
    }
    else
        DisplayLog( LVL_VERB, DIFF_TAG, "ListManager successfully initialized" );


    if ( CheckLastFS(  ) != 0 )
        exit( 1 );

    if (options.diff_mask)
        rh_config.entry_proc_config.diff_mask = options.diff_mask;
    else
    {
        /* parse "all" */
        char tmpstr[] = "all";
        if (parse_diff_mask(tmpstr, &rh_config.entry_proc_config.diff_mask, err_msg))
        {
            DisplayLog(LVL_CRIT, DIFF_TAG, "unexpected error parsing diff mask: %s", err_msg);
            exit(1);
        }
    }

#ifdef LUSTRE_DUMP_FILES
    if (options.diff_arg.apply == APPLY_FS && !(options.flags & FLAG_DRY_RUN))
    {
        /* open the file to write LOV EA and FID remapping */
        if (!EMPTY_STRING(options.output_dir))
        {
            char fname[RBH_PATH_MAX];
            if (mkdir(options.output_dir, 0700) && (errno != EEXIST))
            {
                DisplayLog(LVL_CRIT, DIFF_TAG, "Failed to create directory %s: %s",
                           options.output_dir, strerror(errno));
                exit(1);
            }
            snprintf(fname, RBH_PATH_MAX-1, "%s/"LOVEA_FNAME, options.output_dir);
            options.diff_arg.lovea_file = fopen(fname, "w");
            if (options.diff_arg.lovea_file == NULL)
            {
                DisplayLog(LVL_CRIT, DIFF_TAG, "Failed to open %s for writting: %s",
                           fname, strerror(errno));
                exit(1);
            }
            snprintf(fname, RBH_PATH_MAX-1, "%s/"FIDREMAP_FNAME, options.output_dir);
            options.diff_arg.fid_remap_file = fopen(fname, "w");
            if (options.diff_arg.fid_remap_file == NULL)
            {
                DisplayLog(LVL_CRIT, DIFF_TAG, "Failed to open %s for writting: %s",
                           fname, strerror(errno));
                exit(1);
            }
        }
    }
#endif

    /* if no DB apply action is specified, can't use md_update field for checking
     * removed entries. So, create a special tag for that. */
    if ((options.diff_arg.apply != APPLY_DB) || (options.flags & FLAG_DRY_RUN))
    {
        fprintf(stderr, "Preparing diff table...\n");

        /* create a connexion to the DB. this is safe to use the global lmgr var
         * as statistics thread is not running */
        if (!ensure_db_access())
            exit(1);
        /* create a tag to clear entries after the scan */

        /* There could be several diff running in parallel,
         * so set a suffix to avoid conflicts */
        sprintf(tag_name, "DIFF_%u", (unsigned int) getpid());
        options.diff_arg.db_tag = tag_name;

        /* add filter for partial scan */
        if (options.partial_scan)
        {
            lmgr_filter_t  filter;
            filter_value_t val;
            lmgr_simple_filter_init( &filter );

            char tmp[RBH_PATH_MAX];
            strcpy(tmp, options.partial_scan_path);
            strcat(tmp, "/*");
            val.value.val_str = tmp;
            lmgr_simple_filter_add(&filter, ATTR_INDEX_fullpath, LIKE, val, 0);

            rc = ListMgr_CreateTag(&lmgr, tag_name, &filter, FALSE);
            lmgr_simple_filter_free(&filter);
        }
        else
            rc = ListMgr_CreateTag(&lmgr, tag_name, NULL, FALSE);

        if (rc)
            exit(rc);
    }

    /* Initialise Pipeline */
    rc = EntryProcessor_Init(&rh_config.entry_proc_config, DIFF_PIPELINE,
                             options.flags, &options.diff_arg);
    if ( rc )
    {
        DisplayLog( LVL_CRIT, DIFF_TAG, "Error %d initializing EntryProcessor pipeline", rc );
        goto clean_tag;
    }
    else
        DisplayLog( LVL_VERB, DIFF_TAG, "EntryProcessor successfully initialized" );

    fprintf(stderr, "Starting scan\n");

    /* print header to indicate the content of diff
     * #<diff cmd>
     * ---fs[=/subdir]
     * +++db
     */
    for (i = 0; i < argc; i++)
        printf("%s%s", i==0?"# ":" ", argv[i]);
    printf("\n");
    if (options.diff_arg.apply == APPLY_FS)
    {
        if (options.partial_scan)
            printf("---fs=%s\n",options.partial_scan_path);
        else
            printf("---fs\n");
        printf("+++db\n");
    }
    else
    {
        printf("---db\n");
        if (options.partial_scan)
            printf("+++fs=%s\n",options.partial_scan_path);
        else
            printf("+++fs\n");
    }

    /* Start FS scan */
    if (options.partial_scan)
        rc = FSScan_Start(&rh_config.fs_scan_config, options.flags,
                          options.partial_scan_path);
    else
        rc = FSScan_Start(&rh_config.fs_scan_config, options.flags, NULL);

    if ( rc )
    {
        DisplayLog( LVL_CRIT, DIFF_TAG, "Error %d initializing FS Scan module", rc );
        goto clean_tag;
    }
    else
        DisplayLog( LVL_VERB, DIFF_TAG, "FS Scan module successfully initialized" );

    /* Flush logs now, to have a trace in the logs */
    FlushLogs(  );

    /* both pipeline and scan are now running, can now trap events and display stats */

    /* create signal handling thread */
    rc = pthread_create( &sig_thr, NULL, signal_handler_thr, NULL );
    if ( rc )
    {
        DisplayLog( LVL_CRIT, DIFF_TAG, "Error starting signal handler thread: %s",
                    strerror( errno ) );
        goto clean_tag;
    }
    else
        DisplayLog( LVL_VERB, DIFF_TAG, "Signal handler thread started successfully" );

    pthread_create(&stat_thread, NULL, stats_thr, NULL);

    /* wait for FS scan to end */
    FSScan_Wait(  );
    DisplayLog( LVL_MAJOR, DIFF_TAG, "FS Scan finished" );

    /* Pipeline must be flushed */
    EntryProcessor_Terminate( TRUE );

#ifdef LUSTRE_DUMP_FILES
    /* flush the lovea file */
    if (options.diff_arg.lovea_file)
    {
        fprintf(stderr, " > LOV EA information written to %s/"LOVEA_FNAME"\n", options.output_dir);
        fclose(options.diff_arg.lovea_file);
    }
    if (options.diff_arg.fid_remap_file)
    {
        fprintf(stderr, " > FID remapping written to %s/"FIDREMAP_FNAME"\n", options.output_dir);
        fclose(options.diff_arg.fid_remap_file);
    }
#endif

    fprintf(stderr, "End of scan\n");

    DisplayLog( LVL_MAJOR, DIFF_TAG, "All tasks done! Exiting." );
    rc = 0;

clean_tag:
    /* destroy the tag before exit */
    if (options.diff_arg.db_tag != NULL && ensure_db_access())
    {
        fprintf(stderr, "Cleaning diff table...\n");
        ListMgr_DestroyTag(&lmgr, options.diff_arg.db_tag);
    }

    exit(rc);
    return rc; /* for compiler */
}
Пример #2
0
int main( int argc, char **argv )
{
    int            i;
    pthread_t      worker[NB_WORKER];
    void          *dummy;
    pthread_attr_t attr;
    robinhood_config_t config;
    char           err_msg[2048];

    srand( time( NULL ) + getpid(  ) );

    if ( argc < 2 )
    {
        fprintf( stderr, "Usage : test_fs_scan <config_file>\n" );
        exit( 1 );
    }

    /* Initilize uidgid cache */
    if ( InitUidGid_Cache(  ) )
    {
        fprintf( stderr, "Error initializing uid/gid cache\n" );
        exit( 1 );
    }

    /* only parse config for mandatory module */
    if ( ReadRobinhoodConfig
         ( MODULE_MASK_ENTRY_PROCESSOR | MODULE_MASK_INFO_COLLECTOR, argv[1], err_msg, &config ) )
    {
        fprintf( stderr, "Error reading configuration:\n%s\n", err_msg );
        exit( 1 );
    }



    /* set global configuration */
    global_config = config.global_config;

    /* set policies */
    purge_policy = config.purge_policy;


    InitializeLogs( "test_fs_scan", &config.log_config );

    /* Initialise List Mgr */
    if ( ListMgr_Init( &config.lmgr_config ) )
    {
        printf( "List Mgr init error" );
        return -1;
    }

    /* Initialise Pipeline */
    if ( EntryProcessor_Init( &config.entry_proc_config ) )
    {
        printf( "Pipeline init error" );
        return -1;
    }

    /* Start info collectors */
    if ( Start_Info_Collectors( &config.info_collect_config ) )
    {
        printf( "Info collector init error" );
        return -1;
    }


    pthread_attr_init( &attr );
    pthread_attr_setstacksize( &attr, 32 * 4096 );
    pthread_attr_setscope( &attr, PTHREAD_SCOPE_SYSTEM );

    while ( 1 )                 /* exit is done by db_apply thread */
    {
        sleep( 30 );
        EntryProcessor_DumpCurrentStages(  );
        Dump_Info_Collectors_Stats(  );
    }

    for ( i = 0; i < NB_WORKER; i++ )
        pthread_join( worker[i], &dummy );
    return 0;
}
Пример #3
0
/**
 * Main daemon routine
 */
int main( int argc, char **argv )
{
    int            c, option_index = 0;
    char          *bin = basename( argv[0] );

    char           config_file[MAX_OPT_LEN] = "";
    int            force_log_level = FALSE;
    int            log_level = 0;
    int            rc;
    int            chgd = 0;
    char           err_msg[4096];
    int            neg = 0;
    char           badcfg[RBH_PATH_MAX];

    /* parse command line options */
    while ((c = getopt_long_only(argc, argv, SHORT_OPT_STRING, option_tab,
                            &option_index )) != -1)
    {
        switch ( c )
        {
        case '!':
            neg = 1;
            break;
        case 'u':
            toggle_option(match_user, "user");
            prog_options.user = optarg;
            prog_options.userneg = neg;
            neg = 0;
            break;
        case 'g':
            toggle_option(match_group, "group");
            prog_options.group = optarg;
            prog_options.groupneg = neg;
            neg = 0;
            break;
        case 'n':
            toggle_option(match_name, "name");
            prog_options.name = optarg;
            prog_options.nameneg = neg;
            neg = 0;
            break;
#ifdef _LUSTRE
        case 'o':
            toggle_option(match_ost, "ost");
            prog_options.ost_idx = str2int(optarg);
            if (prog_options.ost_idx == (unsigned int)-1)
            {
                fprintf(stderr, "invalid ost index '%s': unsigned integer expected\n", optarg);
                exit(1);
            }
            if (neg) {
                fprintf(stderr, "! () is not supported for ost criteria\n");
                exit(1);
            }
            break;
#endif
        case 't':
            toggle_option(match_type, "type");
            prog_options.type = opt2type(optarg);
            if (prog_options.type == NULL)
            {
                fprintf(stderr, "invalid type '%s': expected types: "TYPE_HELP".\n", optarg);
                exit(1);
            }
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for type criteria\n");
                exit(1);
            }
            break;
        case 's':
            toggle_option(match_size, "size");
            if (set_size_filter(optarg))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for size criteria\n");
                exit(1);
            }
            break;

        case 'A':
            toggle_option(match_atime, "atime/amin");
            if (set_time_filter(optarg, 0, TRUE, atime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case 'a':
            toggle_option(match_atime, "atime/amin");
            if (set_time_filter(optarg, 60, TRUE, atime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case 'C':
            toggle_option(match_crtime, "crtime");
            if (set_time_filter(optarg, 0, TRUE, rh_crtime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case 'M':
            toggle_option(match_mtime, "mtime/mmin/msec");
            if (set_time_filter(optarg, 0, TRUE, mtime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case 'm':
            toggle_option(match_mtime, "mtime/mmin/msec");
            if (set_time_filter(optarg, 60, FALSE, mtime)) /* don't allow suffix (multiplier is 1min) */
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case 'z':
            toggle_option(match_mtime, "mtime/mmin/msec");
            if (set_time_filter(optarg, 1, FALSE, mtime)) /* don't allow suffix (multiplier is 1sec) */
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case OPT_ST_CTIME:
            toggle_option(match_st_ctime, "st-ctime");
            if (set_time_filter(optarg, 0, TRUE, stat_ctime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case OPT_ST_MTIME:
            toggle_option(match_st_mtime, "st-mtime");
            if (set_time_filter(optarg, 0, TRUE, stat_mtime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;

        case OPT_ST_ATIME:
            toggle_option(match_st_atime, "st-atime");
            if (set_time_filter(optarg, 0, TRUE, stat_atime))
                exit(1);
            if (neg) {
                fprintf(stderr, "! (-not) is not supported for time criteria\n");
                exit(1);
            }
            break;


#ifdef ATTR_INDEX_status
        case 'S':
            toggle_option(match_status, "status");
            prog_options.status = status2dbval(optarg);
            if ( prog_options.status == (file_status_t)-1 )
            {
                fprintf(stderr, "Unknown status '%s'. Allowed status: %s.\n", optarg,
                        allowed_status());
                exit(1);
            }
            prog_options.statusneg = neg;
            neg = 0;
            break;
#endif
        case 'l':
            prog_options.ls = 1;
            disp_mask = DISPLAY_MASK;
            if (neg) {
                fprintf(stderr, "! (-not) unexpected before -l option\n");
                exit(1);
            }
            break;
        case 'e':
            prog_options.lsstat = 1;
            disp_mask = LSSTAT_MASK;
            if (neg) {
                fprintf(stderr, "! (-not) unexpected before -e option\n");
                exit(1);
            }
            break;
#ifdef _LUSTRE
        case OPT_LSOST:
            prog_options.lsost = 1;
            disp_mask = LSOST_MASK;
            if (neg) {
                fprintf(stderr, "! (-not) unexpected before -lsost option\n");
                exit(1);
            }
            break;
#endif
        case 'f':
            strncpy( config_file, optarg, MAX_OPT_LEN );
            if (neg) {
                fprintf(stderr, "! (-not) unexpected before -f option\n");
                exit(1);
            }
            break;
        case 'd':
            force_log_level = TRUE;
            log_level = str2debuglevel( optarg );
            if ( log_level == -1 )
            {
                fprintf(stderr,
                        "Unsupported log level '%s'. CRIT, MAJOR, EVENT, VERB, DEBUG or FULL expected.\n",
                        optarg);
                exit(1);
            }
            if (neg) {
                fprintf(stderr, "! (-not) unexpected before -d option\n");
                exit(1);
            }
            break;
        case 'b':
            prog_options.bulk = 1;
            break;
        case 'h':
            display_help( bin );
            exit( 0 );
            break;
        case 'V':
            display_version( bin );
            exit( 0 );
            break;
        case ':':
        case '?':
        default:
            display_help( bin );
            exit( 1 );
            break;
        }
    }

    /* get default config file, if not specified */
    if ( SearchConfig( config_file, config_file, &chgd, badcfg ) != 0 )
    {
        fprintf(stderr, "No config file (or too many) found matching %s\n", badcfg);
        exit(2);
    }
    else if (chgd)
    {
        fprintf(stderr, "Using config file '%s'.\n", config_file );
    }

    /* only read ListMgr config */

    if ( ReadRobinhoodConfig( 0, config_file, err_msg, &config, FALSE ) )
    {
        fprintf( stderr, "Error reading configuration file '%s': %s\n", config_file, err_msg );
        exit( 1 );
    }
    process_config_file = config_file;

    /* set global configuration */
    global_config = config.global_config;

    /* set policies info */
    policies = config.policies;

    if ( force_log_level )
        config.log_config.debug_level = log_level;
    else
        config.log_config.debug_level = LVL_MAJOR; /* no event message */

    /* Set logging to stderr */
    strcpy( config.log_config.log_file, "stderr" );
    strcpy( config.log_config.report_file, "stderr" );
    strcpy( config.log_config.alert_file, "stderr" );

    /* Initialize logging */
    rc = InitializeLogs( bin, &config.log_config );
    if ( rc )
    {
        fprintf( stderr, "Error opening log files: rc=%d, errno=%d: %s\n",
                 rc, errno, strerror( errno ) );
        exit( rc );
    }

    /* Initialize filesystem access */
    rc = InitFS();
    if (rc)
        exit(rc);

    /* Initialize list manager */
    rc = ListMgr_Init( &config.lmgr_config, TRUE );
    if ( rc )
    {
        DisplayLog( LVL_CRIT, FIND_TAG, "Error %d initializing list manager", rc );
        exit( rc );
    }
    else
        DisplayLog( LVL_DEBUG, FIND_TAG, "ListManager successfully initialized" );

    if (CheckLastFS(  ) != 0)
        exit(1);

    /* Initialize UID/GID cache. */
    InitUidGid_Cache();

    /* Create database access */
    rc = ListMgr_InitAccess(&lmgr);
    if (rc)
    {
        DisplayLog( LVL_CRIT, FIND_TAG, "Error %d: cannot connect to database", rc );
        exit(rc);
    }

    if (argc == optind)
    {
        if (prog_options.bulk)
        {
            DisplayLog(LVL_DEBUG, FIND_TAG, "Performing bulk request on DB");
            mkfilters(FALSE); /* keep dirs */
            return list_bulk();
        }
        else
        {
            char *id = config.global_config.fs_path;
            mkfilters(TRUE); /* exclude dirs */
            /* no path specified, list all entries */
            rc = list_content(&id, 1);
        }
    }
    else
    {
        if (prog_options.bulk)
            fprintf(stderr, "No path argument expected with '-bulk' option. Option ignored.\n");

        mkfilters(TRUE); /* exclude dirs */
        rc = list_content(argv+optind, argc-optind);
    }

    ListMgr_CloseAccess( &lmgr );

    return rc;

}
Пример #4
0
int main(int argc, char **argv)
{
    unsigned int   i;
    uid_t          u;
    gid_t          g;
    unsigned int   c;
    struct timeval tinit, tcurr, tdiff, tlast = {0};
    struct timeval tref_u, tref_g = {0};
    float ratio;

    InitUidGid_Cache();

    printf("Reference test of getpwuid (%u items)\n", MAX_UID);
    gettimeofday(&tinit, NULL);
    for (i = 0; i <= MAX_UID/10; i++)
        for (u = 0; u < 10; u++)
            getpwuid(u);
    gettimeofday(&tcurr, NULL);
    timersub(&tcurr, &tinit, &tdiff);
    tref_u = tdiff;
    printf("Elapsed time: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
           MAX_UID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));

    printf("\nReference test of getgrgid (%u items)\n", MAX_GID);
    gettimeofday(&tinit, NULL);
    for (i = 0; i <= MAX_GID/10; i++)
        for (u = 0; u < 10; u++)
            getgrgid(u);
    gettimeofday(&tcurr, NULL);
    timersub(&tcurr, &tinit, &tdiff);
    tref_g = tdiff;
    printf("Elapsed time: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
           MAX_GID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));

    printf("\nTest of password cache\n");

    gettimeofday(&tinit, NULL);

    for (i = 0; i <= 10; i++)
    {
        c = 0;
        for (u = 0; u < MAX_UID; u++)
        {
            const struct passwd *ppw;

            ppw = GetPwUid(u);
            if (ppw)
                c++;
        }
        gettimeofday(&tcurr, NULL);
        timersub(&tcurr, &tinit, &tdiff);
        if (i == 0)
            tlast = tdiff;
        printf("loop %u, %u items: %lu.%06lu\n", i, c, tdiff.tv_sec, tdiff.tv_usec);
        if (i == 0)
        {
           printf("  Insertion rate: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
                  MAX_UID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));
        }
        else if (i == 1)
        {
            printf("  Elapsed time: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
                   MAX_UID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));

            ratio = (tlast.tv_sec*1000000.0 + tlast.tv_usec)/(tdiff.tv_sec*1000000.0 + tdiff.tv_usec);
            printf("  SPEED-UP (vs insert): x%.2f\n", ratio);

            ratio = (tref_u.tv_sec*1000000.0 + tref_u.tv_usec)/(tdiff.tv_sec*1000000.0 + tdiff.tv_usec);
            printf("  SPEED-UP (vs ref): x%.2f\n", ratio);
        }
        tinit = tcurr;
    }

    /* Now, check that the values returned are correct */
    for (u = 0; u < MAX_UID; u++)
    {
        const struct passwd *ppw;
        char buf[50];

        ppw = GetPwUid(u);
        assert(ppw != NULL);
        assert(ppw->pw_uid == u);

        sprintf(buf, "%ld", (long)u);
        assert(strcmp(ppw->pw_name, buf) == 0);
    }

    assert(GetPwUid(MAX_UID) == NULL);

    printf("\nTest of group cache\n");

    gettimeofday(&tinit, NULL);

    for (i = 0; i <= 10; i++)
    {
        c = 0;
        for (g = 0; g < MAX_GID; g++)
        {
            const struct group *pgr;

            pgr = GetGrGid(g);
            if (pgr)
                c++;
        }
        gettimeofday(&tcurr, NULL);
        timersub(&tcurr, &tinit, &tdiff);
        if (i == 0)
            tlast = tdiff;
        printf("loop %u, %u items: %lu.%06lu\n", i, c, tdiff.tv_sec, tdiff.tv_usec);
        if (i == 0)
        {
           printf("  Insertion rate: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
                  MAX_GID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));
        }
        else if (i == 1)
        {
            /* compute speedup */
            ratio = (tlast.tv_sec*1000000.0 + tlast.tv_usec)/(tdiff.tv_sec*1000000.0 + tdiff.tv_usec);
            printf("  SPEED-UP (vs insert): x%.2f\n", ratio);

            ratio = (tref_g.tv_sec*1000000.0 + tref_g.tv_usec)/(tdiff.tv_sec*1000000.0 + tdiff.tv_usec);
            printf("  SPEED-UP (vs ref): x%.2f\n", ratio);

            printf("  Elapsed time: %ld.%06ld (%.1f/s)\n", tdiff.tv_sec, tdiff.tv_usec,
                   MAX_GID / (tdiff.tv_sec + tdiff.tv_usec/1000000.0));
        }
        tinit = tcurr;
    }

    /* Now, check that the values returned are correct */
    for (g = 0; g < MAX_GID; g++)
    {
        const struct group *pgr;
        char buf[50];

        pgr = GetGrGid(g);
        assert(pgr != NULL);
        assert(pgr->gr_gid == g);

        sprintf(buf, "%ld", (long)g);
        assert(strcmp(pgr->gr_name, buf) == 0);
    }

    assert(GetGrGid(MAX_GID) == NULL);

    printf("Stats:\n");
    printf("  password cache hit=%d, miss=%d\n", pw_nb_get, pw_nb_set);
    printf("  group cache hit=%d, miss=%d\n", gr_nb_get, gr_nb_set);

    assert(pw_nb_get == 11 * MAX_UID);
    assert(pw_nb_set == MAX_UID);
    assert(gr_nb_get == 11 * MAX_GID);
    assert(gr_nb_set == MAX_GID);

    return 0;
}