Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Client admin shared memory handler
0003  *   Added to libatm so that tpadmsv can use it too.
0004  *
0005  * @file cltshm.c
0006  */
0007 /* -----------------------------------------------------------------------------
0008  * Enduro/X Middleware Platform for Distributed Transaction Processing
0009  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0010  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0011  * This software is released under one of the following licenses:
0012  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0013  * See LICENSE file for full text.
0014  * -----------------------------------------------------------------------------
0015  * AGPL license:
0016  *
0017  * This program is free software; you can redistribute it and/or modify it under
0018  * the terms of the GNU Affero General Public License, version 3 as published
0019  * by the Free Software Foundation;
0020  *
0021  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0022  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0023  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0024  * for more details.
0025  *
0026  * You should have received a copy of the GNU Affero General Public License along 
0027  * with this program; if not, write to the Free Software Foundation, Inc.,
0028  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0029  *
0030  * -----------------------------------------------------------------------------
0031  * A commercial use license is available from Mavimax, Ltd
0032  * contact@mavimax.com
0033  * -----------------------------------------------------------------------------
0034  */
0035 #include <ndrx_config.h>
0036 #include <string.h>
0037 #include <stdio.h>
0038 #include <stdlib.h>
0039 #include <memory.h>
0040 #include <errno.h>
0041 #include <sys/sem.h>
0042 #include <signal.h>
0043 
0044 #include <sys_unix.h>
0045 #include <atmi.h>
0046 #include <atmi_shm.h>
0047 #include <ndrstandard.h>
0048 #include <ndebug.h>
0049 #include <ndrxdcmn.h>
0050 #include <userlog.h>
0051 
0052 /* shm_* stuff, and mmap() */
0053 #include <sys/mman.h>
0054 #include <sys/types.h>
0055 /* exit() etc */
0056 #include <unistd.h>
0057 #include <fcntl.h>
0058 #include <sys/stat.h>
0059 #include <sys/ipc.h>
0060 
0061 #include <nstd_shm.h>
0062 #include <cpm.h>
0063 /*---------------------------Externs------------------------------------*/
0064 /*---------------------------Macros-------------------------------------*/
0065 /*---------------------------Enums--------------------------------------*/
0066 /*---------------------------Typedefs-----------------------------------*/
0067 /*---------------------------Globals------------------------------------*/
0068 /*---------------------------Statics------------------------------------*/
0069 
0070 exprivate ndrx_shm_t M_clt_shm = {.fd=EXFAIL, .path="", .mem=NULL}; /**< Shared mem block, clt */
0071 exprivate ndrx_sem_t M_clt_sem = {.semid=0};        /**< Protect the shared memory */
0072 exprivate int M_attached = EXFALSE;     /**< Are we attached ? */
0073 
0074 /*---------------------------Prototypes---------------------------------*/
0075 
0076 /**
0077  * Attach to shared memory of the Client Processes
0078  * @param attach_only attach only, if does not exists - fail
0079  * @return EXSUCCEED/EXFAIL
0080  */
0081 expublic int ndrx_cltshm_init(int attach_only)
0082 {
0083     int ret = EXSUCCEED;
0084     
0085     if (M_attached)
0086     {
0087         NDRX_LOG(log_warn, "Already attached to CPM/CLT SHM");
0088         goto out;
0089     }
0090     
0091     M_clt_shm.fd = EXFAIL;
0092     M_clt_shm.key = G_atmi_env.ipckey + NDRX_SHM_CPM_KEYOFSZ;
0093 
0094     snprintf(M_clt_shm.path, sizeof(M_clt_shm.path), NDRX_SHM_CPM, G_atmi_env.qprefix);
0095     M_clt_shm.size = sizeof(ndrx_clt_shm_t)*G_atmi_env.max_clts;
0096     
0097     if (attach_only)
0098     {
0099         if (EXSUCCEED!=ndrx_shm_attach(&M_clt_shm))
0100         {
0101             NDRX_LOG(log_error, "Failed to attach ",
0102                         M_clt_shm.path);
0103             EXFAIL_OUT(ret);
0104         }
0105     }
0106     else if (EXSUCCEED!=ndrx_shm_open(&M_clt_shm, EXTRUE))
0107     {
0108         NDRX_LOG(log_error, "Failed to open shm [%s] - System V Queues cannot work",
0109                     M_clt_shm.path);
0110         EXFAIL_OUT(ret);
0111     }
0112     
0113     memset(&M_clt_sem, 0, sizeof(M_clt_sem));
0114     
0115     /* Init the RW semaphores */
0116     M_clt_sem.key = G_atmi_env.ipckey + NDRX_SEM_CPMLOCKS;
0117     M_clt_sem.nrsems = 1;
0118     M_clt_sem.maxreaders = NDRX_CPMSHM_MAX_READERS;
0119     
0120     NDRX_LOG(log_debug, "CPMSHM: Using service semaphore key: %d max readers: %d", 
0121             M_clt_sem.key, M_clt_sem.maxreaders);
0122     
0123     /* OK, either create or attach... */
0124     if (attach_only)
0125     {
0126         if (EXSUCCEED!=ndrx_sem_attach(&M_clt_sem))
0127         {
0128             NDRX_LOG(log_error, "Failed to attach semaphore for CPM "
0129                     "map shared mem");
0130             EXFAIL_OUT(ret);
0131         }
0132     }
0133     else if (EXSUCCEED!=ndrx_sem_open(&M_clt_sem, EXTRUE))
0134     {
0135         NDRX_LOG(log_error, "Failed to open semaphore for CPM "
0136                 "map shared mem");
0137         userlog("Failed to open semaphore for CPM "
0138                 "map shared mem");
0139         EXFAIL_OUT(ret);
0140     }
0141     
0142     M_attached = EXTRUE;
0143  out:
0144     NDRX_LOG(log_debug, "returns %d", ret);
0145     return ret;
0146 }
0147 
0148 /**
0149  * Generate the hash of the cpm key
0150  * @param conf hash config
0151  * @param key_get key data
0152  * @param key_len key len (not used)
0153  * @return slot number within the array
0154  */
0155 exprivate int cltkey_key_hash(ndrx_lh_config_t *conf, void *key_get, size_t key_len)
0156 {
0157     return ndrx_hash_fn(key_get) % conf->elmmax;
0158 }
0159 
0160 /**
0161  * Generate debug for CPM key
0162  * @param conf hash config
0163  * @param key_get key data
0164  * @param key_len not used
0165  * @param dbg_out output debug string
0166  * @param dbg_len debug len
0167  */
0168 exprivate void cltkey_key_debug(ndrx_lh_config_t *conf, void *key_get, 
0169         size_t key_len, char *dbg_out, size_t dbg_len)
0170 {
0171     NDRX_STRCPY_SAFE_DST(dbg_out, key_get, dbg_len);
0172 }
0173 
0174 /**
0175  * CPM shared mem key debug value
0176  * @param conf hash config
0177  * @param idx index number
0178  * @param dbg_out debug buffer
0179  * @param dbg_len debug len
0180  */
0181 exprivate void cltkey_val_debug(ndrx_lh_config_t *conf, int idx, char *dbg_out, 
0182         size_t dbg_len)
0183 {
0184     NDRX_STRCPY_SAFE_DST(dbg_out, NDRX_CPM_INDEX((*conf->memptr), idx)->key, dbg_len);
0185 }
0186 
0187 /**
0188  * Compare the keys
0189  * @param conf hash config
0190  * @param key_get key
0191  * @param key_len key len (not used)
0192  * @param idx index at which to compare
0193  * @return 0 = equals, others not.
0194  */
0195 exprivate int cltkey_compare(ndrx_lh_config_t *conf, void *key_get, 
0196         size_t key_len, int idx)
0197 {
0198     return strcmp(NDRX_CPM_INDEX((*conf->memptr), idx)->key, key_get);
0199 }
0200 
0201 /**
0202  * Get shm pos by key
0203  * @param key client key, format "tag/subsection" ?
0204  * @param oflag O_CREATE if create new rec
0205  * @param[out] pos position found suitable to succeed request
0206  * @param[out] have_value valid value is found? EXTRUE/EXFALSE.
0207  * @return EXTRUE -> found position/ EXFALSE - no position found
0208  */
0209 expublic int ndrx_cltshm_get_key(char *key, int oflag, int *pos, int *have_value)
0210 {
0211     static ndrx_lh_config_t conf;
0212     static int first = EXTRUE;
0213     
0214     if (first)
0215     {
0216         conf.elmmax = G_atmi_env.max_clts;
0217         conf.elmsz = sizeof(ndrx_clt_shm_t);
0218         conf.flags_offset = EXOFFSET(ndrx_clt_shm_t, flags);
0219         conf.memptr = (void **)&(M_clt_shm.mem);
0220         conf.p_key_hash=&cltkey_key_hash;
0221         conf.p_key_debug=&cltkey_key_debug;
0222         conf.p_val_debug=&cltkey_val_debug;
0223         conf.p_compare=&cltkey_compare;
0224         first = EXFALSE;
0225     }
0226     
0227     return ndrx_lh_position_get(&conf, key, 0, oflag, pos, have_value, "cltkey");
0228 
0229 }
0230 
0231 /**
0232  * Disconnect from shared memory block
0233  */
0234 expublic void ndrx_cltshm_detach(void)
0235 {
0236     NDRX_LOG(log_debug, "cltshm detach");
0237     ndrx_shm_close(&M_clt_shm);
0238     ndrx_sem_close(&M_clt_sem);
0239     
0240     M_attached = EXFALSE;
0241 }
0242 
0243 /**
0244  * Remove the shared resources
0245  * @return 
0246  */
0247 expublic int ndrx_cltshm_remove(int force)
0248 {
0249     int ret = EXSUCCEED;
0250     
0251     NDRX_LOG(log_debug, "cltshm remove force: %d", force);
0252     
0253     if (M_clt_shm.fd !=EXFAIL)
0254     {
0255         if (EXSUCCEED!=ndrx_shm_remove(&M_clt_shm))
0256         {
0257             ret = EXFAIL;
0258         }
0259         M_clt_shm.fd = EXFAIL;
0260     }
0261     
0262     if (EXSUCCEED!=ndrx_sem_remove(&M_clt_sem, force))
0263     {
0264         ret = EXFAIL;
0265     }
0266     
0267 out:
0268     return ret;
0269 }
0270 
0271 /**
0272  * Get shared mem obj
0273  * INIT control is up to user.
0274  * @return  shm obj
0275  */
0276 expublic ndrx_shm_t* ndrx_cltshm_mem_get(void)
0277 {
0278     return &M_clt_shm;
0279 }
0280 
0281 /**
0282  * Get semaphore obj.
0283  * INIT control is up to user.
0284  * @return sem obj
0285  */
0286 expublic ndrx_sem_t* ndrx_cltshm_sem_get(void)
0287 {
0288     return &M_clt_sem;
0289 }
0290 
0291 /**
0292  * Find the current pid of the process
0293  * @param [in] key key with <FS> seperator
0294  * @param [out] procname process name hint, opt
0295  * @param [in] procnamesz procname length 
0296  * @param [out] p_stattime time stamp when started, opt
0297  * @return pid or EXFAIL if not found
0298  */
0299 expublic pid_t ndrx_cltshm_getpid(char *key, char *procname, 
0300         size_t procnamesz, time_t *p_stattime)
0301 {
0302     pid_t ret = EXFAIL;
0303     int pos;
0304     int have_value;
0305     /* readlock */
0306     
0307     if (EXSUCCEED!=ndrx_sem_rwlock(&M_clt_sem, 0, NDRX_SEM_TYP_READ))
0308     {
0309         goto out;
0310     }
0311 
0312     if (ndrx_cltshm_get_key(key, 0, &pos, &have_value))
0313     {
0314         if (have_value)
0315         {
0316             ndrx_clt_shm_t *el = NDRX_CPM_INDEX(M_clt_shm.mem, pos);
0317             
0318             /* if value is set, then we got pid */
0319             ret = el->pid;
0320             
0321             if (NULL!=procname)
0322             {
0323                 NDRX_STRCPY_SAFE_DST(procname, el->procname, procnamesz);
0324             }
0325             
0326             if (NULL!=p_stattime)
0327             {
0328                 memcpy(p_stattime, &el->stattime, sizeof(el->stattime));
0329             }
0330             
0331         }
0332     }
0333     
0334     /* unlock */
0335     ndrx_sem_rwunlock(&M_clt_sem, 0, NDRX_SEM_TYP_READ);
0336 out:
0337     return ret;
0338 }
0339 
0340 /**
0341  * Set position.
0342  * This will check the key, if key requests, ISUSED, then new position will
0343  * be tried to allocate.
0344  * @param key CPM process key
0345  * @param pid PID of the process (used only if setting up)
0346  * @param flags see NDRX_CPM_MAP_* flags
0347  * @param procname process name only if adding the cored with ISUSED
0348  * @return EXSUCCEED/EXFAIL (memory full)
0349  */
0350 expublic int ndrx_cltshm_setpos(char *key, pid_t pid, short flags, char *procname)
0351 {
0352     int ret = EXFAIL;
0353     int pos;
0354     int have_value;
0355     int oflag = 0;
0356     ndrx_clt_shm_t* el;
0357     
0358     if (flags & NDRX_CPM_MAP_ISUSED)
0359     {
0360         oflag |=O_CREAT;
0361     }
0362     /* readlock */
0363     
0364     if (EXSUCCEED!=ndrx_sem_rwlock(&M_clt_sem, 0, NDRX_SEM_TYP_WRITE))
0365     {
0366         goto out;
0367     }
0368 
0369     if (ndrx_cltshm_get_key(key, oflag, &pos, &have_value))
0370     {
0371         
0372         /* we got a position */
0373         el = NDRX_CPM_INDEX(M_clt_shm.mem, pos);
0374         if (oflag)
0375         {
0376             NDRX_STRCPY_SAFE(el->key, key);
0377             el->pid = pid;
0378             NDRX_STRCPY_SAFE(el->procname, procname);
0379             el->flags = flags;
0380             time (&(el->stattime));
0381         }
0382         else
0383         {
0384             el->flags = flags;
0385         }
0386         
0387         ret = EXSUCCEED;
0388     }
0389     
0390     /* ndrx_cltshm_get_key(key, oflag, &pos, &have_value); - why? */
0391     
0392     /* unlock */
0393     ndrx_sem_rwunlock(&M_clt_sem, 0, NDRX_SEM_TYP_WRITE);
0394 out:
0395     
0396     if (EXSUCCEED==ret)
0397     {
0398         if (oflag)
0399         {
0400             NDRX_LOG(log_info, "Process installed in CPM SHM: [%s]/%s/%d/%hd",
0401                     key, procname, (int)pid, flags);
0402         }
0403         else
0404         {
0405             NDRX_LOG(log_info, "Process removed from CPM SHM: [%s]/%s/%d/%hd",
0406                     key, NDRX_CPM_INDEX(M_clt_shm.mem, pos)->procname, 
0407                     (int)NDRX_CPM_INDEX(M_clt_shm.mem, pos)->pid, flags);
0408         }
0409     }
0410     
0411     return ret;
0412 }
0413     
0414 /**
0415  * Scan the shared memory with client data, try to kill
0416  * probably the largest part is already dead due to child killings.
0417  * After wards, remove the shared segments
0418  * @param [in] signals list of signals for kill, terminated with EXFAIL
0419  * @param [out] p_was_any if there was any killings set to EXTRUE
0420  */
0421 expublic void ndrx_cltshm_down(int *signals, int *p_was_any)
0422 {
0423     int i, s;
0424     ndrx_clt_shm_t *el;
0425     string_list_t* cltchildren = NULL;
0426     int was_kill = EXFALSE;
0427     char *mem_cpy = NULL;
0428     size_t cpsz;
0429     int err;
0430     
0431     /* but if future api will allow to deregister client, then
0432      * shared memory shall be writable to them..
0433      */
0434     
0435     if (EXSUCCEED==ndrx_cltshm_init(EXTRUE))
0436     {
0437         NDRX_LOG(log_warn, "CLTSHM processing down");
0438         
0439         cpsz = G_atmi_env.max_clts * sizeof(ndrx_clt_shm_t);
0440         mem_cpy = NDRX_MALLOC(cpsz);
0441         
0442         if (NULL==mem_cpy)
0443         {
0444             err = errno;
0445             NDRX_LOG(log_error, "Failed to malloc %d bytes: %s", 
0446                     cpsz, strerror(err));
0447             userlog("Failed to malloc %d bytes: %s", 
0448                     cpsz, strerror(err));
0449             
0450             /* nothing todo; */
0451             goto out;
0452         }
0453         
0454         if (EXSUCCEED!=ndrx_sem_rwlock(&M_clt_sem, 0, NDRX_SEM_TYP_WRITE))
0455         {
0456             goto out;
0457         }
0458         
0459         /* thus we need to copy of memory...! */
0460         memcpy(mem_cpy, M_clt_shm.mem, cpsz);
0461         
0462          /* unlock */
0463         ndrx_sem_rwunlock(&M_clt_sem, 0, NDRX_SEM_TYP_WRITE);
0464         
0465         for (s=0; EXFAIL!=signals[s]; s++)
0466         {
0467             for (i=0; i<G_atmi_env.max_clts; i++)
0468             {
0469                 el = NDRX_CPM_INDEX(mem_cpy, i);
0470 
0471                 if (el->flags & NDRX_CPM_MAP_ISUSED && 
0472                         ndrx_sys_is_process_running_by_pid(el->pid))
0473                 {
0474                     /* grab the childs at first loop*/
0475                     if (0==s)
0476                     {
0477                         ndrx_proc_children_get_recursive(&cltchildren, el->pid);
0478                     }
0479                     
0480                     /* kill the process by it self */
0481                     kill(el->pid, signals[s]);
0482                     was_kill = EXTRUE;
0483                 } /* if used */
0484             } /* for map entry */
0485             
0486             /* not need to wait after final signal */
0487             if (was_kill && EXFAIL!=signals[s+1])
0488             {
0489                 sleep(EX_KILL_SLEEP_SECS);
0490             }
0491             else
0492             {
0493                 break;
0494             }
0495         } /* for signal */
0496         
0497         ndrx_proc_kill_list(cltchildren);
0498         ndrx_string_list_free(cltchildren);
0499 
0500         cltchildren = NULL;
0501         
0502         /* deatch and remove shared mem... */
0503         ndrx_cltshm_detach();
0504         ndrx_cltshm_remove(EXTRUE);
0505     }
0506     
0507 out:
0508     *p_was_any = was_kill;
0509 
0510     if (NULL!=mem_cpy)
0511     {
0512         NDRX_FREE(mem_cpy);
0513     }
0514     return;
0515 }
0516 /* vim: set ts=4 sw=4 et smartindent: */