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