Whamcloud - gitweb
b=14230
[fs/lustre-release.git] / snmp / lustre-snmp-util.c
1 /* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
2  * vim:expandtab:shiftwidth=8:tabstop=8:
3  *
4  *  Copyright (c) 2005 Cluster File Systems, Inc.
5  *   Author: PJ Kirner <pjkirner@clusterfs.com>
6  *
7  *   This file is part of Lustre, http://www.lustre.org.
8  *
9  *   Lustre is free software; you can redistribute it and/or
10  *   modify it under the terms of version 2 of the GNU General Public
11  *   License as published by the Free Software Foundation.
12  *
13  *   Lustre is distributed in the hope that it will be useful,
14  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *   GNU General Public License for more details.
17  *
18  *   You should have received a copy of the GNU General Public License
19  *   along with Lustre; if not, write to the Free Software
20  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22
23 /*
24  *   include important headers
25  */
26
27 #include <net-snmp/net-snmp-config.h>
28 #include <net-snmp/net-snmp-includes.h>
29 #include <net-snmp/agent/net-snmp-agent-includes.h>
30
31 /*
32  *  include our .h file
33  */ 
34
35 #include <sys/types.h>
36 #if defined (__linux__)
37 #include <sys/vfs.h>
38 #endif
39 #include <dirent.h>
40 #include <sys/stat.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <stdarg.h>
44 #include <string.h>
45 #include "lustre-snmp-util.h"
46
47 /*********************************************************************
48  * Function:    get_file_list
49  *
50  * Description: For the given valid directory  path, returns the list
51  *              all directories or files in that path.
52  *
53  * Input:   'dirname' the directory path.
54  *          'file_type' if this takes the value DIR_TYPE then
55  *              returns the list of directories in that path.
56  *          If its of type FILE_TYPE then returns the list of files
57  *          in that path.
58  *          'count' pointer to number of elements returned in the
59  *          return string. 
60  *
61  * Output:  List of  directories/files in that path.
62  *
63  *********************************************************************/
64
65 char *get_file_list(const char *dirname, int file_type, uint32_t *count)
66 {
67
68     DIR           *pdir = NULL;
69     struct dirent *pdirent = NULL;
70     int           curr_offset = 0;
71     int           byte_count = 0;
72     int           file_count = 0;
73     char          *ret_str = NULL;
74     char          filename[MAX_PATH_SIZE];
75     int           cond1, cond2;
76
77     if ((dirname == NULL) || ((pdir = opendir(dirname)) == NULL )) {
78         if (dirname == NULL) {
79             report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
80                    "NULL directory is passed as parameter to funtion");
81         } else {
82             report("%s %s:line %d Error in opening the dir %s", __FILE__,
83                    __FUNCTION__, __LINE__, dirname);
84         }
85         if (count)
86             *count = 0;
87         return NULL;
88     }
89
90     while (1) {
91         if ((pdirent = readdir(pdir)) == NULL)
92             break;
93
94         /* Skip over '.' and '..' directores */
95         if ((pdirent->d_name[0] == '.') ||
96             !strcmp(pdirent->d_name, FILENAME_NUM_REF))
97             continue;
98         
99         sprintf(filename, "%s/%s", dirname, pdirent->d_name);
100         cond1 = (file_type == FILE_TYPE) && is_directory(filename);
101         cond2 = (file_type == DIR_TYPE) && (!is_directory(filename));
102
103         if (cond1 || cond2)
104             continue;
105
106         /* Calculate the number of bytes for this new entry.*/                    
107         byte_count += strlen(pdirent->d_name) + 1;
108         file_count++;
109     }
110     if (count)
111         *count = file_count;
112     
113     if (file_count != 0) {
114         
115         /* need one extra one for the finall NULL terminator*/
116         if ((ret_str = (char *) malloc(byte_count + 1)) == NULL) {
117             report("get_file_list() failed to malloc(%d)",byte_count+1);
118             closedir(pdir);
119             return NULL;
120         }    
121         
122         rewinddir(pdir);
123         
124         while (file_count != 0) {
125             if ((pdirent = readdir(pdir)) == NULL)
126                 break;
127
128             if ((pdirent->d_name[0] == '.') ||
129                 !strcmp(pdirent->d_name, FILENAME_NUM_REF))
130                 continue;
131             
132             sprintf(filename, "%s/%s", dirname, pdirent->d_name);
133             cond1 = (file_type == FILE_TYPE) && is_directory(filename);
134             cond2 = (file_type == DIR_TYPE) && (!is_directory(filename));
135
136             if (cond1 || cond2)
137                 continue;
138
139             strcpy(ret_str + curr_offset, pdirent->d_name);
140             curr_offset = curr_offset + strlen(pdirent->d_name) + 1;
141             file_count--;
142         }
143         /* Put in the finall null terminator*/
144         ret_str[byte_count] = '\0';
145     }
146     closedir(pdir);
147     return ret_str;
148 }
149
150
151 /*********************************************************************
152  * Function:    is_directory
153  *
154  * Description: Checks if given filename is a directory or not.
155  *              all directories or files in that path.
156  *
157  * Input:   'filename' the directory path to be checked.
158  *
159  * Output:  Returns 1 if its a directory else 0.
160  *
161  *********************************************************************/
162
163 int is_directory(const char *filename)
164 {
165
166     struct stat statf;
167     int result;
168
169     result = stat(filename, &statf);
170     return ((result == SUCCESS) && (statf.st_mode & S_IFDIR));
171 }
172
173 /*********************************************************************
174  * Function:    read_string
175  *
176  * Description: For the given valid file path, reads the data in
177  *              that file.
178  *
179  * Input:   'filepath' the file whose data is to be accessed.
180  *          'lustre_var' the data from the file is read into
181  *           this variable, returned to the requestor.
182  *          'var_max_size' the max size of the string
183  *          'report_error' boolean if error should be reported on 
184  *           missing filepath
185  *
186  * Output:  Returns SUCCESS if read successfully from file else
187  *          returns ERROR.
188  *********************************************************************/
189  
190 int  read_string(const char *filepath, char *lustre_var, size_t var_max_size)
191 {
192     FILE    *fptr = NULL;
193     int     len = 0;
194     int     ret_val = SUCCESS;
195     int     report_error = 1;
196
197     if ((filepath == NULL) || (lustre_var == NULL)) {
198         report("%s %s:line %d %s", __FILE__, __FUNCTION__, __LINE__,
199                "Input parameter is NULL");
200         ret_val = ERROR;
201     } else {
202         fptr = fopen(filepath, "r");
203
204         if (fptr == NULL) {
205             if(report_error)
206                 report("%s %s:line %d Unable to open the file %s", __FILE__,
207                        __FUNCTION__, __LINE__, filepath);
208             ret_val = ERROR;
209         } else {
210             if (fgets(lustre_var, var_max_size, fptr) == NULL) {
211                 report("%s %s:line %d read failed for file %s", __FILE__,
212                        __FUNCTION__, __LINE__, filepath);
213                  ret_val = ERROR;
214             } else {
215                 len = strlen(lustre_var);
216                 /*
217                     Last char is EOF, before string ends,
218                     so '\0' is moved to last but one.
219                 */
220                 lustre_var[len-1] = lustre_var[len];
221             }
222             fclose(fptr);
223         }
224     }
225     return ret_val;
226 }
227
228 /**************************************************************************
229  * Function:   lustrefs_ctrl
230  *
231  * Description: Execute /etc/init.d/lustre script for starting,
232  *              stopping and restarting Lustre services in child process.
233  *
234  * Input:  Start/Stop/Restart Command Number.
235  * Output: Returns  void
236  *
237  **************************************************************************/
238
239 void lustrefs_ctrl(int command)
240 {
241     char *cmd[3];
242
243     cmd[0] = LUSTRE_SERVICE;
244     switch (command) {
245     case ONLINE:
246         cmd[1] = "start";
247         break;
248     case OFFLINE:
249         cmd[1] = "stop";
250         break;
251     case RESTART:
252         cmd[1] = "restart";
253         break;
254     default:
255         return;
256     }
257
258     cmd[2] = (char *)0;
259
260     if (fork() == 0) {
261         execvp(cmd[0], cmd);
262         report("failed to execvp(\'%s %s\')",cmd[0],cmd[1]);
263     }
264     return;
265 }
266
267 /*****************************************************************************
268  * Function:     get_sysstatus
269  *
270  * Description:  Read /var/lustre/sysStatus file, and based on file contents
271  *               return the status of Lustre services.
272  *
273  * Input:   void
274  * Output:  Return ONLINE/OFFLINE/ONLINE PENDING/OFFLINE PENDING status
275  *          values.
276  *
277  ****************************************************************************/
278
279 int get_sysstatus(void)
280 {
281     FILE    *fptr = NULL;
282     int     len = 0;
283     int     ret_val = ERROR ;
284     char    sys_status[50] = {0};
285     
286     if(SUCCESS == read_string(FILENAME_SYS_STATUS,sys_status,sizeof(sys_status)))
287     {
288         if (memcmp(sys_status, STR_ONLINE_PENDING,strlen(STR_ONLINE_PENDING)) == 0)
289             ret_val = ONLINE_PENDING;
290         else if (memcmp(sys_status, STR_ONLINE, strlen(STR_ONLINE)) == 0)
291             ret_val = ONLINE;
292         else if (memcmp(sys_status, STR_OFFLINE_PENDING,strlen(STR_OFFLINE_PENDING)) == 0)
293             ret_val = OFFLINE_PENDING;
294         else if (memcmp(sys_status, STR_OFFLINE, strlen(STR_OFFLINE)) == 0)
295             ret_val = OFFLINE;
296         else
297             report("%s %s:line %d Bad Contents in file %s \'%s\'", __FILE__,
298                 __FUNCTION__, __LINE__, FILENAME_SYS_STATUS,sys_status);
299     }
300     return ret_val;
301 }
302
303
304 /*****************************************************************************
305  * Function:     read_ulong
306  *
307  * Description:  Read long values from lproc and copy to the location
308  *               pointed by input parameter.
309  *
310  * Input:   file path, and pointer for data to be copied
311  *
312  * Output:  Return ERROR or SUCCESS.
313  *
314  ****************************************************************************/
315
316 int read_ulong(const char *file_path, unsigned long *valuep)
317 {
318     char    file_data[MAX_LINE_SIZE];
319     int     ret_val;
320
321     if ((ret_val = read_string(file_path, file_data,sizeof(file_data))) == SUCCESS){
322         *valuep = strtoul(file_data,NULL,10);
323     }
324     return ret_val;
325 }
326
327 /*****************************************************************************
328  * Function:     read_counter64
329  *
330  * Description:  Read counter64 values from lproc and copy to the location
331  *               pointed by input parameter.
332  *
333  * Input:   file path, and pointer for data to be copied
334  *
335  * Output:  Return ERROR or SUCCESS.
336  *
337  ****************************************************************************/
338
339 int read_counter64(const char *file_path, counter64 *c64,int factor)
340 {
341     char    file_data[MAX_LINE_SIZE];
342     int     ret_val;
343     unsigned long long tmp = 0;
344
345     if ((ret_val = read_string(file_path, file_data,sizeof(file_data))) == SUCCESS) {
346         tmp = atoll(file_data) * factor;
347         c64->low = (unsigned long) (0x0FFFFFFFF & tmp);
348         tmp >>= 32; /* Shift right by 4 bytes */
349         c64->high = (unsigned long) (0x0FFFFFFFF & tmp);
350     }
351     return ret_val;
352 }
353
354 /*****************************************************************************
355  * Function:     get_nth_entry_from_list
356  *
357  * Description:  Find the n'th entry from a null terminated list of string
358  *
359  * Input:   dir_list - the list
360  *          num - the number of elements in the list
361  *          index - the index we are looking for
362  *
363  * Output:  Return NULL on failure, or the string name on success.
364  *
365  ****************************************************************************/
366
367 const char *get_nth_entry_from_list(const char* dir_list,int num,int index)
368 {
369     int i;
370     int cur_ptr = 0;
371     for(i=0;i<num;i++){
372         
373         /* 
374          * if we've reached the end of the list for some reason
375          * because num was wrong then stop processing
376          */
377         if( *(dir_list+cur_ptr) == 0)
378             break;
379             
380         /* If we've found the right one */    
381         if( i == index )
382             return dir_list+cur_ptr;
383             
384         /* Move to the next one*/            
385         cur_ptr += strlen(dir_list + cur_ptr)+1;
386     }
387     return NULL;
388 }
389
390 /*****************************************************************************
391  * Function:    report
392  *
393  * Description: This function used to report error msg to stderr and log into
394  *    log file(default file:/var/log/snmpd.log) when agent is started with
395  *    debug option -Dlsnmpd
396  * Input:   format string and variable arguments.
397  * Output:  void
398  ****************************************************************************/
399
400 void report(const char *fmt, ...)
401 {
402     char buf[1024];
403
404     va_list arg_list;
405     va_start(arg_list, fmt);
406     vsprintf(buf, fmt, arg_list);
407     va_end(arg_list);
408
409     DEBUGMSGTL(("lsnmpd", "%s\n", buf));
410     fprintf(stderr, "%s\n", buf);
411     return;
412 }
413
414
415
416 /**************************************************************************
417  * Function:   oid_table_ulong_handler
418  *
419  * Description: Fetch a unsigned long from the given location.
420  *              Setup var_len, and return a pointer to the data.
421  *
422  * Input:  file_path, and var_len pointer
423  *
424  * Output: NULL on failure, or pointer to data
425  *
426  **************************************************************************/
427
428 unsigned char* 
429     oid_table_ulong_handler(
430         const char* file_path,
431         size_t  *var_len)
432 {
433     static unsigned long ulong_ret;
434     if (SUCCESS != read_ulong(file_path,&ulong_ret))
435         return NULL;
436     *var_len = sizeof(ulong_ret);
437     return  (unsigned char *) &ulong_ret;
438 }
439
440 /**************************************************************************
441  * Function:   oid_table_c64_handler
442  *
443  * Description: Fetch a counter64 from the given location.
444  *              Setup var_len, and return a pointer to the data.
445  *
446  * Input:  file_path, and var_len pointer
447  *
448  * Output: NULL on failure, or pointer to data
449  *
450  **************************************************************************/
451
452 unsigned char* oid_table_c64_handler(const char* file_path,size_t  *var_len)
453 {
454     static counter64 c64;
455     if (SUCCESS != read_counter64(file_path,&c64,1))
456         return NULL;
457     *var_len = sizeof(c64);
458     return (unsigned char *) &c64;
459 }
460
461 /**************************************************************************
462  * Function:   oid_table_c64_kb_handler
463  *
464  * Description: Fetch a counter64 from the given location.
465  *              Setup var_len, and return a pointer to the data.
466  *              Different than oid_table_c64_handler in that
467  *              the original value is multiplied by 1024 before converting
468  *              to a counter64.  (e.g. turn KB into a Byte scaled value)
469  *
470  * Input:  file_path, and var_len pointer
471  *
472  * Output: NULL on failure, or pointer to data
473  *
474  **************************************************************************/
475
476 unsigned char* oid_table_c64_kb_handler(const char* file_path,size_t  *var_len)
477 {
478     static counter64 c64;
479     /* scale by factor of 1024*/
480     if (SUCCESS != read_counter64(file_path,&c64,1024))
481         return NULL;
482     *var_len = sizeof(c64);
483     return (unsigned char *) &c64;
484 }
485
486 /**************************************************************************
487  * Function:   oid_table_obj_name_handler
488  *
489  * Description: Just copy the file_path and return as the output value.
490  *
491  * Input:  file_path, and var_len pointer
492  *
493  * Output: NULL on failure, or pointer to data
494  *
495  **************************************************************************/
496
497 unsigned char* 
498     oid_table_obj_name_handler(
499         const char* file_path,
500         size_t  *var_len)
501 {
502     static unsigned char string[SPRINT_MAX_LEN];
503     *var_len = strlen(file_path);
504     *var_len = MIN_LEN(*var_len, sizeof(string));
505     memcpy(string, file_path, *var_len);
506     return (unsigned char *) string;
507 }
508
509 /**************************************************************************
510  * Function:   oid_table_string_handler
511  *
512  * Description: Fetch a string from the given location.
513  *              Setup var_len, and return a pointer to the data.
514  *
515  * Input:  file_path, and var_len pointer
516  *
517  * Output: NULL on failure, or pointer to data
518  *
519  **************************************************************************/
520
521 unsigned char* 
522     oid_table_string_handler(
523         const char* file_path,
524         size_t  *var_len)
525 {
526     static unsigned char string[SPRINT_MAX_LEN];
527     if( SUCCESS != read_string(file_path, string,sizeof(string)))
528         return NULL;
529     *var_len = strlen(string);
530     return (unsigned char *) string;
531 }
532
533
534 /**************************************************************************
535  * Function:   oid_table_is_directory_handler
536  *
537  * Description: Determine if the file_path is a directory.  
538  *              Setup a boolean return value.
539  *              Setup var_len, and return a pointer to the data.
540  *
541  * Input:  file_path, and var_len pointer
542  *
543  * Output: NULL on failure, or pointer to data
544  *
545  **************************************************************************/
546
547 unsigned char* 
548     oid_table_is_directory_handler(
549         const char* file_path,
550         size_t *var_len)
551 {
552     static long long_ret;
553     long_ret =  is_directory(file_path);
554     *var_len = sizeof(long_ret);
555     return (unsigned char *) &long_ret;
556 }
557
558 /**************************************************************************
559  * Function:   var_genericTable
560  *
561  * Description: Handle Table driven OID processing
562  *
563  **************************************************************************/
564
565 unsigned char *
566 var_genericTable(struct variable *vp,
567             oid     *name,
568             size_t  *length,
569             int     exact,
570             size_t  *var_len,
571             WriteMethod **write_method,
572             const char *path,
573             struct oid_table *ptable)
574 {
575     char *dir_list;
576     uint32_t num;
577     int  deviceindex;
578     unsigned char *ret_val = NULL;
579     int i=0;
580     const char* obj_name;
581     
582     
583     /*
584      * Get the list of file.  If there are no elements
585      * return nothing
586      */
587     if( 0 == (dir_list = get_file_list(path, DIR_TYPE, &num)))
588         return NULL;
589
590     /*
591      * Setup the table
592      */
593     if (header_simple_table(vp,name,length,exact,var_len,write_method, num)
594                                                 == MATCH_FAILED )
595         goto cleanup_and_exit;
596
597     /*
598      * The number of the device we're looking at
599      */
600     deviceindex = name[*length - 1] - 1;
601
602     /*
603      * If we couldn't find this element
604      * something must have recently changed return
605      * nothing
606      */
607     if(deviceindex >= num){
608         report("deviceindex=%d exceeds number of elements=%d",deviceindex,num);
609         goto cleanup_and_exit;
610     }
611
612     /*
613      * Fetch the object name from the list
614      */
615     obj_name = get_nth_entry_from_list(dir_list,num,deviceindex);
616     if(obj_name == NULL){
617         /*
618          * Note this should never really happen because we check deviceindex >=num
619          * above.  And dir_list should be consitent with num
620          * but just in case...
621          */
622         report("object name not found in list",deviceindex,num);
623         goto cleanup_and_exit;
624     }
625
626     /*
627      * Find the matching magic - or the end of the list
628      */
629     while(ptable[i].magic != vp->magic && ptable[i].magic != 0)
630         i++;
631
632     /*
633      * If we didn't find a matching entry return
634      */
635     if(ptable[i].magic==0)
636         goto cleanup_and_exit;
637
638     /*
639      * If the name is NULL is a special case and 
640      * just just pass the obj_name as the file_path
641      * otherwise we create a file path from the given components
642      */
643     if(ptable[i].name != 0){
644         char file_path[MAX_PATH_SIZE];
645         sprintf(file_path, "%s%s/%s",path,obj_name,ptable[i].name);
646         ret_val =  ptable[i].fhandler(file_path,var_len);
647     }
648     else
649         ret_val =  ptable[i].fhandler(obj_name,var_len);
650
651 cleanup_and_exit:
652     free(dir_list);
653     return ret_val;
654 };
655
656 /**************************************************************************
657  * Function:   stats_values
658  *
659  * Description: Setup nb_sample, min, max, sum and sum_square stats values
660                 for name_value from filepath.
661  *
662  * Input:  filepath, name_value,
663  *         pointer to nb_sample, min, max, sum, sum_square
664  *
665  * Output: SUCCESS or ERROR on failure
666  *
667  **************************************************************************/
668 int stats_values(char * filepath,char * name_value, unsigned long long * nb_sample, unsigned long long * min, unsigned long long * max, unsigned long long * sum, unsigned long long * sum_square)
669 {
670   FILE * statfile;
671   char line[MAX_LINE_SIZE];
672   int nbReadValues = 0;
673
674   if( (statfile=fopen(filepath,"r")) == NULL) {
675     report("stats_value() failed to open %s",filepath);
676     return ERROR;
677   }
678 /*find the good line for name_value*/
679   do {
680     if( fgets(line,MAX_LINE_SIZE,statfile) == NULL ) {
681       report("stats_values() failed to find %s values in %s stat_file",name_value,statfile);
682       goto error_out;
683     }
684   } while ( strstr(line,name_value) == NULL );
685 /*get stats*/
686   if((nbReadValues=sscanf(line,"%*s %llu %*s %*s %llu %llu %llu %llu",nb_sample,min,max,sum,sum_square)) == 5) {
687     goto success_out;
688   } else if( nbReadValues == 1 && *nb_sample == 0) {
689     *min = *max = *sum = *sum_square = 0;
690     goto success_out;
691   } else {
692     report("stats_values() failed to read stats_values for %s value in %s stat_file",name_value,statfile);
693     goto error_out;
694   }
695
696 success_out :
697   fclose(statfile);
698   return SUCCESS;
699 error_out :
700   fclose(statfile);
701   return ERROR;
702 }
703
704 /**************************************************************************
705  * Function:   mds_stats_values
706  *
707  * Description: Setup nb_sample, min, max, sum and sum_square stats values
708                 for mds stats name_value .
709  *
710  * Input:  name_value,
711  *         pointer to nb_sample, min, max, sum, sum_square
712  *
713  * Output: SUCCESS or ERROR on failure
714  *
715  **************************************************************************/
716 extern int mds_stats_values(char * name_value, unsigned long long * nb_sample, unsigned long long * min, unsigned long long * max, unsigned long long * sum, unsigned long long * sum_square)
717 {
718   unsigned long long tmp_nb_sample=0,tmp_min=0,tmp_max=0,tmp_sum=0,tmp_sum_square=0;
719 /*we parse the three MDS stat files and sum values*/
720   if( stats_values(FILEPATH_MDS_SERVER_STATS,name_value,&tmp_nb_sample,&tmp_min,&tmp_max,&tmp_sum,&tmp_sum_square) == ERROR ) {
721     return ERROR;
722   } else {
723     *nb_sample=tmp_nb_sample;
724     *min=tmp_min;
725     *max=tmp_max;
726     *sum=tmp_sum;
727     *sum_square=tmp_sum_square;
728   }
729
730   if( stats_values(FILEPATH_MDS_SERVER_READPAGE_STATS,name_value,&tmp_nb_sample,&tmp_min,&tmp_max,&tmp_sum,&tmp_sum_square) == ERROR ) {
731     return ERROR;
732   } else {
733     *nb_sample += tmp_nb_sample;
734     *min += tmp_min;
735     *max += tmp_max;
736     *sum += tmp_sum;
737     *sum_square += tmp_sum_square;
738   }
739
740   if( stats_values(FILEPATH_MDS_SERVER_SETATTR_STATS,name_value,&tmp_nb_sample,&tmp_min,&tmp_max,&tmp_sum,&tmp_sum_square) == ERROR ) {
741     return ERROR;
742   } else {
743     *nb_sample += tmp_nb_sample;
744     *min += tmp_min;
745     *max += tmp_max;
746     *sum += tmp_sum;
747     *sum_square += tmp_sum_square;
748   }
749   
750   return SUCCESS;
751 }