Back to index

nagios-plugins  1.4.16
utils_base.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002 *
00003 * utils_base.c
00004 *
00005 * License: GPL
00006 * Copyright (c) 2006 Nagios Plugins Development Team
00007 *
00008 * Library of useful functions for plugins
00009 * 
00010 *
00011 * This program is free software: you can redistribute it and/or modify
00012 * it under the terms of the GNU General Public License as published by
00013 * the Free Software Foundation, either version 3 of the License, or
00014 * (at your option) any later version.
00015 * 
00016 * This program is distributed in the hope that it will be useful,
00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 * GNU General Public License for more details.
00020 * 
00021 * You should have received a copy of the GNU General Public License
00022 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00023 * 
00024 *
00025 *****************************************************************************/
00026 
00027 #include "common.h"
00028 #include <stdarg.h>
00029 #include "utils_base.h"
00030 #include <fcntl.h>
00031 
00032 #define np_free(ptr) { if(ptr) { free(ptr); ptr = NULL; } }
00033 
00034 nagios_plugin *this_nagios_plugin=NULL;
00035 
00036 void np_init( char *plugin_name, int argc, char **argv ) {
00037        if (this_nagios_plugin==NULL) {
00038               this_nagios_plugin = malloc(sizeof(nagios_plugin));
00039               if (this_nagios_plugin==NULL) {
00040                      die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00041                          strerror(errno));
00042               }
00043               this_nagios_plugin->plugin_name = strdup(plugin_name);
00044               if (this_nagios_plugin->plugin_name==NULL)
00045                      die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
00046               this_nagios_plugin->argc = argc;
00047               this_nagios_plugin->argv = argv;
00048        }
00049 }
00050 
00051 void np_set_args( int argc, char **argv ) {
00052        if (this_nagios_plugin==NULL)
00053               die(STATE_UNKNOWN, _("This requires np_init to be called"));
00054 
00055        this_nagios_plugin->argc = argc;
00056        this_nagios_plugin->argv = argv;
00057 }
00058 
00059 
00060 void np_cleanup() {
00061        if (this_nagios_plugin!=NULL) {
00062               if(this_nagios_plugin->state!=NULL) {
00063                      if(this_nagios_plugin->state->state_data) { 
00064                             np_free(this_nagios_plugin->state->state_data->data);
00065                             np_free(this_nagios_plugin->state->state_data);
00066                      }
00067                      np_free(this_nagios_plugin->state->name);
00068                      np_free(this_nagios_plugin->state);
00069               }
00070               np_free(this_nagios_plugin->plugin_name);
00071               np_free(this_nagios_plugin);
00072        }
00073        this_nagios_plugin=NULL;
00074 }
00075 
00076 /* Hidden function to get a pointer to this_nagios_plugin for testing */
00077 void _get_nagios_plugin( nagios_plugin **pointer ){
00078        *pointer = this_nagios_plugin;
00079 }
00080 
00081 void
00082 die (int result, const char *fmt, ...)
00083 {
00084        va_list ap;
00085        va_start (ap, fmt);
00086        vprintf (fmt, ap);
00087        va_end (ap);
00088        if(this_nagios_plugin!=NULL) {
00089               np_cleanup();
00090        }
00091        exit (result);
00092 }
00093 
00094 void set_range_start (range *this, double value) {
00095        this->start = value;
00096        this->start_infinity = FALSE;
00097 }
00098 
00099 void set_range_end (range *this, double value) {
00100        this->end = value;
00101        this->end_infinity = FALSE;
00102 }
00103 
00104 range
00105 *parse_range_string (char *str) {
00106        range *temp_range;
00107        double start;
00108        double end;
00109        char *end_str;
00110 
00111        temp_range = (range *) malloc(sizeof(range));
00112 
00113        /* Set defaults */
00114        temp_range->start = 0;
00115        temp_range->start_infinity = FALSE;
00116        temp_range->end = 0;
00117        temp_range->end_infinity = TRUE;
00118        temp_range->alert_on = OUTSIDE;
00119 
00120        if (str[0] == '@') {
00121               temp_range->alert_on = INSIDE;
00122               str++;
00123        }
00124 
00125        end_str = index(str, ':');
00126        if (end_str != NULL) {
00127               if (str[0] == '~') {
00128                      temp_range->start_infinity = TRUE;
00129               } else {
00130                      start = strtod(str, NULL);  /* Will stop at the ':' */
00131                      set_range_start(temp_range, start);
00132               }
00133               end_str++;           /* Move past the ':' */
00134        } else {
00135               end_str = str;
00136        }
00137        end = strtod(end_str, NULL);
00138        if (strcmp(end_str, "") != 0) {
00139               set_range_end(temp_range, end);
00140        }
00141 
00142        if (temp_range->start_infinity == TRUE ||
00143               temp_range->end_infinity == TRUE ||
00144               temp_range->start <= temp_range->end) {
00145               return temp_range;
00146        }
00147        free(temp_range);
00148        return NULL;
00149 }
00150 
00151 /* returns 0 if okay, otherwise 1 */
00152 int
00153 _set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
00154 {
00155        thresholds *temp_thresholds = NULL;
00156 
00157        if ((temp_thresholds = malloc(sizeof(thresholds))) == NULL)
00158               die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00159                   strerror(errno));
00160 
00161        temp_thresholds->warning = NULL;
00162        temp_thresholds->critical = NULL;
00163 
00164        if (warn_string != NULL) {
00165               if ((temp_thresholds->warning = parse_range_string(warn_string)) == NULL) {
00166                      return NP_RANGE_UNPARSEABLE;
00167               }
00168        }
00169        if (critical_string != NULL) {
00170               if ((temp_thresholds->critical = parse_range_string(critical_string)) == NULL) {
00171                      return NP_RANGE_UNPARSEABLE;
00172               }
00173        }
00174 
00175        *my_thresholds = temp_thresholds;
00176 
00177        return 0;
00178 }
00179 
00180 void
00181 set_thresholds(thresholds **my_thresholds, char *warn_string, char *critical_string)
00182 {
00183        switch (_set_thresholds(my_thresholds, warn_string, critical_string)) {
00184        case 0:
00185               return;
00186        case NP_RANGE_UNPARSEABLE:
00187               die(STATE_UNKNOWN, _("Range format incorrect"));
00188        case NP_WARN_WITHIN_CRIT:
00189               die(STATE_UNKNOWN, _("Warning level is a subset of critical and will not be alerted"));
00190               break;
00191        }
00192 }
00193 
00194 void print_thresholds(const char *threshold_name, thresholds *my_threshold) {
00195        printf("%s - ", threshold_name);
00196        if (! my_threshold) {
00197               printf("Threshold not set");
00198        } else {
00199               if (my_threshold->warning) {
00200                      printf("Warning: start=%g end=%g; ", my_threshold->warning->start, my_threshold->warning->end);
00201               } else {
00202                      printf("Warning not set; ");
00203               }
00204               if (my_threshold->critical) {
00205                      printf("Critical: start=%g end=%g", my_threshold->critical->start, my_threshold->critical->end);
00206               } else {
00207                      printf("Critical not set");
00208               }
00209        }
00210        printf("\n");
00211 }
00212 
00213 /* Returns TRUE if alert should be raised based on the range */
00214 int
00215 check_range(double value, range *my_range)
00216 {
00217        int no = FALSE;
00218        int yes = TRUE;
00219 
00220        if (my_range->alert_on == INSIDE) {
00221               no = TRUE;
00222               yes = FALSE;
00223        }
00224 
00225        if (my_range->end_infinity == FALSE && my_range->start_infinity == FALSE) {
00226               if ((my_range->start <= value) && (value <= my_range->end)) {
00227                      return no;
00228               } else {
00229                      return yes;
00230               }
00231        } else if (my_range->start_infinity == FALSE && my_range->end_infinity == TRUE) {
00232               if (my_range->start <= value) {
00233                      return no;
00234               } else {
00235                      return yes;
00236               }
00237        } else if (my_range->start_infinity == TRUE && my_range->end_infinity == FALSE) {
00238               if (value <= my_range->end) {
00239                      return no;
00240               } else {
00241                      return yes;
00242               }
00243        } else {
00244               return no;
00245        }
00246 }
00247 
00248 /* Returns status */
00249 int
00250 get_status(double value, thresholds *my_thresholds)
00251 {
00252        if (my_thresholds->critical != NULL) {
00253               if (check_range(value, my_thresholds->critical) == TRUE) {
00254                      return STATE_CRITICAL;
00255               }
00256        }
00257        if (my_thresholds->warning != NULL) {
00258               if (check_range(value, my_thresholds->warning) == TRUE) {
00259                      return STATE_WARNING;
00260               }
00261        }
00262        return STATE_OK;
00263 }
00264 
00265 char *np_escaped_string (const char *string) {
00266        char *data;
00267        int i, j=0;
00268        data = strdup(string);
00269        for (i=0; data[i]; i++) {
00270               if (data[i] == '\\') {
00271                      switch(data[++i]) {
00272                             case 'n':
00273                                    data[j++] = '\n';
00274                                    break;
00275                             case 'r':
00276                                    data[j++] = '\r';
00277                                    break;
00278                             case 't':
00279                                    data[j++] = '\t';
00280                                    break;
00281                             case '\\':
00282                                    data[j++] = '\\';
00283                                    break;
00284                             default:
00285                                    data[j++] = data[i];
00286                      }
00287               } else {
00288                      data[j++] = data[i];
00289               }
00290        }
00291        data[j] = '\0';
00292        return data;
00293 }
00294 
00295 int np_check_if_root(void) { return (geteuid() == 0); }
00296 
00297 int np_warn_if_not_root(void) {
00298        int status = np_check_if_root();
00299        if(!status) {
00300               printf(_("Warning: "));
00301               printf(_("This plugin must be either run as root or setuid root.\n"));
00302               printf(_("To run as root, you can use a tool like sudo.\n"));
00303               printf(_("To set the setuid permissions, use the command:\n"));
00304               /* XXX could we use something like progname? */
00305               printf("\tchmod u+s yourpluginfile\n");
00306        }
00307        return status;
00308 }
00309 
00310 /*
00311  * Extract the value from key/value pairs, or return NULL. The value returned
00312  * can be free()ed.
00313  * This function can be used to parse NTP control packet data and performance
00314  * data strings.
00315  */
00316 char *np_extract_value(const char *varlist, const char *name, char sep) {
00317        char *tmp=NULL, *value=NULL;
00318        int i;
00319 
00320        while (1) {
00321               /* Strip any leading space */
00322               for (varlist; isspace(varlist[0]); varlist++);
00323 
00324               if (strncmp(name, varlist, strlen(name)) == 0) {
00325                      varlist += strlen(name);
00326                      /* strip trailing spaces */
00327                      for (varlist; isspace(varlist[0]); varlist++);
00328 
00329                      if (varlist[0] == '=') {
00330                             /* We matched the key, go past the = sign */
00331                             varlist++;
00332                             /* strip leading spaces */
00333                             for (varlist; isspace(varlist[0]); varlist++);
00334 
00335                             if (tmp = index(varlist, sep)) {
00336                                    /* Value is delimited by a comma */
00337                                    if (tmp-varlist == 0) continue;
00338                                    value = (char *)malloc(tmp-varlist+1);
00339                                    strncpy(value, varlist, tmp-varlist);
00340                                    value[tmp-varlist] = '\0';
00341                             } else {
00342                                    /* Value is delimited by a \0 */
00343                                    if (strlen(varlist) == 0) continue;
00344                                    value = (char *)malloc(strlen(varlist) + 1);
00345                                    strncpy(value, varlist, strlen(varlist));
00346                                    value[strlen(varlist)] = '\0';
00347                             }
00348                             break;
00349                      }
00350               }
00351               if (tmp = index(varlist, sep)) {
00352                      /* More keys, keep going... */
00353                      varlist = tmp + 1;
00354               } else {
00355                      /* We're done */
00356                      break;
00357               }
00358        }
00359 
00360        /* Clean-up trailing spaces/newlines */
00361        if (value) for (i=strlen(value)-1; isspace(value[i]); i--) value[i] = '\0';
00362 
00363        return value;
00364 }
00365 
00366 /*
00367  * Returns a string to use as a keyname, based on an md5 hash of argv, thus
00368  * hopefully a unique key per service/plugin invocation. Use the extra-opts
00369  * parse of argv, so that uniqueness in parameters are reflected there.
00370  */
00371 char *_np_state_generate_key() {
00372        struct sha1_ctx ctx;
00373        int i;
00374        char **argv = this_nagios_plugin->argv;
00375        unsigned char result[20];
00376        char keyname[41];
00377        char *p=NULL;
00378 
00379        sha1_init_ctx(&ctx);
00380        
00381        for(i=0; i<this_nagios_plugin->argc; i++) {
00382               sha1_process_bytes(argv[i], strlen(argv[i]), &ctx);
00383        }
00384 
00385        sha1_finish_ctx(&ctx, &result);
00386        
00387        for (i=0; i<20; ++i) {
00388               sprintf(&keyname[2*i], "%02x", result[i]);
00389        }
00390        keyname[40]='\0';
00391        
00392        p = strdup(keyname);
00393        if(p==NULL) {
00394               die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
00395        }
00396        return p;
00397 }
00398 
00399 void _cleanup_state_data() {
00400        if (this_nagios_plugin->state->state_data!=NULL) {
00401               np_free(this_nagios_plugin->state->state_data->data);
00402               np_free(this_nagios_plugin->state->state_data);
00403        }
00404 }
00405 
00406 /*
00407  * Internal function. Returns either:
00408  *   envvar NAGIOS_PLUGIN_STATE_DIRECTORY
00409  *   statically compiled shared state directory
00410  */
00411 char* _np_state_calculate_location_prefix(){
00412        char *env_dir;
00413 
00414        env_dir = getenv("NAGIOS_PLUGIN_STATE_DIRECTORY");
00415        if(env_dir && env_dir[0] != '\0')
00416               return env_dir;
00417        return NP_STATE_DIR_PREFIX;
00418 }
00419 
00420 /*
00421  * Initiatializer for state routines.
00422  * Sets variables. Generates filename. Returns np_state_key. die with
00423  * UNKNOWN if exception
00424  */
00425 void np_enable_state(char *keyname, int expected_data_version) {
00426        state_key *this_state = NULL;
00427        char *temp_filename = NULL;
00428        char *temp_keyname = NULL;
00429        char *p=NULL;
00430 
00431        if(this_nagios_plugin==NULL)
00432               die(STATE_UNKNOWN, _("This requires np_init to be called"));
00433 
00434        this_state = (state_key *) malloc(sizeof(state_key));
00435        if(this_state==NULL)
00436               die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00437                   strerror(errno));
00438 
00439        if(keyname==NULL) {
00440               temp_keyname = _np_state_generate_key();
00441        } else {
00442               temp_keyname = strdup(keyname);
00443               if(temp_keyname==NULL)
00444                      die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
00445        }
00446        /* Die if invalid characters used for keyname */
00447        p = temp_keyname;
00448        while(*p!='\0') {
00449               if(! (isalnum(*p) || *p == '_')) {
00450                      die(STATE_UNKNOWN, _("Invalid character for keyname - only alphanumerics or '_'"));
00451               }
00452               p++;
00453        }
00454        this_state->name=temp_keyname;
00455        this_state->plugin_name=this_nagios_plugin->plugin_name;
00456        this_state->data_version=expected_data_version;
00457        this_state->state_data=NULL;
00458 
00459        /* Calculate filename */
00460        asprintf(&temp_filename, "%s/%s/%s", _np_state_calculate_location_prefix(), this_nagios_plugin->plugin_name, this_state->name);
00461        this_state->_filename=temp_filename;
00462 
00463        this_nagios_plugin->state = this_state;
00464 }
00465 
00466 /*
00467  * Will return NULL if no data is available (first run). If key currently
00468  * exists, read data. If state file format version is not expected, return
00469  * as if no data. Get state data version number and compares to expected.
00470  * If numerically lower, then return as no previous state. die with UNKNOWN
00471  * if exceptional error.
00472  */
00473 state_data *np_state_read() {
00474        state_data *this_state_data=NULL;
00475        FILE *statefile;
00476        int rc = FALSE;
00477 
00478        if(this_nagios_plugin==NULL)
00479               die(STATE_UNKNOWN, _("This requires np_init to be called"));
00480 
00481        /* Open file. If this fails, no previous state found */
00482        statefile = fopen( this_nagios_plugin->state->_filename, "r" );
00483        if(statefile!=NULL) {
00484 
00485               this_state_data = (state_data *) malloc(sizeof(state_data));
00486               if(this_state_data==NULL)
00487                      die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00488                          strerror(errno));
00489 
00490               this_state_data->data=NULL;
00491               this_nagios_plugin->state->state_data = this_state_data;
00492 
00493               rc = _np_state_read_file(statefile);
00494 
00495               fclose(statefile);
00496        }
00497 
00498        if(rc==FALSE) {
00499               _cleanup_state_data();
00500        }
00501 
00502        return this_nagios_plugin->state->state_data;
00503 }
00504 
00505 /* 
00506  * Read the state file
00507  */
00508 int _np_state_read_file(FILE *f) {
00509        int status=FALSE;
00510        size_t pos;
00511        char *line;
00512        int i;
00513        int failure=0;
00514        time_t current_time, data_time;
00515        enum { STATE_FILE_VERSION, STATE_DATA_VERSION, STATE_DATA_TIME, STATE_DATA_TEXT, STATE_DATA_END } expected=STATE_FILE_VERSION;
00516 
00517        time(&current_time);
00518 
00519        /* Note: This introduces a limit of 1024 bytes in the string data */
00520        line = (char *) malloc(1024);
00521        if(line==NULL)
00522               die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00523                   strerror(errno));
00524 
00525        while(!failure && (fgets(line,1024,f))!=NULL){
00526               pos=strlen(line);
00527               if(line[pos-1]=='\n') {
00528                      line[pos-1]='\0';
00529               }
00530 
00531               if(line[0] == '#') continue;
00532 
00533               switch(expected) {
00534                      case STATE_FILE_VERSION:
00535                             i=atoi(line);
00536                             if(i!=NP_STATE_FORMAT_VERSION)
00537                                    failure++;
00538                             else
00539                                    expected=STATE_DATA_VERSION;
00540                             break;
00541                      case STATE_DATA_VERSION:
00542                             i=atoi(line);
00543                             if(i != this_nagios_plugin->state->data_version)
00544                                    failure++;
00545                             else
00546                                    expected=STATE_DATA_TIME;
00547                             break;
00548                      case STATE_DATA_TIME:
00549                             /* If time > now, error */
00550                             data_time=strtoul(line,NULL,10);
00551                             if(data_time > current_time)
00552                                    failure++;
00553                             else {
00554                                    this_nagios_plugin->state->state_data->time = data_time;
00555                                    expected=STATE_DATA_TEXT;
00556                             }
00557                             break;
00558                      case STATE_DATA_TEXT:
00559                             this_nagios_plugin->state->state_data->data = strdup(line);
00560                             if(this_nagios_plugin->state->state_data->data==NULL)
00561                                    die(STATE_UNKNOWN, _("Cannot execute strdup: %s"), strerror(errno));
00562                             expected=STATE_DATA_END;
00563                             status=TRUE;
00564                             break;
00565                      case STATE_DATA_END:
00566                             ;
00567               }
00568        }
00569 
00570        np_free(line);
00571        return status;
00572 }
00573 
00574 /*
00575  * If time=NULL, use current time. Create state file, with state format 
00576  * version, default text. Writes version, time, and data. Avoid locking 
00577  * problems - use mv to write and then swap. Possible loss of state data if 
00578  * two things writing to same key at same time. 
00579  * Will die with UNKNOWN if errors
00580  */
00581 void np_state_write_string(time_t data_time, char *data_string) {
00582        FILE *fp;
00583        char *temp_file=NULL;
00584        int fd=0, result=0;
00585        time_t current_time;
00586        char *directories=NULL;
00587        char *p=NULL;
00588 
00589        if(data_time==0)
00590               time(&current_time);
00591        else
00592               current_time=data_time;
00593        
00594        /* If file doesn't currently exist, create directories */
00595        if(access(this_nagios_plugin->state->_filename,F_OK)!=0) {
00596               asprintf(&directories, "%s", this_nagios_plugin->state->_filename);
00597               if(directories==NULL)
00598                      die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00599                          strerror(errno));
00600 
00601               for(p=directories+1; *p; p++) {
00602                      if(*p=='/') {
00603                             *p='\0';
00604                             if((access(directories,F_OK)!=0) && (mkdir(directories, S_IRWXU)!=0)) {
00605                                    /* Can't free this! Otherwise error message is wrong! */
00606                                    /* np_free(directories); */ 
00607                                    die(STATE_UNKNOWN, _("Cannot create directory: %s"), directories);
00608                             }
00609                             *p='/';
00610                      }
00611               }
00612               np_free(directories);
00613        }
00614 
00615        asprintf(&temp_file,"%s.XXXXXX",this_nagios_plugin->state->_filename);
00616        if(temp_file==NULL)
00617               die(STATE_UNKNOWN, _("Cannot allocate memory: %s"),
00618                   strerror(errno));
00619 
00620        if((fd=mkstemp(temp_file))==-1) {
00621               np_free(temp_file);
00622               die(STATE_UNKNOWN, _("Cannot create temporary filename"));
00623        }
00624 
00625        fp=(FILE *)fdopen(fd,"w");
00626        if(fp==NULL) {
00627               close(fd);
00628               unlink(temp_file);
00629               np_free(temp_file);
00630               die(STATE_UNKNOWN, _("Unable to open temporary state file"));
00631        }
00632        
00633        fprintf(fp,"# NP State file\n");
00634        fprintf(fp,"%d\n",NP_STATE_FORMAT_VERSION);
00635        fprintf(fp,"%d\n",this_nagios_plugin->state->data_version);
00636        fprintf(fp,"%lu\n",current_time);
00637        fprintf(fp,"%s\n",data_string);
00638        
00639        fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP);
00640        
00641        fflush(fp);
00642 
00643        result=fclose(fp);
00644 
00645        fsync(fd);
00646 
00647        if(result!=0) {
00648               unlink(temp_file);
00649               np_free(temp_file);
00650               die(STATE_UNKNOWN, _("Error writing temp file"));
00651        }
00652 
00653        if(rename(temp_file, this_nagios_plugin->state->_filename)!=0) {
00654               unlink(temp_file);
00655               np_free(temp_file);
00656               die(STATE_UNKNOWN, _("Cannot rename state temp file"));
00657        }
00658 
00659        np_free(temp_file);
00660 }
00661