Skip to content
Snippets Groups Projects
lustre-snmp-util.c 19.43 KiB
/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
 * vim:expandtab:shiftwidth=8:tabstop=8:
 *
 *  Copyright (c) 2005 Cluster File Systems, Inc.
 *   Author: PJ Kirner <pjkirner@clusterfs.com>
 *
 *   This file is part of Lustre, http://www.lustre.org.
 *
 *   Lustre is free software; you can redistribute it and/or
 *   modify it under the terms of version 2 of the GNU General Public
 *   License as published by the Free Software Foundation.
 *
 *   Lustre is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Lustre; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *   include important headers
 */

#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <net-snmp/agent/net-snmp-agent-includes.h>

/*
 *  include our .h file
 */ 

#include <sys/types.h>
#include <sys/vfs.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdarg.h>
#include "lustre-snmp-util.h"

/*********************************************************************
 * Function:    get_file_list
 *
 * Description: For the given valid directory  path, returns the list
 *              all directories or files in that path.
 *
 * Input:   'dirname' the directory path.
 *          'file_type' if this takes the value DIR_TYPE then
 *              returns the list of directories in that path.
 *          If its of type FILE_TYPE then returns the list of files
 *          in that path.
 *          'count' pointer to number of elements returned in the
 *          return string. 
 *
 * Output:  List of  directories/files in that path.
 *
 *********************************************************************/

char *get_file_list(const char *dirname, int file_type, uint32_t *count)
{

    DIR           *pdir = NULL;
    struct dirent *pdirent = NULL;
    int           curr_offset = 0;
    int           byte_count = 0;
    int           file_count = 0;
    char          *ret_str = NULL;
    char          filename[MAX_PATH_SIZE];
    int           cond1, cond2;

    if ((dirname == NULL) || ((pdir = opendir(dirname)) == NULL )) {
        if (dirname == NULL) {
            report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
                   "NULL directory is passed as parameter to funtion");
        } else {
            report("%s %s:line %d Error in opening the dir %s", __FILE__,
                   __FUNCTION__, __LINE__, dirname);
        }
        if (count)
            *count = 0;
        return NULL;
    }

    while (1) {
        if ((pdirent = readdir(pdir)) == NULL)
            break;

        /* Skip over '.' and '..' directores */
        if ((pdirent->d_name[0] == '.') ||
            !strcmp(pdirent->d_name, FILENAME_NUM_REF))
            continue;
        
        sprintf(filename, "%s/%s", dirname, pdirent->d_name);
        cond1 = (file_type == FILE_TYPE) && is_directory(filename);
        cond2 = (file_type == DIR_TYPE) && (!is_directory(filename));

        if (cond1 || cond2)
            continue;

        /* Calculate the number of bytes for this new entry.*/                    
        byte_count += strlen(pdirent->d_name) + 1;
        file_count++;
    }
    if (count)
        *count = file_count;
    
    if (file_count != 0) {
        
        /* need one extra one for the finall NULL terminator*/
        if ((ret_str = (char *) malloc(byte_count + 1)) == NULL) {
            report("get_file_list() failed to malloc(%d)",byte_count+1);
            closedir(pdir);
            return NULL;
        }    
        
        rewinddir(pdir);
        
        while (file_count != 0) {
            if ((pdirent = readdir(pdir)) == NULL)
                break;

            if ((pdirent->d_name[0] == '.') ||
                !strcmp(pdirent->d_name, FILENAME_NUM_REF))
                continue;
            
            sprintf(filename, "%s/%s", dirname, pdirent->d_name);
            cond1 = (file_type == FILE_TYPE) && is_directory(filename);
            cond2 = (file_type == DIR_TYPE) && (!is_directory(filename));

            if (cond1 || cond2)
                continue;

            strcpy(ret_str + curr_offset, pdirent->d_name);
            curr_offset = curr_offset + strlen(pdirent->d_name) + 1;
            file_count--;
        }
        /* Put in the finall null terminator*/
        ret_str[byte_count] = '\0';
    }
    closedir(pdir);
    return ret_str;
}


/*********************************************************************
 * Function:    is_directory
 *
 * Description: Checks if given filename is a directory or not.
 *              all directories or files in that path.
 *
 * Input:   'filename' the directory path to be checked.
 *
 * Output:  Returns 1 if its a directory else 0.
 *
 *********************************************************************/

int is_directory(const char *filename)
{

    struct stat statf;
    int result;

    result = stat(filename, &statf);
    return ((result == SUCCESS) && (statf.st_mode & S_IFDIR));
}

/*********************************************************************
 * Function:    read_string
 *
 * Description: For the given valid file path, reads the data in
 *              that file.
 *
 * Input:   'filepath' the file whose data is to be accessed.
 *          'lustre_var' the data from the file is read into
 *           this variable, returned to the requestor.
 *          'var_max_size' the max size of the string
 *          'report_error' boolean if error should be reported on 
 *           missing filepath
 *
 * Output:  Returns SUCCESS if read successfully from file else
 *          returns ERROR.
 *********************************************************************/
 
int  read_string(const char *filepath, char *lustre_var, size_t var_max_size)
{
    FILE    *fptr = NULL;
    int     len = 0;
    int     ret_val = SUCCESS;
    int     report_error = 1;

    if ((filepath == NULL) || (lustre_var == NULL)) {
        report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
               "Input parameter is NULL");
        ret_val = ERROR;
    } else {
        fptr = fopen(filepath, "r");

        if (fptr == NULL) {
            if(report_error)
                report("%s %s:line %d Unable to open the file %s", __FILE__,
                       __FUNCTION__, __LINE__, filepath);
            ret_val = ERROR;
        } else {
            if (fgets(lustre_var, var_max_size, fptr) == NULL) {
                report("%s %s:line %d read failed for file %s", __FILE__,
                       __FUNCTION__, __LINE__, filepath);
                 ret_val = ERROR;
            } else {
                len = strlen(lustre_var);
                /*
                    Last char is EOF, before string ends,
                    so '\0' is moved to last but one.
                */
                lustre_var[len-1] = lustre_var[len];
            }
            fclose(fptr);
        }
    }
    return ret_val;
}

/**************************************************************************
 * Function:   lustrefs_ctrl
 *
 * Description: Execute /etc/init.d/lustre script for starting,
 *              stopping and restarting Lustre services in child process.
 *
 * Input:  Start/Stop/Restart Command Number.
 * Output: Returns  void
 *
 **************************************************************************/

void lustrefs_ctrl(int command)
{
    char *cmd[3];

    cmd[0] = LUSTRE_SERVICE;
    switch (command) {
    case ONLINE:
        cmd[1] = "start";
        break;
    case OFFLINE:
        cmd[1] = "stop";
        break;
    case RESTART:
        cmd[1] = "restart";
        break;
    default:
        return;
    }

    cmd[2] = (char *)0;

    if (fork() == 0) {
        execvp(cmd[0], cmd);
        report("failed to execvp(\'%s %s\')",cmd[0],cmd[1]);
    }
    return;
}

/*****************************************************************************
 * Function:     get_sysstatus
 *
 * Description:  Read /var/lustre/sysStatus file, and based on file contents
 *               return the status of Lustre services.
 *
 * Input:   void
 * Output:  Return ONLINE/OFFLINE/ONLINE PENDING/OFFLINE PENDING status
 *          values.
 *
 ****************************************************************************/

int get_sysstatus(void)
{
    FILE    *fptr = NULL;
    int     len = 0;
    int     ret_val = ERROR ;
    char    sys_status[50] = {0};
    
    if(SUCCESS == read_string(FILENAME_SYS_STATUS,sys_status,sizeof(sys_status)))
    {
        if (memcmp(sys_status, STR_ONLINE_PENDING,strlen(STR_ONLINE_PENDING)) == 0)
            ret_val = ONLINE_PENDING;
        else if (memcmp(sys_status, STR_ONLINE, strlen(STR_ONLINE)) == 0)
            ret_val = ONLINE;
        else if (memcmp(sys_status, STR_OFFLINE_PENDING,strlen(STR_OFFLINE_PENDING)) == 0)
            ret_val = OFFLINE_PENDING;
        else if (memcmp(sys_status, STR_OFFLINE, strlen(STR_OFFLINE)) == 0)
            ret_val = OFFLINE;
        else
            report("%s %s:line %d Bad Contents in file %s \'%s\'", __FILE__,
                __FUNCTION__, __LINE__, FILENAME_SYS_STATUS,sys_status);
    }
    return ret_val;
}


/*****************************************************************************
 * Function:     read_ulong
 *
 * Description:  Read long values from lproc and copy to the location
 *               pointed by input parameter.
 *
 * Input:   file path, and pointer for data to be copied
 *
 * Output:  Return ERROR or SUCCESS.
 *
 ****************************************************************************/

int read_ulong(const char *file_path, unsigned long *valuep)
{
    char    file_data[MAX_LINE_SIZE];
    int     ret_val;

    if ((ret_val = read_string(file_path, file_data,sizeof(file_data))) == SUCCESS){
        *valuep = strtoul(file_data,NULL,10);
    }
    return ret_val;
}

/*****************************************************************************
 * Function:     read_counter64
 *
 * Description:  Read counter64 values from lproc and copy to the location
 *               pointed by input parameter.
 *
 * Input:   file path, and pointer for data to be copied
 *
 * Output:  Return ERROR or SUCCESS.
 *
 ****************************************************************************/

int read_counter64(const char *file_path, counter64 *c64,int factor)
{
    char    file_data[MAX_LINE_SIZE];
    int     ret_val;
    unsigned long long tmp = 0;

    if ((ret_val = read_string(file_path, file_data,sizeof(file_data))) == SUCCESS) {
        tmp = atoll(file_data) * factor;
        c64->low = (ulong) (0x0FFFFFFFF & tmp);
        tmp >>= 32; /* Shift right by 4 bytes */
        c64->high = (ulong) (0x0FFFFFFFF & tmp);
    }
    return ret_val;
}
/*****************************************************************************
 * Function:     get_nth_entry_from_list
 *
 * Description:  Find the n'th entry from a null terminated list of string
 *
 * Input:   dir_list - the list
 *          num - the number of elements in the list
 *          index - the index we are looking for
 *
 * Output:  Return NULL on failure, or the string name on success.
 *
 ****************************************************************************/

const char *get_nth_entry_from_list(const char* dir_list,int num,int index)
{
    int i;
    int cur_ptr = 0;
    for(i=0;i<num;i++){
        
        /* 
         * if we've reached the end of the list for some reason
         * because num was wrong then stop processing
         */
        if( *(dir_list+cur_ptr) == 0)
            break;
            
        /* If we've found the right one */    
        if( i == index )
            return dir_list+cur_ptr;
            
        /* Move to the next one*/            
        cur_ptr += strlen(dir_list + cur_ptr)+1;
    }
    return NULL;
}

/*****************************************************************************
 * Function:    report
 *
 * Description: This function used to report error msg to stderr and log into
 *    log file(default file:/var/log/snmpd.log) when agent is started with
 *    debug option -Dlsnmpd
 * Input:   format string and variable arguments.
 * Output:  void
 ****************************************************************************/

void report(const char *fmt, ...)
{
    char buf[1024];

    va_list arg_list;
    va_start(arg_list, fmt);
    vsprintf(buf, fmt, arg_list);
    va_end(arg_list);

    DEBUGMSGTL(("lsnmpd", "%s\n", buf));
    fprintf(stderr, "%s\n", buf);
    return;
}



/**************************************************************************
 * Function:   oid_table_ulong_handler
 *
 * Description: Fetch a unsigned long from the given location.
 *              Setup var_len, and return a pointer to the data.
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* 
    oid_table_ulong_handler(
        const char* file_path,
        size_t  *var_len)
{
    static unsigned long ulong_ret;
    if (SUCCESS != read_ulong(file_path,&ulong_ret))
        return NULL;
    *var_len = sizeof(ulong_ret);
    return  (unsigned char *) &ulong_ret;
}

/**************************************************************************
 * Function:   oid_table_c64_handler
 *
 * Description: Fetch a counter64 from the given location.
 *              Setup var_len, and return a pointer to the data.
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* oid_table_c64_handler(const char* file_path,size_t  *var_len)
{
    static counter64 c64;
    if (SUCCESS != read_counter64(file_path,&c64,1))
        return NULL;
    *var_len = sizeof(c64);
    return (unsigned char *) &c64;
}

/**************************************************************************
 * Function:   oid_table_c64_kb_handler
 *
 * Description: Fetch a counter64 from the given location.
 *              Setup var_len, and return a pointer to the data.
 *              Different than oid_table_c64_handler in that
 *              the original value is multiplied by 1024 before converting
 *              to a counter64.  (e.g. turn KB into a Byte scaled value)
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* oid_table_c64_kb_handler(const char* file_path,size_t  *var_len)
{
    static counter64 c64;
    /* scale by factor of 1024*/
    if (SUCCESS != read_counter64(file_path,&c64,1024))
        return NULL;
    *var_len = sizeof(c64);
    return (unsigned char *) &c64;
}

/**************************************************************************
 * Function:   oid_table_obj_name_handler
 *
 * Description: Just copy the file_path and return as the output value.
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* 
    oid_table_obj_name_handler(
        const char* file_path,
        size_t  *var_len)
{
    static unsigned char string[SPRINT_MAX_LEN];
    *var_len = strlen(file_path);
    *var_len = MIN_LEN(*var_len, sizeof(string));
    memcpy(string, file_path, *var_len);
    return (unsigned char *) string;
}

/**************************************************************************
 * Function:   oid_table_string_handler
 *
 * Description: Fetch a string from the given location.
 *              Setup var_len, and return a pointer to the data.
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* 
    oid_table_string_handler(
        const char* file_path,
        size_t  *var_len)
{
    static unsigned char string[SPRINT_MAX_LEN];
    if( SUCCESS != read_string(file_path, string,sizeof(string)))
        return NULL;
    *var_len = strlen(string);
    return (unsigned char *) string;
}


/**************************************************************************
 * Function:   oid_table_is_directory_handler
 *
 * Description: Determine if the file_path is a directory.  
 *              Setup a boolean return value.
 *              Setup var_len, and return a pointer to the data.
 *
 * Input:  file_path, and var_len pointer
 *
 * Output: NULL on failure, or pointer to data
 *
 **************************************************************************/

unsigned char* 
    oid_table_is_directory_handler(
        const char* file_path,
        size_t *var_len)
{
    static long long_ret;
    long_ret =  is_directory(file_path);
    *var_len = sizeof(long_ret);
    return (unsigned char *) &long_ret;
}

/**************************************************************************
 * Function:   var_genericTable
 *
 * Description: Handle Table driven OID processing
 *
 **************************************************************************/

unsigned char *
var_genericTable(struct variable *vp,
    	    oid     *name,
    	    size_t  *length,
    	    int     exact,
    	    size_t  *var_len,
    	    WriteMethod **write_method,
            const char *path,
            struct oid_table *ptable)
{
    char *dir_list;
    uint32_t num;
    int  deviceindex;
    unsigned char *ret_val = NULL;
    int i=0;
    const char* obj_name;
    
    
    /*
     * Get the list of file.  If there are no elements
     * return nothing
     */
    if( 0 == (dir_list = get_file_list(path, DIR_TYPE, &num)))
        return NULL;

    /*
     * Setup the table
     */
    if (header_simple_table(vp,name,length,exact,var_len,write_method, num)
                                                == MATCH_FAILED )
        goto cleanup_and_exit;

    /*
     * The number of the device we're looking at
     */
    deviceindex = name[*length - 1] - 1;

    /*
     * If we couldn't find this element
     * something must have recently changed return
     * nothing
     */
    if(deviceindex >= num){
        report("deviceindex=%d exceeds number of elements=%d",deviceindex,num);
        goto cleanup_and_exit;
    }

    /*
     * Fetch the object name from the list
     */
    obj_name = get_nth_entry_from_list(dir_list,num,deviceindex);
    if(obj_name == NULL){
        /*
         * Note this should never really happen because we check deviceindex >=num
         * above.  And dir_list should be consitent with num
         * but just in case...
         */
        report("object name not found in list",deviceindex,num);
        goto cleanup_and_exit;
    }

    /*
     * Find the matching magic - or the end of the list
     */
    while(ptable[i].magic != vp->magic && ptable[i].magic != 0)
        i++;

    /*
     * If we didn't find a matching entry return
     */
    if(ptable[i].magic==0)
        goto cleanup_and_exit;

    /*
     * If the name is NULL is a special case and 
     * just just pass the obj_name as the file_path
     * otherwise we create a file path from the given components
     */
    if(ptable[i].name != 0){
        char file_path[MAX_PATH_SIZE];
        sprintf(file_path, "%s%s/%s",path,obj_name,ptable[i].name);
        ret_val =  ptable[i].fhandler(file_path,var_len);
    }
    else
        ret_val =  ptable[i].fhandler(obj_name,var_len);

cleanup_and_exit:
    free(dir_list);
    return ret_val;
};