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