Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Environment API (groups & lists) for XATMI servers and clients (by cpmsrv)
0003  *
0004  * @file envapi.c
0005  */
0006 /* -----------------------------------------------------------------------------
0007  * Enduro/X Middleware Platform for Distributed Transaction Processing
0008  * Copyright (C) 2009-2016, ATR Baltic, Ltd. All Rights Reserved.
0009  * Copyright (C) 2017-2023, Mavimax, Ltd. All Rights Reserved.
0010  * This software is released under one of the following licenses:
0011  * AGPL (with Java and Go exceptions) or Mavimax's license for commercial use.
0012  * See LICENSE file for full text.
0013  * -----------------------------------------------------------------------------
0014  * AGPL license:
0015  *
0016  * This program is free software; you can redistribute it and/or modify it under
0017  * the terms of the GNU Affero General Public License, version 3 as published
0018  * by the Free Software Foundation;
0019  *
0020  * This program is distributed in the hope that it will be useful, but WITHOUT ANY
0021  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
0022  * PARTICULAR PURPOSE. See the GNU Affero General Public License, version 3
0023  * for more details.
0024  *
0025  * You should have received a copy of the GNU Affero General Public License along 
0026  * with this program; if not, write to the Free Software Foundation, Inc.,
0027  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0028  *
0029  * -----------------------------------------------------------------------------
0030  * A commercial use license is available from Mavimax, Ltd
0031  * contact@mavimax.com
0032  * -----------------------------------------------------------------------------
0033  */
0034 #include <string.h>
0035 #include <stdio.h>
0036 #include <stdlib.h>
0037 #include <libgen.h>
0038 #include <memory.h>
0039 #include <libxml/xmlreader.h>
0040 #include <errno.h>
0041 
0042 #include <ndrstandard.h>
0043 #include <ndebug.h>
0044 #include <utlist.h>
0045 #include <nstdutil.h>
0046 #include <exenv.h>
0047 /*---------------------------Externs------------------------------------*/
0048 /*---------------------------Macros-------------------------------------*/
0049 /*---------------------------Enums--------------------------------------*/
0050 /*---------------------------Typedefs-----------------------------------*/
0051 /*---------------------------Globals------------------------------------*/
0052 /*---------------------------Statics------------------------------------*/
0053 /*---------------------------Prototypes---------------------------------*/
0054 
0055 exprivate ndrx_env_group_t * ndrx_endrxconf_envs_group_get(ndrx_env_group_t *grouphash, 
0056         char *group);
0057 
0058 exprivate void env_free(ndrx_env_list_t *env);
0059 
0060 
0061 /**
0062  * Allocate and copy list entry
0063  * @param el element to duplicate
0064  * @return NULL (in case of errro) or ptr to copy
0065  */
0066 exprivate ndrx_env_list_t * copy_list_entry(ndrx_env_list_t *el)
0067 {
0068     int ret = EXSUCCEED;
0069     ndrx_env_list_t * cpy = NULL;
0070     
0071     NDRX_MALLOC_OUT(cpy, sizeof(ndrx_env_list_t), ndrx_env_list_t);    
0072     NDRX_STRDUP_OUT(cpy->key, el->key);
0073     
0074     if (NULL!=el->value)
0075     {
0076         /* in case of unset this is NULL */
0077         NDRX_STRDUP_OUT(cpy->value, el->value);
0078     }
0079     cpy->flags = el->flags;
0080     
0081 out:
0082     
0083     if (EXSUCCEED!=ret)
0084     {
0085         env_free(cpy);
0086         return NULL;
0087     }
0088 
0089     return cpy;
0090 }
0091 
0092 /**
0093  * Append to dest list all from source list
0094  * @param dest destination env list
0095  * @param source source env list
0096  * @return EXUSCCEED/EXFAIL
0097  */
0098 expublic int ndrx_ndrxconf_envs_append(ndrx_env_list_t **dest, ndrx_env_list_t *source)
0099 {
0100     int ret = EXSUCCEED;
0101     ndrx_env_list_t *el, *cpy;
0102     
0103     
0104     DL_FOREACH(source, el)
0105     {
0106         cpy = copy_list_entry(el);
0107         
0108         if (NULL==cpy)
0109         {
0110             NDRX_LOG(log_error, "Failed to cpy env entry");
0111             EXFAIL_OUT(ret);
0112         }    
0113         
0114         DL_APPEND(*dest, cpy);
0115     }
0116     
0117 out:
0118     return ret;
0119 }
0120 
0121 /**
0122  * Parse environments. This could be done for group closure or process closure
0123  * @param doc xml doc
0124  * @param cur xml cursor pointing on envs block 
0125  * @param envs where to store environments
0126  * @param grouphash list of groups currently known in system
0127  * @param grouplist if provided then any groups by <usegroup> tag will be populated
0128  *  to this list, otherwise group will be merged into current list.
0129  * @return 
0130  */
0131 expublic int ndrx_ndrxconf_envs_parse(xmlDocPtr doc, xmlNodePtr cur, 
0132         ndrx_env_list_t **envs, ndrx_env_group_t *grouphash, 
0133         ndrx_env_grouplist_t **grouplist)
0134 {
0135     int ret = EXSUCCEED;
0136     xmlAttrPtr attr;
0137     char *p;
0138     ndrx_env_list_t *env = NULL;
0139     ndrx_env_grouplist_t *glist = NULL;
0140     
0141     /* loop over the current element and parse tags accordingly */
0142     cur=cur->children;
0143     
0144     for (; cur; cur=cur->next)
0145     {
0146         if (0==strcmp("env", (char *)cur->name))
0147         {
0148             /* allocate new env variable */
0149             NDRX_CALLOC_OUT(env, 1, sizeof(*env), ndrx_env_list_t);
0150             
0151             /* extract attributes */
0152             for (attr=cur->properties; attr; attr = attr->next)
0153             {
0154                 if (0==strcmp((char *)attr->name, "name"))
0155                 {
0156                     p = (char *)xmlNodeGetContent(attr->children);
0157                     
0158                     env->key = NDRX_STRDUP(p);
0159                     
0160                     if (NULL==env->key)
0161                     {
0162                         NDRX_LOG(log_error, "Failed to strdup: %s", 
0163                                 strerror(errno));
0164                         xmlFree(p);
0165                         EXFAIL_OUT(ret);
0166                     }
0167                     xmlFree(p);
0168                 }
0169                 else if (0==strcmp((char *)attr->name, "unset"))
0170                 {
0171                     
0172                     p = (char *)xmlNodeGetContent(attr->children);
0173                     
0174                     if (0==strcmp(p, "Y") || 0==strcmp(p, "y") ||
0175                             0==strcmp(p, "Yes") || 0==strcmp(p, "yes"))
0176                     {
0177                         env->flags|=NDRX_ENV_ACTION_UNSET;
0178                     }
0179                     xmlFree(p);
0180                 }
0181             }
0182             
0183             if (EXEOS==env->key[0])
0184             {
0185                 NDRX_LOG(log_error, "XML config <envs> tag name attrib!", p);
0186                 userlog("XML config <envs> tag name attrib!", p);
0187                 EXFAIL_OUT(ret);
0188             }
0189             
0190             /* extract value */
0191             p = (char *)xmlNodeGetContent(cur);
0192             
0193             /* no need for value if action unset */
0194             if (!(env->flags & NDRX_ENV_ACTION_UNSET))
0195             {
0196                 env->value = NDRX_STRDUP(p);
0197                 
0198                 if (NULL==env->value)
0199                 {
0200                     NDRX_LOG(log_error, "Failed to strdup: %s", 
0201                                     strerror(errno));
0202                     xmlFree(p);
0203                     EXFAIL_OUT(ret);
0204                 }
0205                 
0206                 /* subsitute content? */
0207                 
0208             }
0209             xmlFree(p);
0210             
0211             /* if we are ok, add to linked list */
0212             DL_APPEND(*envs, env);
0213             env = NULL;
0214 
0215         }
0216         else if (0==strcmp("usegroup", (char *)cur->name))
0217         {
0218             ndrx_env_group_t * pullin;
0219             p = (char *)xmlNodeGetContent(cur);
0220             
0221             /* user wants to pull in the group p */
0222             
0223             pullin = ndrx_endrxconf_envs_group_get(grouphash,p);
0224             
0225             if (NULL==pullin)
0226             {
0227                 NDRX_LOG(log_error, "ndrxconfig.xml error: group [%s] not defined!", p);
0228                 userlog("ndrxconfig.xml error: group [%s] not defined!", p);
0229                 xmlFree(p);
0230                 EXFAIL_OUT(ret);
0231             }
0232             xmlFree(p);
0233             
0234             if (NULL!=grouplist)
0235             {
0236                 /* add the group to the list */
0237                 NDRX_CALLOC_OUT(glist, 1, sizeof(ndrx_env_grouplist_t), 
0238                         ndrx_env_grouplist_t);
0239                 glist->group = pullin;
0240                 
0241                 DL_APPEND(*grouplist, glist);
0242                 glist = NULL;
0243             }
0244             else
0245             {
0246                 /* merge their group into this group */
0247                 if (EXSUCCEED!=ndrx_ndrxconf_envs_append(envs, pullin->envs))
0248                 {
0249                     NDRX_LOG(log_error, "Failed to append envs");
0250                     EXFAIL_OUT(ret);
0251                 }
0252             }
0253         } /* use group */
0254     } /* for cursor... */
0255     
0256 out:
0257     
0258     if (EXSUCCEED!=ret)
0259         
0260     {
0261         if (NULL!=env)
0262         {
0263             env_free(env);
0264         }
0265         
0266         if (NULL!=glist)
0267         {
0268             NDRX_FREE(glist);
0269         }
0270     }
0271     
0272     return ret;
0273 }
0274 
0275 /**
0276  * Resolve group by group name
0277  * @param grouphash group hash
0278  * @param group group name
0279  * @return NULL (not found) or ptr to group found
0280  */
0281 exprivate ndrx_env_group_t * ndrx_endrxconf_envs_group_get(ndrx_env_group_t *grouphash, 
0282         char *group)
0283 {
0284     ndrx_env_group_t * ret = NULL;
0285     
0286     EXHASH_FIND_STR(grouphash, group, ret);
0287     
0288     return ret;
0289 }
0290 
0291 /**
0292  * Free up single env entry
0293  * @param env env list entry
0294  */
0295 exprivate void env_free(ndrx_env_list_t *env)
0296 {
0297     if (NULL!=env->key)
0298     {
0299         NDRX_FREE(env->key);
0300     }
0301 
0302     if (NULL!=env->value)
0303     {
0304         NDRX_FREE(env->value);
0305     }
0306     
0307     if (NULL!=env)
0308     {
0309         NDRX_FREE(env);
0310     }
0311 }
0312 
0313 /**
0314  * Free up the environments list
0315  * @param envs list of 
0316  */
0317 expublic void  ndrx_ndrxconf_envs_envs_free(ndrx_env_list_t **envs)
0318 {
0319     ndrx_env_list_t * el, *elt;
0320     
0321     DL_FOREACH_SAFE(*envs, el, elt)
0322     {
0323         DL_DELETE(*envs, el);
0324         
0325         env_free(el);
0326     }
0327 }
0328 
0329 /**
0330  * Free up the group, we shall clean up the envs
0331  * and free up the group by it self
0332  * @param grouphash hash list to remove from (if set)
0333  * @param group group to delete
0334  */
0335 exprivate void  ndrx_ndrxconf_envs_group_free(ndrx_env_group_t **grouphash, 
0336         ndrx_env_group_t *group)
0337 {
0338      ndrx_ndrxconf_envs_envs_free(&group->envs);
0339     
0340     if (NULL!=grouphash)
0341     {
0342         EXHASH_DEL(*grouphash, group);
0343     }
0344     
0345     NDRX_FREE(group);
0346 }
0347 
0348 /**
0349  * Delete group lists
0350  * @param grouplist group list
0351  */
0352 expublic void ndrx_ndrxconf_envs_grouplists_free(ndrx_env_grouplist_t **grouplist)
0353 {
0354     ndrx_env_grouplist_t *el, *elt;
0355     
0356     DL_FOREACH_SAFE(*grouplist, el, elt)
0357     {
0358         DL_DELETE(*grouplist, el);
0359         NDRX_FREE(el);
0360     }
0361 }
0362 
0363 /**
0364  * Remove all groups noted in hash
0365  * @param grouphash group hash to clean up
0366  */
0367 expublic void ndrx_ndrxconf_envs_groups_free(ndrx_env_group_t **grouphash)
0368 {
0369     ndrx_env_group_t *el, *elt;
0370     
0371     EXHASH_ITER(hh, *grouphash, el, elt)
0372     {
0373         ndrx_ndrxconf_envs_group_free(grouphash, el);
0374     }
0375 }
0376 
0377 /**
0378  * Parse group tag and fill up the hash list
0379  * @param doc xml document
0380  * @param cur current cursor
0381  * @param grouphash Hash list of the groups
0382  * @return EXSUCCEED/EXFAIL
0383  */
0384 expublic int ndrx_ndrxconf_envs_group_parse(xmlDocPtr doc, xmlNodePtr cur, 
0385          ndrx_env_group_t **grouphash)
0386 {
0387     int ret = EXSUCCEED;
0388     ndrx_env_group_t *group=NULL;
0389     xmlAttrPtr attr;
0390     char *p;
0391     
0392     NDRX_CALLOC_OUT(group, 1, sizeof(ndrx_env_group_t), ndrx_env_group_t);
0393     
0394     /* extract the  group code */
0395     for (attr=cur->properties; attr; attr = attr->next)
0396     {
0397         /* TODO: Test that name is mandatory! */
0398         if (0==strcmp((char *)attr->name, "group"))
0399         {
0400             p = (char *)xmlNodeGetContent(attr->children);
0401             NDRX_STRCPY_SAFE(group->group, p);
0402             xmlFree(p);
0403         }
0404     }
0405     
0406     if (EXEOS==group->group[0])
0407     {
0408         NDRX_LOG(log_error, "ERROR ! missing group name in XML config, <envs> tag");
0409         userlog("ERROR ! missing group name in XML config, <envs> tag");
0410         EXFAIL_OUT(ret);
0411     }
0412     
0413     /* test group code against hash list if found, then reject with error */
0414     if (NULL!=ndrx_endrxconf_envs_group_get(*grouphash, group->group))
0415     {
0416         NDRX_LOG(log_error, "ERROR ! Group [%s] already defined in xml config", 
0417                 group->group);
0418         userlog("ERROR ! Group [%s] already defined in xml config", 
0419                 group->group);
0420         EXFAIL_OUT(ret);
0421     }
0422     
0423     /* load the environments 
0424      * This shall loop over the all items in the cursor closure
0425      */
0426     if (EXSUCCEED!=ndrx_ndrxconf_envs_parse(doc, cur, &group->envs, *grouphash, NULL))
0427     {
0428         NDRX_LOG(log_error, "Failed to parse environment variables from xml");
0429         EXFAIL_OUT(ret);
0430     }
0431     
0432     /* finally add group to hash list */
0433     
0434     EXHASH_ADD_STR(*grouphash, group, group);
0435     
0436     
0437 out:
0438     
0439     /* delete invalid group */
0440     if (EXSUCCEED!=ret && NULL!=group)
0441     {
0442          ndrx_ndrxconf_envs_group_free(NULL, group);
0443     }
0444         
0445     return ret;
0446 }
0447 
0448 /**
0449  * Load environment from list
0450  * @param envs linked list with env settings
0451  * @return EXSUCCED/EXFAIL
0452  */
0453 expublic int ndrx_ndrxconf_envs_apply(ndrx_env_list_t *envs)
0454 {
0455     int ret = EXSUCCEED;
0456     char tmp[PATH_MAX];
0457     ndrx_env_list_t *el;
0458     
0459     DL_FOREACH(envs, el)
0460     {   
0461         if (el->flags & NDRX_ENV_ACTION_UNSET)
0462         {
0463             NDRX_LOG(log_dump, "Unsetting env [%s]", el->key);
0464 
0465             if (EXSUCCEED!=unsetenv(el->key))
0466             {
0467                 int err = errno;
0468                 NDRX_LOG(log_error, "Failed to set [%s]=[%s]: %s",
0469                         el->key, tmp, strerror(err));
0470                 userlog("Failed to set [%s]=[%s]: %s",
0471                         el->key, tmp, strerror(err));
0472                 EXFAIL_OUT(ret);
0473             }
0474         }
0475         else
0476         {
0477             NDRX_STRCPY_SAFE(tmp, el->value);
0478             ndrx_str_env_subs_len(tmp, sizeof(tmp));
0479 
0480             NDRX_LOG(log_dump, "Setting env [%s]=[%s]",
0481                     el->key, tmp);
0482 
0483             if (EXSUCCEED!=setenv(el->key, tmp, EXTRUE))
0484             {
0485                 int err = errno;
0486                 NDRX_LOG(log_error, "Failed to set [%s]=[%s]: %s",
0487                         el->key, tmp, strerror(err));
0488                 userlog("Failed to set [%s]=[%s]: %s",
0489                         el->key, tmp, strerror(err));
0490                 EXFAIL_OUT(ret);
0491             }
0492         }
0493     }
0494     
0495 out:
0496     return ret;
0497 }
0498 
0499 /**
0500  * Apply environment variables from the group list and individuals from the
0501  * list (load variables into environment)
0502  * @param grouplist process references to group listings
0503  * @param envs process specific environment variables
0504  * @return EXSUCCEED/EXFAIL
0505  */
0506 expublic int ndrx_ndrxconf_envs_applyall(ndrx_env_grouplist_t *grouplist, 
0507         ndrx_env_list_t *envs)
0508 {
0509     int ret = EXSUCCEED;
0510     ndrx_env_grouplist_t *el;
0511     
0512     DL_FOREACH(grouplist, el)
0513     {
0514         NDRX_LOG(log_debug, "Loading group envs [%s]", el->group->group);
0515      
0516         if (EXSUCCEED!=ndrx_ndrxconf_envs_apply(el->group->envs))
0517         {
0518             NDRX_LOG(log_error, "Failed to load group envs [%s]", 
0519                     el->group->group);
0520             EXFAIL_OUT(ret);
0521         }
0522     }
0523     
0524     if (EXSUCCEED!=ndrx_ndrxconf_envs_apply(envs))
0525     {
0526         NDRX_LOG(log_error, "Failed to load process specific envs!");
0527         EXFAIL_OUT(ret);
0528     }
0529 out:
0530     return ret;
0531 }
0532 
0533 /* vim: set ts=4 sw=4 et smartindent: */