Icinga-core 1.4.0
next gen monitoring
base/logging.c
Go to the documentation of this file.
00001 /*****************************************************************************
00002  *
00003  * LOGGING.C - Log file functions for use with Icinga
00004  *
00005  * Copyright (c) 1999-2008 Ethan Galstad (egalstad@nagios.org)
00006  * Copyright (c) 2009-2011 Nagios Core Development Team and Community Contributors
00007  * Copyright (c) 2009-2011 Icinga Development Team (http://www.icinga.org)
00008  *
00009  * License:
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 version 2 as
00013  * published by the Free Software Foundation.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00023  *
00024  *****************************************************************************/
00025 
00026 #include "../include/config.h"
00027 #include "../include/common.h"
00028 #include "../include/statusdata.h"
00029 #include "../include/macros.h"
00030 #include "../include/icinga.h"
00031 #include "../include/broker.h"
00032 
00033 
00034 extern char     *log_file;
00035 extern char     *temp_file;
00036 extern char     *log_archive_path;
00037 
00038 extern host     *host_list;
00039 extern service  *service_list;
00040 
00041 extern int      use_daemon_log;
00042 extern int      use_syslog;
00043 extern int      use_syslog_local_facility;
00044 extern int      syslog_local_facility;
00045 extern int      log_service_retries;
00046 extern int      log_initial_states;
00047 extern int      log_current_states;
00048 extern int      log_long_plugin_output;
00049 
00050 extern unsigned long      logging_options;
00051 extern unsigned long      syslog_options;
00052 
00053 extern int      verify_config;
00054 extern int      test_scheduling;
00055 
00056 extern time_t   last_log_rotation;
00057 extern int      log_rotation_method;
00058 
00059 extern int      daemon_mode;
00060 
00061 extern char     *debug_file;
00062 extern int      debug_level;
00063 extern int      debug_verbosity;
00064 extern unsigned long max_debug_file_size;
00065 FILE            *debug_file_fp=NULL;
00066 
00067 int dummy;      /* reduce compiler warnings */
00068 
00069 static pthread_mutex_t debug_fp_lock;
00070 
00071 /*
00072  * since we don't want child processes to hang indefinitely
00073  * in case they inherit a locked lock, we use soft-locking
00074  * here, which basically tries to acquire the lock for a
00075  * short while and then gives up, returning -1 to signal
00076  * the error
00077  */
00078 static inline int soft_lock(pthread_mutex_t *lock){
00079         int i;
00080 
00081         for (i = 0; i < 5; i++) {
00082                 if (!pthread_mutex_trylock(lock)) {
00083                         /* success */
00084                         return 0;
00085                 }
00086 
00087                 if (errno == EDEADLK) {
00088                         /* we already have the lock */
00089                         return 0;
00090                 }
00091 
00092                 /* sleep briefly */
00093                 usleep(30);
00094         }
00095 
00096         return -1; /* we failed to get the lock. Nothing to do */
00097 }
00098 
00099 
00100 /******************************************************************/
00101 /************************ LOGGING FUNCTIONS ***********************/
00102 /******************************************************************/
00103 
00104 /* write something to the console */
00105 static void write_to_console(char *buffer){
00106         /* should we print to the console? */
00107         if(daemon_mode==FALSE)
00108                 printf("%s\n",buffer);
00109 }
00110 
00111 
00112 /* write something to the log file, syslog, and possibly the console */
00113 static void write_to_logs_and_console(char *buffer, unsigned long data_type, int display){
00114         register int len=0;
00115         register int x=0;
00116 
00117         /* strip unnecessary newlines */
00118         len=strlen(buffer);
00119         for(x=len-1;x>=0;x--){
00120                 if(buffer[x]=='\n')
00121                         buffer[x]='\x0';
00122                 else
00123                         break;
00124         }
00125 
00126         /* write messages to the logs */
00127         write_to_all_logs(buffer,data_type);
00128 
00129         /* write message to the console */
00130         if(display==TRUE){
00131 
00132                 /* don't display warnings if we're just testing scheduling */
00133                 if(test_scheduling==TRUE && data_type==NSLOG_VERIFICATION_WARNING)
00134                         return;
00135 
00136                 write_to_console(buffer);
00137         }
00138 }
00139 
00140 
00141 /* The main logging function */
00142 void logit(int data_type, int display, const char *fmt, ...){
00143         va_list ap;
00144         char *buffer=NULL;
00145         va_start(ap,fmt);
00146         if (vasprintf(&buffer, fmt, ap) > 0) {
00147                 write_to_logs_and_console(buffer,data_type,display);
00148                 free(buffer);
00149         }
00150         va_end(ap);
00151 }
00152 
00153 
00154 /* write something to the log file and syslog facility */
00155 int write_to_all_logs(char *buffer, unsigned long data_type){
00156 
00157         /* write to syslog */
00158         write_to_syslog(buffer,data_type);
00159 
00160         /* write to main log */
00161         write_to_log(buffer,data_type,NULL);
00162 
00163         return OK;
00164         }
00165 
00166 
00167 /* write something to the log file and syslog facility */
00168 static void write_to_all_logs_with_timestamp(char *buffer, unsigned long data_type, time_t *timestamp){
00169 
00170         /* write to syslog */
00171         write_to_syslog(buffer,data_type);
00172 
00173         /* write to main log */
00174         write_to_log(buffer,data_type,timestamp);
00175 }
00176 
00177 
00178 /* write something to the icinga log file */
00179 int write_to_log(char *buffer, unsigned long data_type, time_t *timestamp){
00180         FILE *fp=NULL;
00181         time_t log_time=0L;
00182 
00183         if(buffer==NULL)
00184                 return ERROR;
00185 
00186         /* don't log anything if we're not actually running... */
00187         if(verify_config==TRUE || test_scheduling==TRUE)
00188                 return OK;
00189 
00190         /* bail out if we shouldn't write to daemon log */
00191         if(use_daemon_log==FALSE)
00192                 return OK;
00193 
00194         /* make sure we can log this type of entry */
00195         if(!(data_type & logging_options))
00196                 return OK;
00197 
00198         fp=fopen(log_file,"a+");
00199         if(fp==NULL){
00200                 if(daemon_mode==FALSE)
00201                         printf("Warning: Cannot open log file '%s' for writing\n",log_file);
00202                 return ERROR;
00203                 }
00204 
00205         /* what timestamp should we use? */
00206         if(timestamp==NULL)
00207                 time(&log_time);
00208         else
00209                 log_time=*timestamp;
00210 
00211         /* strip any newlines from the end of the buffer */
00212         strip(buffer);
00213 
00214         /* write the buffer to the log file */
00215         fprintf(fp,"[%lu] %s\n",log_time,buffer);
00216 
00217         fclose(fp);
00218 
00219 #ifdef USE_EVENT_BROKER
00220         /* send data to the event broker */
00221         broker_log_data(NEBTYPE_LOG_DATA,NEBFLAG_NONE,NEBATTR_NONE,buffer,data_type,log_time,NULL);
00222 #endif
00223 
00224         return OK;
00225         }
00226 
00227 
00228 /* write something to the syslog facility */
00229 int write_to_syslog(char *buffer, unsigned long data_type){
00230 
00231         if(buffer==NULL)
00232                 return ERROR;
00233 
00234         /* don't log anything if we're not actually running... */
00235         if(verify_config==TRUE || test_scheduling==TRUE)
00236                 return OK;
00237 
00238         /* bail out if we shouldn't write to syslog */
00239         if(use_syslog==FALSE)
00240                 return OK;
00241 
00242         /* make sure we should log this type of entry */
00243         if(!(data_type & syslog_options))
00244                 return OK;
00245 
00246         /* write the buffer to the syslog facility */
00247         if (use_syslog_local_facility) {
00248                 switch (syslog_local_facility) {
00249                         case 0:
00250                                 syslog(LOG_LOCAL0|LOG_INFO,"%s",buffer);
00251                                 return OK;
00252                         case 1:
00253                                 syslog(LOG_LOCAL1|LOG_INFO,"%s",buffer);
00254                                 return OK;
00255                         case 2:
00256                                 syslog(LOG_LOCAL2|LOG_INFO,"%s",buffer);
00257                                 return OK;
00258                         case 3:
00259                                 syslog(LOG_LOCAL3|LOG_INFO,"%s",buffer);
00260                                 return OK;
00261                         case 4:
00262                                 syslog(LOG_LOCAL4|LOG_INFO,"%s",buffer);
00263                                 return OK;
00264                         case 5:
00265                                 syslog(LOG_LOCAL5|LOG_INFO,"%s",buffer);
00266                                 return OK;
00267                         case 6:
00268                                 syslog(LOG_LOCAL6|LOG_INFO,"%s",buffer);
00269                                 return OK;
00270                         case 7:
00271                                 syslog(LOG_LOCAL7|LOG_INFO,"%s",buffer);
00272                                 return OK;
00273                         default:
00274                                 break;
00275                 }
00276         }
00277 
00278         syslog(LOG_USER|LOG_INFO,"%s",buffer);
00279 
00280         return OK;
00281         }
00282 
00283 
00284 /* write a service problem/recovery to the icinga log file */
00285 int log_service_event(service *svc){
00286         char *temp_buffer=NULL;
00287         char *processed_buffer=NULL;
00288         unsigned long log_options=0L;
00289         host *temp_host=NULL;
00290         icinga_macros mac;
00291 
00292         /* don't log soft errors if the user doesn't want to */
00293         if(svc->state_type==SOFT_STATE && !log_service_retries)
00294                 return OK;
00295 
00296         /* get the log options */
00297         if(svc->current_state==STATE_UNKNOWN)
00298                 log_options=NSLOG_SERVICE_UNKNOWN;
00299         else if(svc->current_state==STATE_WARNING)
00300                 log_options=NSLOG_SERVICE_WARNING;
00301         else if(svc->current_state==STATE_CRITICAL)
00302                 log_options=NSLOG_SERVICE_CRITICAL;
00303         else
00304                 log_options=NSLOG_SERVICE_OK;
00305 
00306         /* find the associated host */
00307         if((temp_host=svc->host_ptr)==NULL)
00308                 return ERROR;
00309 
00310         /* grab service macros */
00311         memset(&mac, 0, sizeof(mac));
00312         grab_host_macros_r(&mac, temp_host);
00313         grab_service_macros_r(&mac, svc);
00314 
00315         /* XXX: replace the macro madness with some simple helpers instead */
00316         /* either log only the output, or if enabled, add long_output */
00317         if(log_long_plugin_output==TRUE && svc->long_plugin_output!=NULL) {
00318                 dummy=asprintf(&temp_buffer,"SERVICE ALERT: %s;%s;$SERVICESTATE$;$SERVICESTATETYPE$;$SERVICEATTEMPT$;%s\\n%s\n",svc->host_name,svc->description,(svc->plugin_output==NULL)?"":svc->plugin_output,svc->long_plugin_output);
00319         } else {
00320                 dummy=asprintf(&temp_buffer,"SERVICE ALERT: %s;%s;$SERVICESTATE$;$SERVICESTATETYPE$;$SERVICEATTEMPT$;%s\n",svc->host_name,svc->description,(svc->plugin_output==NULL)?"":svc->plugin_output);
00321         }
00322 
00323         process_macros_r(&mac, temp_buffer,&processed_buffer,0);
00324         clear_host_macros_r(&mac);
00325         clear_service_macros_r(&mac);
00326 
00327         write_to_all_logs(processed_buffer,log_options);
00328 
00329         my_free(temp_buffer);
00330         my_free(processed_buffer);
00331 
00332         return OK;
00333 }
00334 
00335 
00336 /* write a host problem/recovery to the log file */
00337 int log_host_event(host *hst){
00338         char *temp_buffer=NULL;
00339         char *processed_buffer=NULL;
00340         unsigned long log_options=0L;
00341         icinga_macros mac;
00342 
00343         /* grab the host macros */
00344         memset(&mac, 0, sizeof(mac));
00345         grab_host_macros_r(&mac, hst);
00346 
00347         /* get the log options */
00348         if(hst->current_state==HOST_DOWN)
00349                 log_options=NSLOG_HOST_DOWN;
00350         else if(hst->current_state==HOST_UNREACHABLE)
00351                 log_options=NSLOG_HOST_UNREACHABLE;
00352         else
00353                 log_options=NSLOG_HOST_UP;
00354 
00355         /* XXX: replace the macro madness with some simple helpers instead */
00356         /* either log only the output, or if enabled, add long_output */
00357         if(log_long_plugin_output==TRUE && hst->long_plugin_output!=NULL) {
00358                 dummy=asprintf(&temp_buffer,"HOST ALERT: %s;$HOSTSTATE$;$HOSTSTATETYPE$;$HOSTATTEMPT$;%s\\n%s\n",hst->name,(hst->plugin_output==NULL)?"":hst->plugin_output,hst->long_plugin_output);
00359         } else {
00360                 dummy=asprintf(&temp_buffer,"HOST ALERT: %s;$HOSTSTATE$;$HOSTSTATETYPE$;$HOSTATTEMPT$;%s\n",hst->name,(hst->plugin_output==NULL)?"":hst->plugin_output);
00361         }
00362 
00363         process_macros_r(&mac, temp_buffer,&processed_buffer,0);
00364 
00365         write_to_all_logs(processed_buffer,log_options);
00366 
00367         clear_host_macros_r(&mac);
00368         my_free(temp_buffer);
00369         my_free(processed_buffer);
00370 
00371         return OK;
00372 }
00373 
00374 
00375 /* logs host states */
00376 int log_host_states(int type, time_t *timestamp){
00377         char *temp_buffer=NULL;
00378         char *processed_buffer=NULL;
00379         host *temp_host=NULL;
00380         icinga_macros mac;
00381 
00382         /* bail if we shouldn't be logging initial states */
00383         if(type==INITIAL_STATES && log_initial_states==FALSE)
00384                 return OK;
00385 
00386         memset(&mac, 0, sizeof(mac));
00387 
00388         for(temp_host=host_list;temp_host!=NULL;temp_host=temp_host->next){
00389 
00390                 /* grab the host macros */
00391                 grab_host_macros_r(&mac, temp_host);
00392 
00393                 dummy=asprintf(&temp_buffer,"%s HOST STATE: %s;$HOSTSTATE$;$HOSTSTATETYPE$;$HOSTATTEMPT$;%s\n",(type==INITIAL_STATES)?"INITIAL":"CURRENT",temp_host->name,(temp_host->plugin_output==NULL)?"":temp_host->plugin_output);
00394                 process_macros_r(&mac, temp_buffer,&processed_buffer,0);
00395 
00396                 write_to_all_logs_with_timestamp(processed_buffer,NSLOG_INFO_MESSAGE,timestamp);
00397 
00398                 clear_host_macros_r(&mac);
00399 
00400                 my_free(temp_buffer);
00401                 my_free(processed_buffer);
00402                 }
00403 
00404         return OK;
00405 }
00406 
00407 
00408 /* logs service states */
00409 int log_service_states(int type, time_t *timestamp){
00410         char *temp_buffer=NULL;
00411         char *processed_buffer=NULL;
00412         service *temp_service=NULL;
00413         host *temp_host=NULL;
00414         icinga_macros mac;
00415 
00416         /* bail if we shouldn't be logging initial states */
00417         if(type==INITIAL_STATES && log_initial_states==FALSE)
00418                 return OK;
00419 
00420         memset(&mac, 0, sizeof(mac));
00421 
00422         for(temp_service=service_list;temp_service!=NULL;temp_service=temp_service->next){
00423 
00424                 /* find the associated host */
00425                 if((temp_host=temp_service->host_ptr)==NULL)
00426                         continue;
00427 
00428                 /* grab service macros */
00429                 grab_host_macros_r(&mac, temp_host);
00430                 grab_service_macros_r(&mac, temp_service);
00431 
00432                 /* XXX: macro madness */
00433                 dummy=asprintf(&temp_buffer,"%s SERVICE STATE: %s;%s;$SERVICESTATE$;$SERVICESTATETYPE$;$SERVICEATTEMPT$;%s\n",(type==INITIAL_STATES)?"INITIAL":"CURRENT",temp_service->host_name,temp_service->description,temp_service->plugin_output);
00434                 process_macros_r(&mac, temp_buffer,&processed_buffer,0);
00435 
00436                 write_to_all_logs_with_timestamp(processed_buffer,NSLOG_INFO_MESSAGE,timestamp);
00437 
00438                 clear_host_macros_r(&mac);
00439                 clear_service_macros_r(&mac);
00440 
00441                 my_free(temp_buffer);
00442                 my_free(processed_buffer);
00443                 }
00444 
00445         return OK;
00446         }
00447 
00448 
00449 /* rotates the main log file */
00450 int rotate_log_file(time_t rotation_time){
00451         char *temp_buffer=NULL;
00452         char method_string[16]="";
00453         char *log_archive=NULL;
00454         struct tm *t, tm_s;
00455         int rename_result=0;
00456         int stat_result=-1;
00457         struct stat log_file_stat;
00458 
00459         if(log_rotation_method==LOG_ROTATION_NONE){
00460                 return OK;
00461                 }
00462         else if(log_rotation_method==LOG_ROTATION_HOURLY)
00463                 strcpy(method_string,"HOURLY");
00464         else if(log_rotation_method==LOG_ROTATION_DAILY)
00465                 strcpy(method_string,"DAILY");
00466         else if(log_rotation_method==LOG_ROTATION_WEEKLY)
00467                 strcpy(method_string,"WEEKLY");
00468         else if(log_rotation_method==LOG_ROTATION_MONTHLY)
00469                 strcpy(method_string,"MONTHLY");
00470         else
00471                 return ERROR;
00472 
00473         /* update the last log rotation time and status log */
00474         last_log_rotation=time(NULL);
00475         update_program_status(FALSE);
00476 
00477         t = localtime_r(&rotation_time, &tm_s);
00478 
00479         stat_result = stat(log_file, &log_file_stat);
00480 
00481         /* get the archived filename to use */
00482         dummy=asprintf(&log_archive,"%s%sicinga-%02d-%02d-%d-%02d.log", log_archive_path, (log_archive_path[strlen(log_archive_path)-1]=='/')?"":"/", t->tm_mon+1, t->tm_mday, t->tm_year+1900, t->tm_hour);
00483 
00484         /* rotate the log file */
00485         rename_result=my_rename(log_file,log_archive);
00486 
00487         if(rename_result){
00488                 my_free(log_archive);
00489                 return ERROR;
00490                 }
00491 
00492         /* record the log rotation after it has been done... */
00493         dummy=asprintf(&temp_buffer,"LOG ROTATION: %s\n",method_string);
00494         write_to_all_logs_with_timestamp(temp_buffer,NSLOG_PROCESS_INFO,&rotation_time);
00495         my_free(temp_buffer);
00496 
00497         /* record log file version format */
00498         write_log_file_info(&rotation_time);
00499 
00500         if(stat_result==0){
00501                 chmod(log_file, log_file_stat.st_mode);
00502                 dummy=chown(log_file, log_file_stat.st_uid, log_file_stat.st_gid);
00503                 }
00504 
00505         /* log current host and service state if activated*/
00506         if(log_current_states==TRUE) {
00507                 log_host_states(CURRENT_STATES,&rotation_time);
00508                 log_service_states(CURRENT_STATES,&rotation_time);
00509         }
00510 
00511         /* free memory */
00512         my_free(log_archive);
00513 
00514         return OK;
00515         }
00516 
00517 
00518 /* record log file version/info */
00519 int write_log_file_info(time_t *timestamp){
00520         char *temp_buffer=NULL;
00521 
00522         /* write log version */
00523         dummy=asprintf(&temp_buffer,"LOG VERSION: %s\n",LOG_VERSION_2);
00524         write_to_all_logs_with_timestamp(temp_buffer,NSLOG_PROCESS_INFO,timestamp);
00525         my_free(temp_buffer);
00526 
00527         return OK;
00528         }
00529 
00530 
00531 /* opens the debug log for writing */
00532 int open_debug_log(void){
00533 
00534         /* don't do anything if we're not actually running... */
00535         if(verify_config==TRUE || test_scheduling==TRUE)
00536                 return OK;
00537 
00538         /* don't do anything if we're not debugging */
00539         if(debug_level==DEBUGL_NONE)
00540                 return OK;
00541 
00542         if((debug_file_fp=fopen(debug_file,"a+"))==NULL)
00543                 return ERROR;
00544 
00545         return OK;
00546         }
00547 
00548 
00549 /* closes the debug log */
00550 int close_debug_log(void){
00551 
00552         if(debug_file_fp!=NULL)
00553                 fclose(debug_file_fp);
00554 
00555         debug_file_fp=NULL;
00556 
00557         return OK;
00558         }
00559 
00560 
00561 /* write to the debug log */
00562 int log_debug_info(int level, int verbosity, const char *fmt, ...){
00563         va_list ap;
00564         char *temp_path=NULL;
00565         struct timeval current_time;
00566 
00567         if(!(debug_level==DEBUGL_ALL || (level & debug_level)))
00568                 return OK;
00569 
00570         if(verbosity>debug_verbosity)
00571                 return OK;
00572 
00573         if(debug_file_fp==NULL)
00574                 return ERROR;
00575 
00576         /*
00577          * lock it so concurrent threads don't stomp on each other's
00578          * writings. We maintain the lock until we've (optionally)
00579          * renamed the file.
00580          * If soft_lock() fails we return early.
00581          */
00582         if (soft_lock(&debug_fp_lock) < 0)
00583                 return ERROR;
00584 
00585         /* write the timestamp */
00586         gettimeofday(&current_time,NULL);
00587         fprintf(debug_file_fp,"[%lu.%06lu] [%03d.%d] [pid=%lu] ",current_time.tv_sec,current_time.tv_usec,level,verbosity,(unsigned long)getpid());
00588 
00589         /* write the data */
00590         va_start(ap,fmt);
00591         vfprintf(debug_file_fp,fmt,ap);
00592         va_end(ap);
00593 
00594         /* flush, so we don't have problems tailing or when fork()ing */
00595         fflush(debug_file_fp);
00596 
00597         /* if file has grown beyond max, rotate it */
00598         if((unsigned long)ftell(debug_file_fp)>max_debug_file_size && max_debug_file_size>0L){
00599 
00600                 /* close the file */
00601                 close_debug_log();
00602                 
00603                 /* rotate the log file */
00604                 dummy=asprintf(&temp_path,"%s.old",debug_file);
00605                 if(temp_path){
00606 
00607                         /* unlink the old debug file */
00608                         unlink(temp_path);
00609 
00610                         /* rotate the debug file */
00611                         my_rename(debug_file,temp_path);
00612 
00613                         /* free memory */
00614                         my_free(temp_path);
00615                         }
00616 
00617                 /* open a new file */
00618                 open_debug_log();
00619                 }
00620 
00621         pthread_mutex_unlock(&debug_fp_lock);
00622 
00623         return OK;
00624 }
00625 
 All Data Structures Files Functions Variables Typedefs Defines