/* * disk_hw.c */ #include #include #if HAVE_STDLIB_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_FCNTL_H #include #endif #include #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include #include #include #include "struct.h" #include "disk.h" #include "disk_hw.h" #include "util_funcs/header_simple_table.h" #if USING_UCD_SNMP_ERRORMIB_MODULE #include "errormib.h" #else #define setPerrorstatus(x) snmp_log_perror(x) #endif /* * * config file parsing routines * */ static void disk_free_config(void); static void disk_parse_config(const char *, char *); static void disk_parse_config_all(const char *, char *); static netsnmp_fsys_info ** _expand_disk_array( char *cptr ); #define MAX_INT_32 0x7fffffff #define MAX_UINT_32 0xffffffff int numdisks; int allDisksIncluded = 0; int allDisksMinPercent = 0; int maxdisks = 0; netsnmp_fsys_info **disks = NULL; struct variable2 extensible_disk_variables[] = { {MIBINDEX, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {MIBINDEX}}, {ERRORNAME, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {ERRORNAME}}, {DISKDEVICE, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKDEVICE}}, {DISKMINIMUM, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKMINIMUM}}, {DISKMINPERCENT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKMINPERCENT}}, {DISKTOTAL, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKTOTAL}}, {DISKAVAIL, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKAVAIL}}, {DISKUSED, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKUSED}}, {DISKPERCENT, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKPERCENT}}, {DISKPERCENTNODE, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKPERCENTNODE}}, {ERRORFLAG, ASN_INTEGER, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {ERRORFLAG}}, {ERRORMSG, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {ERRORMSG}}, {DISKTOTALLOW, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKTOTALLOW}}, {DISKTOTALHIGH, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKTOTALHIGH}}, {DISKAVAILLOW, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKAVAILLOW}}, {DISKAVAILHIGH, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKAVAILHIGH}}, {DISKUSEDLOW, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKUSEDLOW}}, {DISKUSEDHIGH, ASN_UNSIGNED, NETSNMP_OLDAPI_RONLY, var_extensible_disk, 1, {DISKUSEDHIGH}}, }; /* * Define the OID pointer to the top of the mib tree that we're * registering underneath */ oid disk_variables_oid[] = { NETSNMP_UCDAVIS_MIB, NETSNMP_DISKMIBNUM, 1 }; void init_disk_hw(void) { /* * register ourselves with the agent to handle our mib tree */ REGISTER_MIB("ucd-snmp/disk", extensible_disk_variables, variable2, disk_variables_oid); snmpd_register_config_handler("disk", disk_parse_config, disk_free_config, "path [ minspace | minpercent% ]"); snmpd_register_config_handler("includeAllDisks", disk_parse_config_all, disk_free_config, "minpercent%"); allDisksIncluded = 0; allDisksMinPercent = 0; } static void disk_free_config(void) { netsnmp_fsys_info *entry; for ( entry = netsnmp_fsys_get_first(); entry != NULL; entry = netsnmp_fsys_get_next( entry )) { entry->minspace = -1; entry->minpercent = -1; entry->flags &= ~NETSNMP_FS_FLAG_UCD; } if (disks) { free( disks ); disks = NULL; maxdisks = numdisks = 0; } allDisksIncluded = 0; allDisksMinPercent = 0; } static void disk_parse_config(const char *token, char *cptr) { char path[STRMAX]; int minpercent; int minspace; netsnmp_fsys_info *entry; /* * Ensure there is space for the new entry */ if (numdisks == maxdisks) { if (!_expand_disk_array( cptr )) return; } /* * read disk path (eg, /1 or /usr) */ copy_nword(cptr, path, sizeof(path)); cptr = skip_not_white(cptr); cptr = skip_white(cptr); /* * read optional minimum disk usage spec */ if(cptr != NULL) { if(strchr(cptr, '%') == NULL) { minspace = atoi(cptr); minpercent = -1; } else { minspace = -1; minpercent = atoi(cptr); } } else { minspace = NETSNMP_DEFDISKMINIMUMSPACE; minpercent = -1; } /* * check if the disk already exists, if so then modify its * parameters. if it does not exist then add it */ entry = netsnmp_fsys_by_path( path, NETSNMP_FS_FIND_CREATE ); if ( entry ) { entry->minspace = minspace; entry->minpercent = minpercent; entry->flags |= NETSNMP_FS_FLAG_UCD; disks[numdisks++] = entry; } } static void disk_parse_config_all(const char *token, char *cptr) { int minpercent = DISKMINPERCENT; /* * read the minimum disk usage percent */ if(cptr != NULL) { if(strchr(cptr, '%') != NULL) { minpercent = atoi(cptr); } } /* * if we have already seen the "includeAllDisks" directive * then search for the disk in the "disks" array and modify * the values. if we havent seen the "includeAllDisks" * directive then include this disk */ if(allDisksIncluded) { config_perror("includeAllDisks already specified."); netsnmp_config_error("\tignoring: includeAllDisks %s", cptr); } else { allDisksIncluded = 1; allDisksMinPercent = minpercent; } } /* add new entries to dskTable dynamically */ static void _refresh_disks(int minpercent) { netsnmp_fsys_info *entry; for ( entry = netsnmp_fsys_get_first(); entry != NULL; entry = netsnmp_fsys_get_next( entry )) { if (!(entry->flags & NETSNMP_FS_FLAG_UCD)) { /* this is new disk, add it to the table */ entry->minspace = -1; entry->minpercent = minpercent; entry->flags |= NETSNMP_FS_FLAG_UCD; /* * Ensure there is space for the new entry */ if (numdisks == maxdisks) { if (!_expand_disk_array( entry->device )) return; } disks[numdisks++] = entry; } } } static int _percent( unsigned long long value, unsigned long long total ) { float v=value, t=total, pct; /* avoid division by zero */ if (total == 0) return 0; pct = (v*100)/t; /* Calculate percentage using floating point arithmetic, to avoid overflow errors */ pct += 0.5; /* rounding */ return (int)pct; } static netsnmp_fsys_info ** _expand_disk_array( char *cptr ) { if ( maxdisks == 0 ) maxdisks = 50; else maxdisks *= 2; disks = realloc( disks, maxdisks * sizeof( netsnmp_fsys_info*)); if (!disks) { config_perror("malloc failed for new disk allocation."); netsnmp_config_error("\tignoring: %s", cptr); return NULL; } if ( maxdisks == 50 ) memset(disks, 0, maxdisks * sizeof( netsnmp_fsys_info*)); else memset(disks + maxdisks/2, 0, maxdisks/2 * sizeof( netsnmp_fsys_info*)); return disks; } /* * var_extensible_disk(... * Arguments: * vp IN - pointer to variable entry that points here * name IN/OUT - IN/name requested, OUT/name found * length IN/OUT - length of IN/OUT oid's * exact IN - TRUE if an exact match was requested * var_len OUT - length of variable or 0 if function returned * write_method * */ u_char * var_extensible_disk(struct variable *vp, oid * name, size_t * length, int exact, size_t * var_len, WriteMethod ** write_method) { int disknum = 0; netsnmp_fsys_info *entry; unsigned long long val; static long long_ret; static char *errmsg; netsnmp_cache *cache; /* Update the fsys H/W module */ cache = netsnmp_fsys_get_cache(); netsnmp_cache_check_and_reload(cache); if (allDisksIncluded) _refresh_disks(allDisksMinPercent); tryAgain: if (header_simple_table (vp, name, length, exact, var_len, write_method, numdisks)) return (NULL); disknum = name[*length - 1] - 1; entry = disks[disknum]; if ( !entry ) { if (exact) return NULL; goto tryAgain; } if (!(entry->flags & NETSNMP_FS_FLAG_ACTIVE) || !(entry->flags & NETSNMP_FS_FLAG_UCD)) { if (exact) return NULL; goto tryAgain; } switch (vp->magic) { case MIBINDEX: long_ret = disknum + 1; return ((u_char *) (&long_ret)); case ERRORNAME: /* DISKPATH */ *var_len = strlen(entry->path); return ((u_char *)entry->path); case DISKDEVICE: *var_len = strlen(entry->device); return ((u_char *)entry->device); case DISKMINIMUM: long_ret = entry->minspace; return ((u_char *) (&long_ret)); case DISKMINPERCENT: long_ret = entry->minpercent; return ((u_char *) (&long_ret)); case DISKTOTAL: val = netsnmp_fsys_size_ull(entry); if (val > MAX_INT_32) long_ret = MAX_INT_32; else long_ret = (long)val; return ((u_char *) (&long_ret)); case DISKTOTALLOW: long_ret = netsnmp_fsys_size_ull(entry) & MAX_UINT_32; return ((u_char *) (&long_ret)); case DISKTOTALHIGH: long_ret = netsnmp_fsys_size_ull(entry) >> 32; return ((u_char *) (&long_ret)); case DISKAVAIL: val = netsnmp_fsys_avail_ull(entry); if (val > MAX_INT_32) long_ret = MAX_INT_32; else long_ret = (long)val; return ((u_char *) (&long_ret)); case DISKAVAILLOW: long_ret = netsnmp_fsys_avail_ull(entry) & MAX_UINT_32; return ((u_char *) (&long_ret)); case DISKAVAILHIGH: long_ret = netsnmp_fsys_avail_ull(entry) >> 32; return ((u_char *) (&long_ret)); case DISKUSED: val = netsnmp_fsys_used_ull(entry); if (val > MAX_INT_32) long_ret = MAX_INT_32; else long_ret = (long)val; return ((u_char *) (&long_ret)); case DISKUSEDLOW: long_ret = netsnmp_fsys_used_ull(entry) & MAX_UINT_32; return ((u_char *) (&long_ret)); case DISKUSEDHIGH: long_ret = netsnmp_fsys_used_ull(entry) >> 32; return ((u_char *) (&long_ret)); case DISKPERCENT: long_ret = _percent( entry->used, entry->size ); return ((u_char *) (&long_ret)); case DISKPERCENTNODE: long_ret = _percent( entry->inums_total - entry->inums_avail, entry->inums_total ); return ((u_char *) (&long_ret)); case ERRORFLAG: long_ret = 0; val = netsnmp_fsys_avail_ull(entry); if (( entry->minspace >= 0 ) && ( val < entry->minspace )) long_ret = 1; else if (( entry->minpercent >= 0 ) && (_percent( entry->avail, entry->size ) < entry->minpercent )) long_ret = 1; return ((u_char *) (&long_ret)); case ERRORMSG: free(errmsg); errmsg = NULL; *var_len = 0; val = netsnmp_fsys_avail_ull(entry); if ((entry->minspace >= 0 && val < entry->minspace && asprintf(&errmsg, "%s: less than %d free (= %d)", entry->path, entry->minspace, (int) val) >= 0) || (entry->minpercent >= 0 && _percent(entry->avail, entry->size) < entry->minpercent && asprintf(&errmsg, "%s: less than %d%% free (= %d%%)", entry->path, entry->minpercent, _percent(entry->avail, entry->size)) >= 0)) { *var_len = strlen(errmsg); } return (u_char *) errmsg; } return NULL; }