Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Commons for system abstractions
0003  *
0004  * @file sys_common.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 
0035 /*---------------------------Includes-----------------------------------*/
0036 #include <stdio.h>
0037 #include <stdlib.h>
0038 #include <time.h>
0039 
0040 #include <unistd.h>
0041 #include <stdarg.h>
0042 #include <ctype.h>
0043 #include <memory.h>
0044 #include <errno.h>
0045 #include <signal.h>
0046 #include <limits.h>
0047 #include <pthread.h>
0048 #include <string.h>
0049 #include <dirent.h>
0050 
0051 #include <ndrstandard.h>
0052 #include <ndebug.h>
0053 #include <nstdutil.h>
0054 #include <limits.h>
0055 #include <exhash.h>
0056 #include <sys_unix.h>
0057 
0058 #include <utlist.h>
0059 #include <pwd.h>
0060 #include <regex.h>
0061 
0062 #include "userlog.h"
0063 #include "exregex.h"
0064 #include "exparson.h"
0065 #include "atmi_int.h"
0066 
0067 
0068 
0069 /*---------------------------Externs------------------------------------*/
0070 /*---------------------------Macros-------------------------------------*/
0071     
0072 /*#define SYSCOMMON_ENABLE_DEBUG - causes locks in case of invalid config,
0073  *      due to recursive debug init */
0074 #define MAX_ATFORKS         3
0075 
0076 /*
0077  * For AIX and Solaris stock forking is working OK
0078  * For Linux: cannot relay on internal forking
0079  * as getting link error such as hidden symbol `pthread_atfork'
0080  */
0081 #if defined(EX_OS_AIX) || defined(EX_OS_SUNOS)
0082 #define USE_STOCK_FORKING
0083 #endif
0084 
0085 /* #define USE_STOCK_FORKING */
0086 
0087 /*---------------------------Enums--------------------------------------*/
0088 /*---------------------------Typedefs-----------------------------------*/
0089 /*---------------------------Globals------------------------------------*/
0090 /*---------------------------Statics------------------------------------*/
0091 
0092 /** Function to run before fork */
0093 exprivate void (*M_prepare[MAX_ATFORKS])(void) = {NULL, NULL, NULL}; 
0094 /** Function to run after fork, by parent */
0095 exprivate void (*M_parent[MAX_ATFORKS])(void) = {NULL, NULL, NULL};
0096 /** Function to run after fork, by child */
0097 exprivate void (*M_child[MAX_ATFORKS])(void) = {NULL, NULL, NULL};
0098 
0099 /*---------------------------Prototypes---------------------------------*/
0100 
0101 
0102 /**
0103  * Counted add
0104  * @param h string hash
0105  * @param str string to add
0106  * @return entry added/updated
0107  */
0108 expublic  string_hash_t * ndrx_string_hash_add_cnt(string_hash_t **h, char *str)
0109 {
0110 
0111     string_hash_t *el=NULL;
0112     
0113     el = ndrx_string_hash_get(*h, str);
0114     
0115     if (NULL!=el)
0116     {
0117         el->cnt++;
0118     }
0119     else
0120     {
0121         el = ndrx_string_hash_add(h, str);
0122         if (NULL!=el)
0123         {
0124             el->cnt=1;
0125         }
0126     }
0127     
0128     return el;
0129 }
0130 
0131 /**
0132  * Add item to string hash
0133  * @param h
0134  * @param str
0135  * @return SUCCEED/FAIL
0136  */
0137 expublic string_hash_t * ndrx_string_hash_add(string_hash_t **h, char *str)
0138 {
0139     int ret = EXSUCCEED;
0140     string_hash_t * tmp = NDRX_CALLOC(1, sizeof(string_hash_t));
0141     
0142     if (NULL==tmp)
0143     {
0144 #ifdef SYSCOMMON_ENABLE_DEBUG
0145         NDRX_LOG(log_error, "alloc of string_hash_t (%d) failed", 
0146                 sizeof(string_hash_t));
0147 #endif
0148         EXFAIL_OUT(ret);
0149     }
0150 
0151     if (NULL==(tmp->str = strdup(str)))
0152     {
0153 #ifdef SYSCOMMON_ENABLE_DEBUG
0154         NDRX_LOG(log_error, "strdup() failed: %s", strerror(errno));
0155 #endif
0156         EXFAIL_OUT(ret);
0157     }
0158     
0159     /* Add stuff to hash finally */
0160     EXHASH_ADD_KEYPTR( hh, (*h), tmp->str, strlen(tmp->str), tmp );
0161     
0162     
0163 out:
0164     if (EXSUCCEED==ret)
0165     {
0166         return tmp;
0167     }
0168     else
0169     {
0170         return NULL;
0171     }
0172 }
0173 
0174 /**
0175  * Search for string existance in hash
0176  * @param h hash handler
0177  * @param str string to search for
0178  * @return NULL not found/not NULL - found
0179  */
0180 expublic string_hash_t * ndrx_string_hash_get(string_hash_t *h, char *str)
0181 {
0182     string_hash_t * r = NULL;
0183     
0184     EXHASH_FIND_STR( h, str, r);
0185     
0186     return r;
0187 }
0188 
0189 /**
0190  * Free up the hash list
0191  * @param h
0192  * @return 
0193  */
0194 expublic void ndrx_string_hash_free(string_hash_t *h)
0195 {
0196     string_hash_t * r, *rt;
0197     /* safe iter over the list */
0198     EXHASH_ITER(hh, h, r, rt)
0199     {
0200         EXHASH_DEL(h, r);
0201         NDRX_FREE(r->str);
0202         NDRX_FREE(r);
0203     }
0204 }
0205 
0206 /**
0207  * Free the list of message queues
0208  */
0209 expublic void ndrx_string_list_free(string_list_t* list)
0210 {
0211     string_list_t *elt, *tmp;
0212     
0213     if (NULL!=list)
0214     {
0215         LL_FOREACH_SAFE(list,elt,tmp) 
0216         {
0217             LL_DELETE(list,elt);
0218             if (NULL!=elt->qname)
0219             {
0220                 NDRX_FREE(elt->qname);
0221             }
0222             
0223             NDRX_FREE((char *)elt);
0224         }
0225     }
0226 }
0227 
0228 /**
0229  * Add element to string list
0230  * @param list List to append, can be ptr to NULL
0231  * @param string String element to add to list
0232  * @return SUCCEED/FAIL. If fail, list element will not be allocated
0233  */
0234 expublic int ndrx_string_list_add(string_list_t**list, char *string)
0235 {
0236     int ret = EXSUCCEED;
0237     string_list_t* tmp = NULL;
0238     
0239     if (NULL==(tmp = NDRX_CALLOC(1, sizeof(string_list_t))))
0240     {
0241         NDRX_LOG(log_error, "alloc of string_list_t (%d) failed", 
0242                 sizeof(string_list_t));
0243         EXFAIL_OUT(ret);
0244     }
0245     
0246     /* Alloc the string down there */
0247     if (NULL==(tmp->qname = NDRX_STRDUP(string)))
0248     {
0249         NDRX_LOG(log_error, "Failed to strdup len (%d): %s", 
0250                strlen(string)+1, strerror(errno));
0251         NDRX_FREE(tmp);
0252         EXFAIL_OUT(ret);
0253     }
0254     
0255     /*  Add the string to list finally */
0256     LL_APPEND(*list, tmp);
0257     
0258  out:
0259     return ret;
0260 }
0261 
0262 /**
0263  * Split the string by separator. Strip whitespace from start and end of each
0264  * token. Add each token to the string list
0265  * @param list string list to fill
0266  * @param string string to split
0267  * @param sep separator to split by
0268  * @return EXSUCCEED/EXFAIL(out of mem)
0269  */
0270 expublic int ndrx_string_list_splitadd(string_list_t**list, char *string, char *sep)
0271 {
0272     int ret = EXSUCCEED;
0273     string_list_t* tmp = NULL;
0274     char *temps = NULL;
0275     char *tag;
0276     char *value_ptr;
0277     char *tag_first;
0278     char *ent;
0279     
0280     temps = NDRX_STRDUP(string);
0281     if (NULL==temps)
0282     {
0283         NDRX_LOG(log_error, "Failed to strdup: %s", strerror(errno));
0284         EXFAIL_OUT(ret);
0285     }
0286     
0287     tag_first = temps;
0288     
0289     NDRX_LOG(log_debug, "About token: [%s] by [%s]", tag_first, sep);
0290     
0291     while ((tag = strtok_r(tag_first, sep, &value_ptr)))
0292     {
0293         if (NULL!=tag_first)
0294         {
0295             tag_first = NULL; /* now loop over the string */
0296         }
0297         
0298         /* get the start */
0299         ent = ndrx_str_lstrip_ptr(tag, " \t");
0300         /* strip off trailing */
0301         ndrx_str_rstrip(ent, " \t");
0302         
0303         /* Add to string list */
0304         if (NULL==(tmp = NDRX_CALLOC(1, sizeof(string_list_t))))
0305         {
0306             NDRX_LOG(log_error, "calloc of string_list_t (%d) failed", 
0307                     sizeof(string_list_t));
0308             EXFAIL_OUT(ret);
0309         }
0310         
0311         if (NULL==(tmp->qname = NDRX_STRDUP(ent)))
0312         {
0313             NDRX_LOG(log_error, "Failed to strdup len (%d): %s", 
0314                    strlen(ent)+1, strerror(errno));
0315             NDRX_FREE(tmp);
0316             EXFAIL_OUT(ret);
0317         }
0318         
0319         NDRX_LOG(log_debug, "Adding [%s]", tmp->qname);
0320         /*  Add the string to list finally */
0321         LL_APPEND(*list, tmp);
0322     }
0323     
0324  out:
0325     
0326     if (NULL!=temps)
0327     {
0328         NDRX_FREE(temps);
0329     }
0330  
0331     return ret;
0332 }
0333 
0334 /**
0335  * Return PS output for the parent
0336  * assuming that next we will extract only pid and not any other infos
0337  * because for BSD the output will be slightly different than normal ps -ef or auxxw
0338  * @param ppid  Parent PID id
0339  * @return List of child processes or NULL.
0340  */
0341 expublic string_list_t * ndrx_sys_ps_getchilds(pid_t ppid)
0342 {
0343     /*
0344      * Free BSD: ps -jauxxw
0345      * Other Unix: ps -ef
0346      * compare third column with 
0347      */
0348     char cmd[128];
0349     FILE *fp=NULL;
0350     string_list_t* ret = NULL;
0351     pid_t pid;
0352     char path[PATH_MAX];
0353     int is_error = EXFALSE;
0354     
0355 #ifdef EX_OS_FREEBSD
0356     /* snprintf(cmd, sizeof(cmd), "ps -jauxxw"); */
0357     NDRX_STRCPY_SAFE(cmd, "ps -wwaxo user,pid,ppid,%cpu,%mem,args");
0358 #elif EX_OS_SUNOS
0359     NDRX_STRCPY_SAFE(cmd, "ps -ef");
0360 #elif EX_OS_AIX
0361     NDRX_STRCPY_SAFE(cmd, "ps -ef");
0362 #else
0363     NDRX_STRCPY_SAFE(cmd, "ps -efww");
0364 #endif
0365     
0366     fp = popen(cmd, "r");
0367     
0368     if (fp == NULL)
0369     {
0370         NDRX_LOG(log_warn, "failed to run command [%s]: %s", cmd, strerror(errno));
0371         goto out;
0372     }
0373     
0374     while (fgets(path, sizeof(path)-1, fp) != NULL)
0375     {
0376         if (EXSUCCEED==ndrx_proc_ppid_get_from_ps(path, &pid) && ppid == pid)
0377         {
0378             if (EXSUCCEED!=ndrx_string_list_add(&ret, path))
0379             {
0380                 NDRX_LOG(log_error, "Failed to add [%s] to list of processes", path);
0381                 is_error = EXTRUE;
0382                 goto out;
0383             }
0384         }
0385     }
0386     
0387  out:
0388     /* close */
0389     if (fp!=NULL)
0390     {
0391         pclose(fp);
0392     }
0393  
0394     if (is_error)
0395     {
0396         ndrx_string_list_free(ret);
0397         ret = NULL;
0398     }
0399 
0400     return ret;
0401 }
0402     
0403 
0404 /**
0405  * List processes by filters.
0406  * NOTE! Possibly might return empty results if OS have issues with forking,
0407  * i.e. limits... etc.
0408  * @param filter1 - match the string1
0409  * @param filter2 - match the string2
0410  * @param filter3 - match the string3
0411  * @param filter4 - match the string4
0412  * @param regex1  - match by regular expression (if any set)
0413  * @return String list of matched lines
0414  */
0415 expublic string_list_t * ndrx_sys_ps_list(char *filter1, char *filter2, 
0416         char *filter3, char *filter4, char *regex1)
0417 {
0418     FILE *fp=NULL;
0419     char cmd[128];
0420     char path[PATH_MAX];
0421     int ok;
0422     int i;
0423     string_list_t* tmp;
0424     string_list_t* ret = NULL;
0425     regex_t r1;
0426     int r1_alloc = EXFALSE;
0427     int is_error = EXFALSE;
0428     
0429 #define MAX_FILTER      5
0430     char *filter[MAX_FILTER] = {filter1, filter2, filter3, filter4, regex1};
0431     
0432 #ifdef EX_OS_FREEBSD
0433     /* snprintf(cmd, sizeof(cmd), "ps -auwwx"); */
0434     NDRX_STRCPY_SAFE(cmd, "ps -wwaxo user,pid,ppid,%cpu,%mem,args");
0435 #elif EX_OS_DARWIN
0436     /* we need full username instead of uid in output...*/
0437     NDRX_STRCPY_SAFE(cmd, "ps -je");
0438 #elif EX_OS_SUNOS
0439     NDRX_STRCPY_SAFE(cmd, "ps -ef");
0440 #elif EX_OS_AIX
0441     NDRX_STRCPY_SAFE(cmd, "ps -ef");
0442 #else
0443     NDRX_STRCPY_SAFE(cmd, "ps -efww");
0444 #endif
0445     
0446     if (EXEOS!=regex1[0])
0447     {
0448         if (EXSUCCEED!=ndrx_regcomp(&r1, regex1))
0449         {
0450             NDRX_LOG(log_error, "ndrx_sys_ps_list: Failed to compile regex1: [%s]", regex1);
0451             userlog("ndrx_sys_ps_list: Failed to compile regex1: [%s]", regex1);
0452             ret = NULL;
0453             goto out;
0454         }
0455         
0456         r1_alloc = EXTRUE;
0457     }
0458     
0459 #ifdef SYSCOMMON_ENABLE_DEBUG
0460     NDRX_LOG(log_debug, "Listing processes [%s] f1=[%s] f2=[%s] f3=[%s] f4=[%s] r1=[%s]", 
0461             cmd, filter1, filter2, filter3, filter4, regex1);
0462 #endif
0463     
0464     /* Open the command for reading. */
0465     fp = popen(cmd, "r");
0466     if (fp == NULL)
0467     {
0468         userlog("failed to run command [%s]: %s", cmd, strerror(errno));
0469         goto out;
0470     }
0471     
0472     /* Check the process name in output... */
0473     while (fgets(path, sizeof(path)-1, fp) != NULL)
0474     {
0475 
0476         /*NDRX_LOG(log_debug, "Got line [%s]", path); - causes locks... */
0477         
0478         ok = 0;
0479         
0480         for (i = 0; i<MAX_FILTER; i++)
0481         {
0482             /* Do the regexp match.. */
0483             if (EXEOS!=filter[i][0] && filter[i]==regex1 
0484                     && EXSUCCEED==ndrx_regexec(&r1, path))
0485             {
0486                 /* for example [/ ]cpmsrv\s */
0487                 ok++;
0488             }
0489             else if (EXEOS!=filter[i][0] && strstr(path, filter[i]))
0490             {
0491              /*   NDRX_LOG(log_debug, "filter%d [%s] - ok", i, filter[i]); */
0492                 ok++;
0493             }
0494             else if (EXEOS==filter[i][0])
0495             {
0496                 /* NDRX_LOG(log_debug, "filter%d [%s] - ok", i, filter[i]); */
0497                 ok++;
0498             }
0499             else
0500             {
0501                 /* NDRX_LOG(log_debug, "filter%d [%s] - fail", i, filter[i]); */
0502             }
0503         }
0504         
0505         /* NDRX_LOG(log_debug, "Filters vote %d/%d", ok, MAX_FILTER); */
0506         
0507         if (MAX_FILTER==ok)
0508         {
0509             /* Remove trailing newline */
0510             ndrx_chomp(path);
0511             if (EXSUCCEED!=ndrx_string_list_add(&ret, path))
0512             {
0513                 is_error = EXTRUE;
0514                 goto out;
0515             }
0516         }
0517     }
0518 
0519 out:
0520     /* close */
0521     if (fp!=NULL)
0522     {
0523         pclose(fp);
0524     }
0525 
0526     if (r1_alloc)
0527     {
0528         ndrx_regfree(&r1);
0529     }
0530 
0531     if (is_error)
0532     {
0533         ndrx_string_list_free(ret);
0534         ret=NULL;
0535     }
0536 
0537     return ret;
0538     
0539 }
0540 
0541 /**
0542  * Transfer pid (as key) and ppid (as value)
0543  * to hashmap
0544  * @param plist process list (ps output)
0545  * @param hash hash to build (might be NULL at input)
0546  * @return EXSUCCEED/EXFAIL (OOM)
0547  */
0548 expublic int ndrx_sys_ps_list2hash(string_list_t *plist, ndrx_intmap_t **hash)
0549 {
0550     string_list_t* elt = NULL;
0551     pid_t pid;
0552     pid_t ppid;
0553     int ret = EXSUCCEED;
0554     
0555     LL_FOREACH(plist,elt)
0556     {
0557         if (EXSUCCEED==ndrx_proc_pid_get_from_ps(elt->qname, &pid) &&
0558                 EXSUCCEED==ndrx_proc_ppid_get_from_ps(elt->qname, &ppid) &&
0559                 0!=pid &&
0560                 0!=ppid)
0561         {
0562             if (NULL==ndrx_intmap_add(hash, (int)pid, (int)ppid))
0563             {
0564                 EXFAIL_OUT(ret);
0565             }
0566         }
0567             
0568     }
0569     
0570 out:
0571     
0572     if (EXSUCCEED!=ret)
0573     {
0574         ndrx_intmap_remove (hash);
0575     }
0576 
0577     return ret;
0578 }
0579 
0580 /**
0581  * Search parents for given pid.
0582  * Search until the dead end (0 of init / no parent).
0583  * @param pshash psout hash
0584  * @param pid to search parents for
0585  * @param parents add parents to this hash. In this hash only pids are stored
0586  * @return EXSUCCEED/EXFAIL (OOM)
0587  */
0588 expublic int ndrx_sys_ps_hash2parents(ndrx_intmap_t **pshash, int pid, ndrx_intmap_t **parents)
0589 {
0590     int ret = EXSUCCEED;    
0591     ndrx_intmap_t *cur = ndrx_intmap_find (pshash, pid);
0592     
0593     while (NULL!=cur)
0594     {
0595         if (NULL==ndrx_intmap_add (parents, cur->value, 0))
0596         {
0597             EXFAIL_OUT(ret);
0598         }
0599         
0600         /* search next. Note that in ps hash value stores parents */
0601         cur = ndrx_intmap_find (pshash, cur->value);
0602     }
0603     
0604 out:
0605     return ret;
0606 }
0607 
0608 /**
0609  * Get current system username
0610  */
0611 expublic char *ndrx_sys_get_cur_username(void)
0612 {
0613     uid_t uid = geteuid();
0614     struct passwd *pw = getpwuid(uid);
0615     if (pw)
0616     {
0617         return pw->pw_name;
0618     }
0619 
0620     return "";
0621 }
0622 
0623 /**
0624  * Return hostname
0625  * @param out_hostname
0626  * @param bufsz
0627  * @return 
0628  */
0629 expublic int ndrx_sys_get_hostname(char *out_hostname, long out_bufsz)
0630 {
0631     int ret = EXSUCCEED;
0632     
0633     if (EXSUCCEED!=gethostname(out_hostname, out_bufsz))
0634     {
0635         userlog("Failed to get hostname: %s", strerror(errno));
0636         EXFAIL_OUT(ret);
0637     }
0638     
0639 out:
0640     return ret;
0641 }
0642 
0643 /**
0644  * List the contents of the folder
0645  */
0646 expublic string_list_t* ndrx_sys_folder_list(char *path, int *return_status)
0647 {
0648     string_list_t* ret = NULL;
0649     struct dirent **namelist;
0650     int n, i;
0651     string_list_t* tmp;
0652     int len;
0653     
0654     *return_status = EXSUCCEED;
0655     
0656     n = scandir(path, &namelist, 0, alphasort);
0657     if (n < 0)
0658     {
0659         /* standard logging might doing init right now */
0660 #ifdef SYSCOMMON_ENABLE_DEBUG
0661         NDRX_LOG(log_error, "Failed to open queue directory [%s]: %s", 
0662                 path, strerror(errno));
0663 #endif
0664         goto exit_fail;
0665     }
0666     else 
0667     {
0668         for (i=0; i<n; i++)
0669         {
0670             
0671             if (0==strcmp(namelist[i]->d_name, ".") || 
0672                         0==strcmp(namelist[i]->d_name, ".."))
0673             {
0674                 NDRX_FREE(namelist[i]);
0675                 continue;
0676             }
0677             
0678             len = 1 /* / */ + strlen(namelist[i]->d_name) + 1 /* EOS */;
0679             
0680             if (NULL==(tmp = NDRX_CALLOC(1, sizeof(string_list_t))))
0681             {
0682                 /* standard logging might doing init right now */
0683 #ifdef SYSCOMMON_ENABLE_DEBUG
0684                 NDRX_LOG(log_error, "alloc of mq_list_t (%d) failed: %s", 
0685                         sizeof(string_list_t), strerror(errno));
0686 #endif
0687                 goto exit_fail;
0688             }
0689             
0690             if (NULL==(tmp->qname = NDRX_MALLOC(len)))
0691             {
0692                 /* standard logging might doing init right now */
0693 #ifdef SYSCOMMON_ENABLE_DEBUG
0694                 NDRX_LOG(log_error,"alloc of %d bytes failed: %s", 
0695                         len, strerror(errno));
0696 #endif
0697                 NDRX_FREE(tmp);
0698                 goto exit_fail;
0699             }
0700             
0701             
0702             strcpy(tmp->qname, "/");
0703             strcat(tmp->qname, namelist[i]->d_name);
0704             
0705             /* Add to LL */
0706             LL_APPEND(ret, tmp);
0707             
0708             NDRX_FREE(namelist[i]);
0709         }
0710         NDRX_FREE(namelist);
0711     }
0712     
0713     return ret;
0714     
0715 exit_fail:
0716 
0717     *return_status = EXFAIL;
0718 
0719     if (NULL!=ret)
0720     {
0721         ndrx_string_list_free(ret);
0722         ret = NULL;
0723     }
0724 
0725     return ret;   
0726 }
0727 
0728 /**
0729  * Kill the list
0730  * @param list list of ps output processes to kill
0731  */
0732 expublic void ndrx_proc_kill_list(string_list_t *list)
0733 {
0734     string_list_t* elt = NULL;
0735     int signals[] = {SIGTERM, SIGKILL};
0736     int i;
0737     int max_signals = 2;
0738     int was_any = EXFALSE;
0739     pid_t pid;
0740     char *fn = "ndrx_proc_kill_list";
0741     NDRX_LOG(log_info, "%s enter-> %p", fn, list);
0742     
0743     for (i=0; i<max_signals; i++)
0744     {
0745         LL_FOREACH(list,elt)
0746         {
0747             if (EXSUCCEED==ndrx_proc_pid_get_from_ps(elt->qname, &pid))
0748             {
0749                  NDRX_LOG(log_error, "! killing  sig=%d "
0750                          "pid=[%d] (%s)", signals[i], pid, elt->qname);
0751 
0752                  if (EXSUCCEED!=kill(pid, signals[i]))
0753                  {
0754                      NDRX_LOG(log_error, "failed to kill with signal %d pid %d: %s",
0755                              signals[i], pid, strerror(errno));
0756                  }
0757                  else
0758                  {
0759                     was_any = EXTRUE;
0760                  }
0761             }    
0762         } /* for list entry */
0763         
0764         if (was_any && i<max_signals-1)
0765         {
0766             /* wait a bit */
0767             sleep(EX_KILL_SLEEP_SECS);
0768         }
0769         
0770     } /* for signals */
0771     
0772 }
0773 
0774 /**
0775  * Hmm we shall not kill up here. but just generate a list of childs
0776  * afterwards will kill one by one
0777  * 
0778  * @param list list to fill with all processes under then parent
0779  * @param pid PID of the parent
0780  * @return SUCCEED/FAIL
0781  */
0782 expublic int ndrx_proc_children_get_recursive(string_list_t**list, pid_t pid)
0783 {
0784     int ret = EXSUCCEED;
0785     string_list_t* elt = NULL;
0786     string_list_t* children = NULL;
0787     
0788     char *fn = "ndrx_get_childproc_recursive";
0789     NDRX_LOG(log_info, "%s enter-> list=%p, pid=%d", fn, list, pid);
0790     
0791     children = ndrx_sys_ps_getchilds(pid);
0792     
0793     LL_FOREACH(children,elt)
0794     {
0795         if (EXSUCCEED==ndrx_proc_pid_get_from_ps(elt->qname, &pid))
0796         {
0797             /* Step into, search for childs */
0798             if (EXSUCCEED!=ndrx_proc_children_get_recursive(list, pid))
0799             {
0800                 EXFAIL_OUT(ret);
0801             }
0802             
0803             if (EXSUCCEED!=ndrx_string_list_add(list, elt->qname))
0804             {
0805                 EXFAIL_OUT(ret);
0806             }
0807     
0808         }
0809     } /* For children... */
0810     
0811 out:
0812     ndrx_string_list_free(children);
0813     return ret;
0814 }
0815 
0816 /**
0817  * Parse pid from PS output
0818  * @param psout
0819  * @param pid
0820  * @return 
0821  */
0822 expublic int ndrx_proc_pid_get_from_ps(char *psout, pid_t *pid)
0823 {
0824     char tmp[PATH_MAX+1];
0825     char *token;
0826     int ret = EXSUCCEED;
0827     
0828     NDRX_STRCPY_SAFE(tmp, psout);
0829 
0830     /* get the first token */
0831     if (NULL==(token = strtok(tmp, "\t ")))
0832     {
0833         NDRX_LOG(log_error, "missing username in ps -ef output");
0834         EXFAIL_OUT(ret);
0835     }
0836 
0837     /* get second token */
0838     token = strtok(NULL, "\t ");
0839     if (NULL==token)
0840     {
0841         NDRX_LOG(log_error, "missing pid in ps -ef output");
0842         EXFAIL_OUT(ret);
0843     }   
0844     else
0845     {
0846         *pid = atoi(token);
0847 #ifdef SYSCOMMON_ENABLE_DEBUG
0848         NDRX_LOG(log_debug, "ndrx_get_pid_from_ps: Got %d as pid of [%s]", *pid, psout);
0849 #endif
0850     }
0851     
0852 out:
0853     return ret;
0854 }
0855 
0856 /**
0857  * Extract line output from command
0858  * @param cmd
0859  * @param buf
0860  * @param busz
0861  * @return 
0862  */
0863 expublic int ndrx_proc_get_line(int line_no, char *cmd, char *buf, int bufsz)
0864 {
0865     int ret = EXSUCCEED;
0866     FILE *fp=NULL;
0867     int line = 0;
0868     NDRX_LOG(log_debug, "%s: About to run: [%s]", __func__, cmd);
0869     
0870     fp = popen(cmd, "r");
0871     if (fp == NULL)
0872     {
0873 #ifdef SYSCOMMON_ENABLE_DEBUG
0874         NDRX_LOG(log_warn, "failed to run command [%s]: %s", cmd, strerror(errno));
0875 #endif
0876         EXFAIL_OUT(ret);
0877     }
0878     
0879     while (fgets(buf, bufsz, fp) != NULL)
0880     {
0881         line ++;
0882         
0883         if (line==line_no)
0884         {
0885             break;
0886         }
0887     }
0888 
0889 out:
0890 
0891     /* close */
0892     if (fp!=NULL)
0893     {
0894         pclose(fp);
0895     }
0896 
0897     if (line!=line_no)
0898     {
0899         NDRX_LOG(log_error, "Extract lines: %d, but requested: %d", 
0900                 line, line_no);
0901         ret=EXFAIL;
0902     }
0903 
0904     if (EXSUCCEED==ret)
0905     {
0906         ndrx_chomp(buf);
0907     }
0908 
0909     return ret;   
0910 }
0911 
0912 /**
0913  * Get child process from ps -ef or ps -jauxxw for bsd
0914  * @param psout ps string
0915  * @param pid   If succeed then PID is loaded
0916  * @return SUCCEED/FAIL
0917  */
0918 expublic int ndrx_proc_ppid_get_from_ps(char *psout, pid_t *ppid)
0919 {
0920     char tmp[PATH_MAX+1];
0921     char *token;
0922     int ret = EXSUCCEED;
0923     
0924     NDRX_STRCPY_SAFE(tmp, psout);
0925 
0926     /* get the first token */
0927     if (NULL==(token = strtok(tmp, "\t ")))
0928     {
0929         NDRX_LOG(log_error, "missing username in ps -ef output (1)");
0930         EXFAIL_OUT(ret);
0931     }
0932 
0933     /* get second token */
0934     token = strtok(NULL, "\t ");
0935     if (NULL==token)
0936     {
0937         NDRX_LOG(log_error, "missing pid in ps -ef output (2)");
0938         EXFAIL_OUT(ret);
0939     }
0940     
0941     /* get third token */
0942     token = strtok(NULL, "\t ");
0943     if (NULL==token)
0944     {
0945         NDRX_LOG(log_error, "missing pid in ps -ef output (3)");
0946         EXFAIL_OUT(ret);
0947     }
0948     else
0949     {
0950         *ppid = atoi(token);
0951 #ifdef SYSCOMMON_ENABLE_DEBUG
0952         NDRX_LOG(log_debug, "Got %d as parent pid of [%s]", *ppid, psout);
0953 #endif
0954     }
0955     
0956 out:
0957     return ret;
0958 }
0959 
0960 /**
0961  * Get process information
0962  * WARNING! This generates SIGCHLD - try to avoid to use in user libraries...
0963  * @param pid
0964  * @param p_infos return structure
0965  * @return 
0966  */
0967 expublic int ndrx_proc_get_infos(pid_t pid, ndrx_proc_info_t *p_infos)
0968 {
0969     int ret = EXSUCCEED;
0970     char cmd[128];
0971     char line[PATH_MAX+1];
0972     long  meminfo[16];
0973     int toks;
0974 /*
0975 All unix:
0976 
0977 $ ps -o rss,vsz -p 1
0978 RSS  VSZ
0979 132 5388
0980 
0981  * aix:
0982 $ ps v 1
0983       PID    TTY STAT  TIME PGIN  SIZE   RSS   LIM  TSIZ   TRS %CPU %MEM COMMAND
0984         1      - A     0:38  298   708   208 32768    30    32  0.0  0.0 /etc/i
0985 
0986 +
0987 $ ps -o vsz -p 1
0988   VSZ
0989   708
0990 */
0991     
0992 #ifdef EX_OS_AIX
0993     snprintf(cmd, sizeof(cmd), "ps v %d", pid);
0994     
0995     if (EXSUCCEED!=ndrx_proc_get_line(2, cmd, line, sizeof(line)))
0996     {
0997         NDRX_LOG(log_error, "Failed to get rss infos from  [%s]", cmd);
0998         EXFAIL_OUT(ret);
0999     }
1000     
1001     NDRX_LOG(log_debug, "Parsing output: [%s]", line);
1002     
1003     toks = ndrx_tokens_extract(line, "%ld", (void *)meminfo, 
1004             sizeof(long), N_DIM(meminfo), 0, 15);
1005     
1006     if (toks<7)
1007     {
1008         NDRX_LOG(log_error, "Invalid tokens, expected at least 7, got %d", toks);
1009        EXFAIL_OUT(ret);
1010     }
1011     
1012     p_infos->rss = meminfo[6];
1013     
1014     snprintf(cmd, sizeof(cmd), "ps -o vsz -p %d", pid);
1015     
1016     if (EXSUCCEED!=ndrx_proc_get_line(2, cmd, line, sizeof(line)))
1017     {
1018         NDRX_LOG(log_error, "Failed to get rss infos from  [%s]", cmd);
1019         EXFAIL_OUT(ret);
1020     }
1021     
1022     NDRX_LOG(log_debug, "Parsing output: [%s]", line);
1023     
1024     toks = ndrx_tokens_extract(line, "%ld", (void *)meminfo, 
1025             sizeof(long), N_DIM(meminfo), 0, 15);
1026     
1027     if (toks!=1)
1028     {
1029        NDRX_LOG(log_error, "Invalid tokens, expected at least 1, got %d", toks);
1030        EXFAIL_OUT(ret);
1031     }
1032     
1033     p_infos->vsz = meminfo[0];  
1034     
1035 #else
1036     
1037     snprintf(cmd, sizeof(cmd), "ps -o rss,vsz -p%d", pid);
1038     
1039     if (EXSUCCEED!=ndrx_proc_get_line(2, cmd, line, sizeof(line)))
1040     {
1041         NDRX_LOG(log_error, "Failed to get rss/vsz infos from  [%s]", cmd);
1042         EXFAIL_OUT(ret);
1043     }
1044     
1045     NDRX_LOG(log_debug, "Parsing output: [%s]", line);
1046     
1047     toks = ndrx_tokens_extract(line, "%ld", (void *)meminfo, 
1048             sizeof(long), N_DIM(meminfo), 0, 15);
1049     
1050     if (2!=toks)
1051     {
1052        NDRX_LOG(log_error, "Invalid tokens, expected 2, got %d", toks);
1053        EXFAIL_OUT(ret);
1054     }
1055     
1056     p_infos->rss = meminfo[0];
1057     p_infos->vsz = meminfo[1];
1058     
1059 #endif
1060     
1061  
1062     NDRX_LOG(log_info, "extracted rss=%ld vsz=%ld", p_infos->rss, p_infos->vsz);
1063     
1064 out:
1065     
1066     NDRX_LOG(log_debug, "%s: returns %d", __func__, ret);
1067 
1068     return ret;
1069 }
1070 
1071 /**
1072  * Test the regexp against the command output strings.
1073  * This will be used for aix/freebsd/macos/solaris
1074  * NOTE: This generate sign child due to fork.
1075  * @param fmt format string for command, must contain %d for pid
1076  * @param pid process id to test
1077  * @param p_re regular expression to match the output
1078  * @return EXFAIL (failed) / EXSUCCEED (0) - not matched, EXTRUE (1) - matched
1079  */
1080 expublic int ndrx_sys_cmdout_test(char *fmt, pid_t pid, regex_t *p_re)
1081 {
1082     char cmd[PATH_MAX];
1083     FILE *fp=NULL;
1084     char *buf = NULL;
1085     size_t n = PATH_MAX;
1086     int ret = EXSUCCEED;
1087     
1088     /* allocate buffer first */
1089     
1090     NDRX_MALLOC_OUT(buf, n, char);
1091     
1092     snprintf(cmd, sizeof(cmd), fmt, pid);
1093     
1094     fp = popen(cmd, "r");
1095     
1096     if (fp == NULL)
1097     {
1098         NDRX_LOG(log_warn, "failed to run command [%s]: %s", cmd, strerror(errno));
1099         goto out;
1100     }
1101     
1102     while (EXFAIL!=ndrx_getline(&buf, &n, fp))
1103     {
1104         /* test the output... */
1105         if (EXSUCCEED==ndrx_regexec(p_re, buf))
1106         {
1107             NDRX_LOG(log_debug, "Matched env [%s] for pid %d", buf, (int)pid);
1108             ret=EXTRUE;
1109             goto out;
1110         }
1111     }
1112     
1113  out:
1114                 
1115     /* close */
1116     if (fp!=NULL)
1117     {
1118         pclose(fp);
1119     }
1120  
1121     if (NULL!=buf)
1122     {
1123         NDRX_FREE(buf);
1124     }
1125  
1126     return ret;
1127 }
1128 
1129 /**
1130  * Print Enduro/X Banner
1131  */
1132 expublic void ndrx_sys_banner(void)
1133 {
1134     NDRX_BANNER("");
1135 }
1136 
1137 /**
1138  * Prepare for forking
1139  */
1140 expublic void ndrx_atfork_prepare(void)
1141 {
1142     
1143 #ifndef USE_STOCK_FORKING
1144     int i;
1145     
1146     for (i=MAX_ATFORKS-1; i>=0; i--)
1147     {
1148         if (NULL!=M_prepare[i])
1149         {
1150             M_prepare[i]();
1151         }
1152     }
1153 #endif
1154 }
1155 
1156 /**
1157  * After fork, run parent runs
1158  */
1159 expublic void ndrx_atfork_parent(void)
1160 {
1161 #ifndef USE_STOCK_FORKING
1162     int i;
1163     for (i=0; i<MAX_ATFORKS; i++)
1164     {
1165         if (NULL!=M_parent[i])
1166         {
1167             M_parent[i]();
1168         }
1169     }
1170 #endif
1171 }
1172 
1173 /**
1174  * After fork, child runs
1175  */
1176 expublic void ndrx_atfork_child(void)
1177 {
1178 #ifndef USE_STOCK_FORKING
1179     int i;
1180     
1181     /* well we shall update pid for logger... */
1182     ndrx_dbg_pid_update();
1183     
1184     for (i=0; i<MAX_ATFORKS; i++)
1185     {
1186         if (NULL!=M_child[i])
1187         {
1188             M_child[i]();
1189         }
1190     }
1191 #endif
1192 }
1193 
1194 /**
1195  * If expecting to continue to use initialized Enduro/X after forking,
1196  * then fork shall be done with this function.
1197  * @param chldresume if 0 - no child resume (i.e. no resume of SystemV aux threads,
1198  *  which is not recommended for XATMI servers as admin thread of child 
1199  *  might consume parent's messages.) if set != 0 then resume aux threads,
1200  *  this is can be suitable for initialized clients which are doing forks
1201  *  for some job/connection handling.
1202  * @return for parent process child process pid is returned, for child 0 is
1203  *  returned.
1204  */
1205 expublic pid_t ndrx_fork(void)
1206 {
1207     pid_t ret;
1208     int err;
1209     
1210 #ifdef USE_STOCK_FORKING
1211     
1212     ret = fork();
1213     err = errno;
1214     
1215     /* update pids, used for internals... */
1216     if (0==ret)
1217     {
1218         ndrx_dbg_pid_update();
1219     }
1220     
1221     return ret;
1222     
1223 #else
1224     
1225     ndrx_atfork_prepare();
1226     
1227     ret = fork();
1228     err = errno;
1229     
1230     if (0==ret)
1231     {
1232         ndrx_atfork_child();
1233     }
1234     else
1235     {
1236         ndrx_atfork_parent();
1237     }
1238     
1239     errno = err;
1240     
1241     return ret;
1242 #endif
1243 }
1244 
1245 /**
1246  * If expecting to continue to use initialized Enduro/X after forking,
1247  * then fork shall be done with this function.
1248  * @param prepare callback to parent
1249  * @param parent parent after fork callback
1250  * @param child child after fork callack
1251  * @return EXSUCCEED/EXFAIL
1252  */
1253 expublic int ndrx_atfork(void (*prepare)(void), void (*parent)(void),
1254        void (*child)(void))
1255 {
1256 #ifdef USE_STOCK_FORKING
1257     return pthread_atfork(prepare, parent, child);
1258 #else
1259     int i=0;
1260     int ret = EXSUCCEED;
1261 
1262     for (i=0;i<MAX_ATFORKS;i++)
1263     {
1264         
1265         /* If all equals just return OK */
1266         
1267         if (prepare==M_prepare[i] && 
1268                 parent==M_parent[i] &&
1269                 child==M_child[i]
1270             )
1271         {
1272             /* called twice with the same data */
1273             goto out;
1274         }
1275         
1276         if (NULL==M_prepare[i] && 
1277                 NULL==M_parent[i] &&
1278                 NULL==M_child[i]
1279             )
1280         {
1281             break;
1282         }
1283     }
1284     
1285     if (i==MAX_ATFORKS)
1286     {
1287         errno=ENOMEM;
1288         EXFAIL_OUT(ret);
1289     }
1290     
1291     M_prepare[i] = prepare;
1292     M_parent[i] = parent;
1293     M_child[i] = child;
1294     
1295 out:
1296     return ret;
1297 #endif
1298 }
1299 
1300 /**
1301  * Return user queues or semaphores
1302  * @param[out] list to init & grow
1303  * @param res_type [in] Return user queues, otherwise return semaphores
1304  * @return EXSUCCEED/EXFAIL
1305  */
1306 expublic int ndrx_sys_sysv_user_res(ndrx_growlist_t *list, int res_type)
1307 {
1308     char cmd[128];
1309     FILE *fp=NULL;
1310     char path[PATH_MAX];
1311     char linematchstr[PATH_MAX];
1312     int ret = EXSUCCEED;
1313     regex_t linematch;
1314     int linematch_comp = EXFALSE;
1315     int sig_set = EXFALSE;
1316     struct sigaction act;
1317     
1318     /* init growlist */
1319     ndrx_growlist_init(list, 256, sizeof(mdrx_sysv_res_t));
1320     
1321     if (NDRX_SV_RESTYPE_QUE == res_type)
1322     {
1323         NDRX_STRCPY_SAFE(cmd, "ipcs -q");
1324         /* output example (LINUX):
1325 
1326         $ ipcs -q
1327         ------ Message Queues --------
1328         key        msqid      owner      perms      used-bytes   messages    
1329         0x00000000 1190428672 user1      700        0            0           
1330         0x00000000 1159593985 user1      700        0            0           
1331         0x00000000 15368194   user1      760        0            0  
1332         ...
1333         
1334         (AIX):
1335         $ ipcs -s
1336         IPC status from /dev/mem as of Thu Oct 18 12:56:29 WET 2018
1337         T        ID     KEY        MODE       OWNER    GROUP
1338         Semaphores:
1339         s   2097152 0x58002281 --ra-ra-ra-     root   system
1340         s         1 0x20002281 --ra-ra-ra-     root   system
1341         s   1048578 0x7a0c584a --ra-------   zabbix   zabbix
1342         s         3 0x620000a3 --ra-r--r--     root   system
1343         
1344         (FREEBSD):
1345         Semaphores:
1346         T           ID          KEY MODE        OWNER    GROUP   
1347         s     24969217   1297266887 --rw-rw-r-- user1    user1   
1348         s     21430274   1297266911 --rw-rw-r-- user1    user1   
1349         s     21168131   1297266936 --rw-rw-r-- user1    user1   
1350         s     21168132   1297266983 --rw-rw-r-- user1    user1   
1351         s     21168133   1297266993 --rw-rw-r-- user1    user1   
1352 
1353 
1354         (HPUX):
1355         $ ipcs
1356         IPC status from /dev/kmem as of Thu Oct 18 12:59:33 2018
1357         T         ID     KEY        MODE        OWNER     GROUP
1358         Message Queues:
1359         q          0 0x3c1c080d -Rrw--w--w-      root      root
1360         q          1 0x3e1c080d --rw-r--r--      root      root
1361         q 1070596098 0x00000000 -Rrw-rw----      toor      toor
1362         
1363         (MACOS)
1364         Shared Memory: 
1365         T ID     KEY        MODE        OWNER GROUP 
1366         m 131071 1095910432 --rw-rw-rw- root kpl 
1367         m 131071 1627522010 --rw-rw---- kpl kpl 
1368         m 131071 1644299226 --rw-rw---- kpl kpl 
1369         m 262143 1661076442 --rw-rw---- kpl kpl 
1370         
1371         So from above we can say that MACOS/AIX/FREEBSD/HPUX have the same layout
1372         */
1373     }
1374     else if (NDRX_SV_RESTYPE_SEM == res_type)
1375     {
1376         NDRX_STRCPY_SAFE(cmd, "ipcs -s");
1377         /* output example:
1378         $ ipcs -s
1379 
1380         ------ Semaphore Arrays --------
1381         key        semid      owner      perms      nsems     
1382         0x0052e2c1 0          user1      600        17        
1383         0x0052e2c2 32769      user1      600        17        
1384         0x0052e2c3 65538      user1      600        17        
1385         0x0052e2c4 98307      user1      600        17          
1386         ...
1387          */
1388     }
1389     else if (NDRX_SV_RESTYPE_SHM == res_type)
1390     {
1391         NDRX_STRCPY_SAFE(cmd, "ipcs -m");
1392     }
1393 #ifdef EX_OS_LINUX
1394     snprintf(linematchstr, sizeof(linematchstr), "^0x[0-9a-fA-F]+\\s*[0-9]+\\s*%s\\s",
1395             ndrx_sys_get_cur_username());
1396 #else
1397     snprintf(linematchstr, sizeof(linematchstr), "^.[ \\t\\r\\n\\v\\f]+[0-9]+[ \\t\\r\\n\\v\\f]+[x0-9a-fA-F]*[ \\t\\r\\n\\v\\f]+.{11}[ \\t\\r\\n\\v\\f]+%s[ \\t\\r\\n\\v\\f]",
1398             ndrx_sys_get_cur_username());
1399 #endif
1400     
1401     if (EXSUCCEED!=ndrx_regcomp(&linematch, linematchstr))
1402     {
1403         userlog("Failed to compile regexp: %s", linematch);
1404         NDRX_LOG(log_error, "Failed to compile regexp: %s", linematch);
1405         EXFAIL_OUT(ret);
1406     }
1407     else
1408     {
1409         linematch_comp = EXTRUE;
1410     }
1411     
1412     NDRX_LOG(log_debug, "Listing resources by: [%s]", cmd);
1413     
1414     /* reset to default action */
1415     sigaction(SIGCHLD, NULL, &act);
1416     signal(SIGCHLD, SIG_DFL);
1417     sig_set=EXTRUE;
1418     
1419     fp = popen(cmd, "r");
1420     
1421     if (fp == NULL)
1422     {
1423         NDRX_LOG(log_warn, "failed to run command [%s]: %s", cmd, strerror(errno));
1424         goto out;
1425     }
1426     
1427     while (fgets(path, sizeof(path)-1, fp) != NULL)
1428     {        
1429         if (EXSUCCEED==ndrx_regexec(&linematch, path))
1430         {
1431             int id;
1432             int key;
1433             int len = strlen(path);
1434             mdrx_sysv_res_t res;
1435             
1436             if (len > 0 && '\n' == path[len-1])
1437             {
1438                 path[len-1]=EXEOS;
1439             }
1440             
1441             NDRX_LOG(log_debug, "Line matched: [%s]", path);
1442             
1443             /* extract second column... valid for Linux and Unix */
1444             if (1!=ndrx_tokens_extract(path, "%d", &id, sizeof(id), 1, 1, 1))
1445             {
1446                 NDRX_LOG(log_error, "Failed to extract resource id from [%s]!",
1447                         path);
1448                 userlog("Failed to extract resource id from [%s]!",
1449                         path);
1450                 EXFAIL_OUT(ret);
1451             }
1452          
1453             NDRX_LOG(log_debug, "Extract id %u", id);
1454             
1455 #if EX_OS_LINUX
1456       
1457             /* first column is key */
1458             if (1!=ndrx_tokens_extract(path, "%x", &key, sizeof(key), 1, 0, 0))
1459             {
1460                 NDRX_LOG(log_error, "Failed to extract resource key from [%s]!",
1461                         path);
1462                 userlog("Failed to extract resource key from [%s]!",
1463                         path);
1464                 EXFAIL_OUT(ret);
1465             }
1466          
1467             NDRX_LOG(log_debug, "Extract key %u", key);
1468 #else
1469             
1470             /* first column is key */
1471             if (1!=ndrx_tokens_extract(path, "%x", &key, sizeof(key), 1, 2, 2))
1472             {
1473                 NDRX_LOG(log_error, "Failed to extract resource key from [%s]!",
1474                         path);
1475                 userlog("Failed to extract resource key from [%s]!",
1476                         path);
1477                 EXFAIL_OUT(ret);
1478             }
1479          
1480             NDRX_LOG(log_debug, "Extract key %u", key);
1481             
1482 #endif
1483             
1484             /* extract a key... */
1485             res.id=id;
1486             res.key = key;
1487             res.restyp=res_type;
1488             /* Add resource to growlist */
1489             if (EXSUCCEED!=ndrx_growlist_append(list, (void *)&res))
1490             {
1491                 NDRX_LOG(log_error, "Failed to add %u/%u to growlist!", id, key);
1492                 userlog("Failed to add %u/%u to growlist!", id, key);
1493                 EXFAIL_OUT(ret);
1494             }
1495         }
1496     }
1497     
1498  out:
1499     /* close */
1500     if (fp!=NULL)
1501     {
1502         pclose(fp);
1503     }
1504  
1505     if (sig_set)
1506     {
1507         sigaction(SIGCHLD, &act, NULL);
1508     }
1509  
1510     if (EXSUCCEED!=ret)
1511     {
1512         ndrx_growlist_free(list);
1513     }
1514  
1515     if (linematch_comp)
1516     {
1517         ndrx_regfree(&linematch);
1518     }
1519 
1520     return ret;
1521 }
1522 
1523 /* vim: set ts=4 sw=4 et smartindent: */