Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Reload services on change - support library.
0003  *   NOTE: Currently list will not clean up during the runtime.
0004  *   So if config changes tons of times with different bins, then this will grow,
0005  *   TODO: Bind to config loader to remove un-needed binaries.
0006  *
0007  * @file reloadonchange.c
0008  */
0009 /* -----------------------------------------------------------------------------
0010  * Enduro/X Middleware Platform for Distributed Transaction Processing
0011  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0012  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0013  * This software is released under one of the following licenses:
0014  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0015  * See LICENSE file for full text.
0016  * -----------------------------------------------------------------------------
0017  * AGPL license:
0018  *
0019  * This program is free software; you can redistribute it and/or modify it under
0020  * the terms of the GNU Affero General Public License, version 3 as published
0021  * by the Free Software Foundation;
0022  *
0023  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0024  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0025  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0026  * for more details.
0027  *
0028  * You should have received a copy of the GNU Affero General Public License along 
0029  * with this program; if not, write to the Free Software Foundation, Inc.,
0030  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0031  *
0032  * -----------------------------------------------------------------------------
0033  * A commercial use license is available from Mavimax, Ltd
0034  * contact@mavimax.com
0035  * -----------------------------------------------------------------------------
0036  */
0037 #include <string.h>
0038 #include <stdio.h>
0039 #include <stdlib.h>
0040 #include <errno.h>
0041 #include <memory.h>
0042 #include <sys/types.h>
0043 #include <dirent.h>
0044 #include <sys/stat.h>
0045 #include <utlist.h>
0046 #include <fcntl.h>
0047 
0048 #include <ndrstandard.h>
0049 #include <ndrxd.h>
0050 #include <atmi_int.h>
0051 #include <nstopwatch.h>
0052 
0053 #include <ndebug.h>
0054 #include <cmd_processor.h>
0055 #include <signal.h>
0056 
0057 #include "userlog.h"
0058 
0059 /*---------------------------Externs------------------------------------*/
0060 /*---------------------------Macros-------------------------------------*/
0061 /* assume this is max time for reload cmd to hang in incoming ndrxd queue of commands*/
0062 #define ROC_MAX_CYCLES_STAY_IN_RELOAD       5
0063 /*---------------------------Enums--------------------------------------*/
0064 /*---------------------------Typedefs-----------------------------------*/
0065 /**
0066  * Have a track of binaries found in system & their check values.
0067  */
0068 typedef struct roc_exe_registry roc_exe_registry_t;
0069 struct roc_exe_registry
0070 {
0071     char binary_path[PATH_MAX+1];
0072     time_t mtime;
0073     unsigned sanity_cycle; /* sanity cycle */
0074     int reload_issued; /* is reload issued? If so do not issue any more checks  */
0075     
0076     EX_hash_handle hh;         /* makes this structure hashable */
0077 };
0078 
0079 /*---------------------------Globals------------------------------------*/
0080 
0081 exprivate roc_exe_registry_t* M_binreg = NULL;
0082 /*---------------------------Statics------------------------------------*/
0083 /*---------------------------Prototypes---------------------------------*/
0084 
0085 /**
0086  * Calculate checksum for binary if exists.
0087  * @param checksum object
0088  */
0089 exprivate void roc_calc_tstamp(roc_exe_registry_t *bin, unsigned sanity_cycle)
0090 {
0091     struct stat file_stat;
0092     
0093     if (EXSUCCEED==stat(bin->binary_path, &file_stat))
0094     {
0095         bin->mtime = file_stat.st_mtime;
0096         bin->sanity_cycle = sanity_cycle;   
0097     }
0098 }
0099 
0100 /**
0101  * Return the object to binary, if not found then crate one
0102  * @param path full path to binary
0103  * @return NULL (if failure of mem) or ptr to object
0104  */
0105 exprivate roc_exe_registry_t *rco_get_binary(char *binary_path, unsigned sanity_cycle)
0106 {
0107     roc_exe_registry_t *ret = NULL;
0108     
0109     EXHASH_FIND_STR( M_binreg, binary_path, ret);
0110     
0111     if (NULL==ret)
0112     {
0113         /* allocate binary */
0114         roc_exe_registry_t * ret = NDRX_CALLOC(1, sizeof(roc_exe_registry_t));
0115 
0116         if (NULL==ret)
0117         {
0118             userlog("malloc failed: %s", strerror(errno));
0119             goto out;
0120         }
0121         NDRX_STRCPY_SAFE(ret->binary_path, binary_path);
0122         
0123         EXHASH_ADD_STR(M_binreg, binary_path, ret);
0124         
0125         /* calculate initial checksum */
0126         roc_calc_tstamp(ret, sanity_cycle);
0127     }
0128     
0129 out:
0130     return ret;
0131 }
0132 
0133 /**
0134  * Return TRUE if reload is in progress.
0135  * This will try to fix things if issue is hanged for very long time 
0136  * (removed from config?)
0137  * @return 
0138  */
0139 expublic int roc_is_reload_in_progress(unsigned sanity_cycle)
0140 {
0141     roc_exe_registry_t *el = NULL;
0142     roc_exe_registry_t *elt = NULL;
0143     long diff;
0144     
0145     /* go over the hash and return TRUE, if in progress... */
0146     
0147     EXHASH_ITER(hh, M_binreg, el, elt)
0148     {
0149         if (el->reload_issued)
0150         {
0151             /* assume 5 cycles is max */
0152             if ((diff=labs((long)el->sanity_cycle-(long)sanity_cycle)) > ROC_MAX_CYCLES_STAY_IN_RELOAD)
0153             {
0154                 NDRX_LOG(log_error, "Current cycle %u reload cycle %u - assume executed",
0155                         el->sanity_cycle, sanity_cycle);
0156                 break;  
0157             } 
0158             else
0159             {
0160                 NDRX_LOG(log_error, "%s - enqueued for reload", el->binary_path);
0161                 return EXTRUE;
0162             }
0163         }
0164     }
0165     
0166     return EXFALSE; 
0167 }
0168 
0169 /**
0170  * Check binary 
0171  * @param path
0172  * @param sanity_cycle
0173  * @return TRUE if needs to issue reload, FALSE if checksums not changed
0174  */
0175 expublic int roc_check_binary(char *binary_path, unsigned sanity_cycle)
0176 {
0177     int ret= EXFALSE;
0178     roc_exe_registry_t *bin = NULL;
0179     time_t old_mtime;
0180     
0181     /* check the table if reload needed then no calculation needed */
0182     if (roc_is_reload_in_progress(sanity_cycle))
0183     {
0184         ret=EXFALSE;
0185         goto out;
0186     }
0187     
0188     /* search for bin, if not exists, then get checksum & add */
0189     if (NULL==(bin=rco_get_binary(binary_path, sanity_cycle)))
0190     {
0191         NDRX_LOG(log_error, "Failed to get reload-on-change binary "
0192             "(%s) - memory issues", binary_path);
0193         ret=EXFALSE;
0194         goto out;
0195     }
0196     
0197     /* if exists, and cycle equals to current one, then no cksum */
0198     if (bin->sanity_cycle==sanity_cycle)
0199     {
0200         /* no cksum changed... as already calculated. */
0201         NDRX_LOG(log_debug, "Already checked at this cycle... (cached)");
0202         ret=EXFALSE;
0203         goto out;
0204     }
0205     
0206     /* if exists and cycle not equals, calc the checksum  */
0207     old_mtime = bin->mtime;
0208     
0209     roc_calc_tstamp(bin, sanity_cycle);
0210     
0211     if (old_mtime!=bin->mtime)
0212     {
0213         NDRX_LOG(log_warn, "Binary [%s] timestamp changed", binary_path);
0214         bin->reload_issued = EXTRUE; /* so will issue reload */
0215         
0216         ret = EXTRUE;
0217         goto out;
0218     }
0219     
0220 out:
0221 
0222     NDRX_LOG(log_debug, "roc_check_binary [%s]: %s", binary_path, 
0223         ret?"issued reload":"reload not issued");
0224 
0225     return ret;
0226 }
0227 
0228 /**
0229  * Mark binary as reloaded.
0230  * @param path
0231  * @return 
0232  */
0233 expublic void roc_mark_as_reloaded(char *binary_path, unsigned sanity_cycle)
0234 {
0235     roc_exe_registry_t * bin = rco_get_binary(binary_path, sanity_cycle);
0236     
0237     if (NULL==bin)
0238     {
0239         NDRX_LOG(log_error, "Binary [%s] not found in hash - mem error!",
0240                 binary_path);
0241     }
0242     else
0243     {
0244         NDRX_LOG(log_error, "Marking binary [%s]/%d as reloaded!",
0245                 binary_path, bin->reload_issued);
0246                 
0247         bin->reload_issued = EXFALSE;
0248     }
0249 }
0250 /* vim: set ts=4 sw=4 et smartindent: */