Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Execute client processes (start, stop and signal handling...)
0003  *
0004  * @file cltexec.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 <memory.h>
0038 #include <sys/param.h>
0039 #include <sys_mqueue.h>
0040 #include <sys/resource.h>
0041 #include <sys/wait.h>
0042 #include <libxml/xmlreader.h>
0043 #include <errno.h>
0044 #include <signal.h>
0045 #include <unistd.h>
0046 #include <sys/stat.h>
0047 #include <sys/types.h>
0048 #include <fcntl.h>
0049 
0050 #include <ndrstandard.h>
0051 #include <userlog.h>
0052 #include <atmi.h>
0053 #include <libndrxconf.h>
0054 #include <ndrxdiag.h>
0055 
0056 #include "cpmsrv.h"
0057 #include "../libatmisrv/srv_int.h"
0058 /*---------------------------Externs------------------------------------*/
0059 /*---------------------------Macros-------------------------------------*/
0060 
0061 /**
0062  * Support #459 
0063  * locked debug use signal-thread
0064  * Fact is if we forking, we might get lock on localtime_r
0065  * by signal thread, and the forked process will try to debug
0066  * but will be unable because there will be left over lock on localtime_r
0067  * from the signal thread which no more exists after the fork.
0068  */
0069 #define LOCKED_DEBUG(lev, fmt, ...) MUTEX_LOCK_V(M_forklock);\
0070                                     NDRX_LOG(lev, fmt, ##__VA_ARGS__);\
0071                                     MUTEX_UNLOCK_V(M_forklock);
0072 
0073 /*---------------------------Enums--------------------------------------*/
0074 /*---------------------------Typedefs-----------------------------------*/
0075 /*---------------------------Globals------------------------------------*/
0076 /*---------------------------Statics------------------------------------*/
0077 exprivate pthread_t M_signal_thread; /* Signalled thread */
0078 exprivate int M_signal_thread_set = EXFALSE; /* Signal thread is set */
0079 exprivate MUTEX_LOCKDECL(M_forklock);       /**< forking lock, no q ops during fork!  */
0080 exprivate volatile int M_shutdown = EXFALSE; /**< Doing shutdown    */
0081 /*---------------------------Prototypes---------------------------------*/
0082 /**
0083  * process child
0084  * @param chldpid child pid
0085  * @param stat_loc exit status
0086  */
0087 exprivate void handle_child(pid_t chldpid, int stat_loc)
0088 {
0089     cpm_process_t * c = cpm_get_client_by_pid(chldpid);
0090     
0091     /* Bug #108 01/04/2015, mvitolin
0092     * If config file is changed by foreground thread in this time,
0093     * then we must synchronize with them.
0094     */
0095    cpm_lock_config();
0096    
0097    if (NULL!=c)
0098    {
0099        c->dyn.cur_state = CLT_STATE_NOTRUN;
0100 
0101        /* these bellow use some debug and time funcs
0102         * thus lock all
0103         */
0104        MUTEX_LOCK_V(M_forklock);
0105        /* update shared memory to stopped... */
0106        ndrx_cltshm_setpos(c->key, EXFAIL, NDRX_CPM_MAP_WASUSED, NULL);
0107 
0108        c->dyn.exit_status = stat_loc;
0109        /* Set status change time */
0110        /* have some lock due to time use... */
0111        cpm_set_cur_time(c);
0112        MUTEX_UNLOCK_V(M_forklock);
0113    }
0114 
0115    cpm_unlock_config(); /* we are done... */
0116 }
0117 
0118 /**
0119  * Checks for child exit.
0120  * We will let mainthread to do all internal struct related work!
0121  * @return Got child exit
0122  */
0123 /**
0124  * Checks for child exit.
0125  * We will let mainthread to do all internal struct related work!
0126  * @return Got child exit
0127  */
0128 exprivate void * check_child_exit(void *arg)
0129 {
0130     pid_t chldpid;
0131     int stat_loc;
0132     sigset_t blockMask;
0133     int sig;
0134     struct rusage rusage;
0135     int old;
0136     
0137     pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
0138     
0139     sigemptyset(&blockMask);
0140     sigaddset(&blockMask, SIGCHLD);
0141     
0142     LOCKED_DEBUG(log_debug, "check_child_exit - enter...");
0143     while (!M_shutdown)
0144     {
0145 /* seems not working on darwin ... thus just wait for pid.
0146  * if we do not have any childs, then sleep for 1 sec.
0147  */
0148 #if !(EX_OS_DARWIN && EX_LSB_RELEASE_VER_MAJOR<23)
0149         LOCKED_DEBUG(log_debug, "about to sigwait()");
0150         
0151         /* Wait for notification signal */
0152         if (EXSUCCEED!=sigwait(&blockMask, &sig))
0153         {
0154             LOCKED_DEBUG(log_warn, "sigwait failed:(%s)", strerror(errno));
0155 
0156         }        
0157 #endif
0158         
0159         if (M_shutdown)
0160         {
0161             break;
0162         }
0163         
0164         LOCKED_DEBUG(log_debug, "about to wait()");
0165         
0166 #if (EX_OS_DARWIN && EX_LSB_RELEASE_VER_MAJOR<23)
0167         /* waitpid cancel enabled...
0168          * sleep if no childs
0169          *  */
0170         while (1)
0171         {
0172             chldpid = (pid_t)waitpid(-1, &stat_loc, WUNTRACED);
0173             int err;
0174             
0175             if (EXFAIL==chldpid)
0176             {
0177                 if (err!=ECHILD)
0178                 {
0179                     userlog("waitpid failed: %s", tpstrerror(err));
0180                 }
0181                 sleep(1);
0182             }
0183             else
0184             {
0185                 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
0186                 handle_child(chldpid, stat_loc);
0187                 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
0188             }
0189         }       
0190 #else
0191         while ((chldpid = wait3(&stat_loc, WNOHANG|WUNTRACED, &rusage)) > 0)
0192         {
0193             handle_child(chldpid, stat_loc);
0194         }
0195 #endif
0196         
0197     }
0198    
0199     LOCKED_DEBUG(log_debug, "check_child_exit terminated");
0200     
0201     return NULL;
0202 }
0203 
0204 /**
0205  * Initialize polling lib
0206  * not thread safe.
0207  * @return EXSUCCEED/EXFAIL
0208  */
0209 expublic int cpm_sigchld_init(void)
0210 {
0211     int ret = EXSUCCEED;
0212     pthread_attr_t pthread_custom_attr;
0213     char *fn = "cpm_sigchld_init";
0214 
0215     NDRX_LOG(log_debug, "%s - enter", fn);
0216     
0217     pthread_attr_init(&pthread_custom_attr);
0218     
0219     /* set some small stacks size, 1M should be fine! */
0220     ndrx_platf_stack_set(&pthread_custom_attr);
0221     if (EXSUCCEED!=pthread_create(&M_signal_thread, &pthread_custom_attr, 
0222             check_child_exit, NULL))
0223     {
0224         NDRX_PLATF_DIAG(NDRX_DIAG_PTHREAD_CREATE, errno, "cpm_sigchld_init");
0225         EXFAIL_OUT(ret);
0226     }
0227 
0228     M_signal_thread_set = EXTRUE;
0229 out:
0230     return ret;
0231 }
0232 
0233 
0234 /**
0235  * Un-initialize sigchild monitor thread
0236  * @return
0237  */
0238 expublic void cpm_sigchld_uninit(void)
0239 {
0240     char *fn = "cpm_sigchld_uninit";
0241     int err;
0242     NDRX_LOG(log_debug, "%s - enter", fn);
0243     
0244     if (!M_signal_thread_set)
0245     {
0246         NDRX_LOG(log_debug, "Signal thread was not initialised, nothing todo...");
0247         goto out;
0248     }
0249 
0250 
0251     NDRX_LOG(log_debug, "About to cancel signal thread");
0252     
0253     /* TODO: have a counter for number of sets, so that we can do 
0254      * un-init...
0255      */
0256     M_shutdown = EXTRUE;
0257     
0258 #if (EX_OS_DARWIN && EX_LSB_RELEASE_VER_MAJOR<23)
0259     if (EXSUCCEED!=pthread_cancel(M_signal_thread))
0260     {
0261         NDRX_LOG(log_error, "Failed to kill poll signal thread: %s", strerror(errno));
0262     }
0263 #else
0264     if (EXSUCCEED!=(err=pthread_kill(M_signal_thread, SIGCHLD)))
0265     {
0266         NDRX_LOG(log_error, "Failed to kill poll signal thread: %s", strerror(err));
0267     }
0268 #endif
0269     else
0270     {
0271         if (EXSUCCEED!=pthread_join(M_signal_thread, NULL))
0272         {
0273             NDRX_LOG(log_error, "Failed to join pthread_join() signal thread: %s", 
0274                     strerror(errno));
0275         }
0276     }
0277     
0278     M_signal_thread_set = EXFALSE;
0279     NDRX_LOG(log_debug, "finished ok");
0280 out:
0281     return;
0282 }
0283 
0284 /**
0285  * Perform test on PID
0286  * @param c client process
0287  * @param sg_groups singleton groups
0288  */
0289 expublic void cpm_pidtest(cpm_process_t *c, int *sg_groups)
0290 {
0291     /* 
0292      * if group lock is lost, kill immediatelly 
0293      */
0294     if (NULL!=sg_groups 
0295         && c->stat.procgrp_no>0 
0296         && ndrx_ndrxconf_procgroups_is_singleton(ndrx_G_procgroups_config, 
0297                 c->stat.procgrp_no)
0298         && !sg_groups[c->stat.procgrp_no-1] && 
0299             CLT_STATE_STARTED==c->dyn.cur_state)
0300     {
0301         NDRX_LOG(log_error, "Singleton process group %d lock lost for "
0302                 "%s/%s pid %d, killing immediatelly", 
0303                 c->stat.procgrp_no, c->tag, c->subsect, (int)c->dyn.pid);
0304         kill(c->dyn.pid, SIGKILL);
0305     }
0306 
0307     if (CLT_STATE_STARTED==c->dyn.cur_state && c->dyn.shm_read)
0308     {
0309         /* check the pid status, as we might be booted with existing
0310          * shared memory, thus we do not get any sig childs...
0311          * if we requested the stop, assume exit ok
0312          * if not requested, assume died
0313          */
0314         if (!ndrx_sys_is_process_running_by_pid(c->dyn.pid))
0315         {
0316             NDRX_LOG(log_info, "Process [%s]/%d exited by pid test",
0317                     c->stat.command_line, (int)c->dyn.pid);
0318             /* update shared memory to stopped... */
0319             ndrx_cltshm_setpos(c->key, EXFAIL, NDRX_CPM_MAP_WASUSED, NULL);
0320 
0321             if (CLT_STATE_NOTRUN==c->dyn.req_state )
0322             {   
0323                 c->dyn.exit_status = 0;
0324             }
0325             else
0326             {
0327                 c->dyn.exit_status = EXFAIL;
0328             }
0329 
0330             c->dyn.cur_state = CLT_STATE_NOTRUN;
0331 
0332             /* Set status change time */
0333             cpm_set_cur_time(c);
0334         }
0335     }
0336 }
0337 
0338 /**
0339  * Killall client running
0340  * @return SUCCEED
0341  */
0342 expublic int cpm_killall(void)
0343 {
0344     int ret = EXSUCCEED;
0345     cpm_process_t *c = NULL, *ct = NULL;
0346     int is_any_running;
0347     ndrx_stopwatch_t t;
0348     char *sig_str[3]={"SIGINT","SIGTERM", "SIGKILL"};
0349     int sig[3]={SIGINT,SIGTERM, SIGKILL};
0350     int i;
0351     int was_chld_kill;
0352     string_list_t* cltchildren = NULL;
0353     
0354     for (i=0; i<3; i++)
0355     {
0356         NDRX_LOG(log_warn, "Terminating all with %s", sig_str[i]);
0357 
0358         EXHASH_ITER(hh, G_clt_config, c, ct)
0359         {
0360             /* 
0361              * still check the pid, not? If running from shared mem blocks?
0362              */
0363             cpm_pidtest(c, NULL);
0364             
0365             if (CLT_STATE_STARTED==c->dyn.cur_state)
0366             {
0367                 NDRX_LOG(log_warn, "Killing: %s/%s/%d with %s",
0368                     c->tag, c->subsect, c->dyn.pid, sig_str[i]);
0369                 
0370                 
0371                 /* if we kill with -9, then kill all childrent too
0372                  * this is lengthly operation, thus only for emergency kill only
0373                  */
0374                 was_chld_kill = EXFALSE;
0375                 if ((SIGKILL==sig[i] && (c->stat.flags & CPM_F_KILL_LEVEL_LOW)) ||
0376                         c->stat.flags & CPM_F_KILL_LEVEL_HIGH)
0377                 {
0378                     was_chld_kill = EXTRUE;
0379                     ndrx_proc_children_get_recursive(&cltchildren, c->dyn.pid);
0380                 }
0381                 
0382                 kill(c->dyn.pid, sig[i]);
0383                 
0384                 if (was_chld_kill)
0385                 {
0386                     ndrx_proc_kill_list(cltchildren);
0387                     ndrx_string_list_free(cltchildren);
0388                     cltchildren=NULL;
0389                 }
0390             } /* for client in hash */
0391         } /* for attempt */
0392 
0393         if (i<2) /*no wait for kill... */
0394         {
0395             /* is_any_running = EXFALSE;*/
0396             ndrx_stopwatch_reset(&t);
0397             do
0398             {
0399                 is_any_running = EXFALSE;
0400                 EXHASH_ITER(hh, G_clt_config, c, ct)
0401                 {
0402                     if (CLT_STATE_STARTED==c->dyn.cur_state)
0403                     {
0404                         is_any_running = EXTRUE;
0405                         break;
0406                     }
0407                 }
0408 
0409                 if (is_any_running)
0410                 {
0411                     usleep(CLT_STEP_INTERVAL_ALL);
0412                 }
0413             }
0414             while (is_any_running && 
0415                     ndrx_stopwatch_get_delta_sec(&t) < G_config.kill_interval);
0416         }
0417     }
0418     
0419     NDRX_LOG(log_debug, "cpm_killall done");
0420     return EXSUCCEED;
0421 }
0422 
0423 /**
0424  * Kill the process...
0425  * Firstly -2, then -15, then -9
0426  * We shall do kill in synchronous mode.
0427  * 
0428  * @param c
0429  * @return 
0430  */
0431 expublic int cpm_kill(cpm_process_t *c)
0432 {
0433     int ret = EXSUCCEED;
0434     ndrx_stopwatch_t t;
0435     string_list_t* cltchildren = NULL;
0436         
0437     NDRX_LOG(log_warn, "Stopping %s/%s - %s", c->tag, c->subsect, c->stat.command_line);
0438             
0439     /* INT interval */
0440     if (c->stat.flags & CPM_F_KILL_LEVEL_HIGH)
0441     {
0442         ndrx_proc_children_get_recursive(&cltchildren, c->dyn.pid);
0443     }
0444     
0445     kill(c->dyn.pid, SIGINT);
0446     
0447     if (c->stat.flags & CPM_F_KILL_LEVEL_HIGH)
0448     {
0449         ndrx_proc_kill_list(cltchildren);
0450         ndrx_string_list_free(cltchildren);
0451         cltchildren=NULL;
0452     }
0453     
0454     ndrx_stopwatch_reset(&t);
0455     do
0456     {
0457         if (CLT_STATE_STARTED==c->dyn.cur_state)
0458         {
0459             usleep(CLT_STEP_INTERVAL);
0460         }
0461     } while (CLT_STATE_STARTED==c->dyn.cur_state && 
0462             ndrx_stopwatch_get_delta_sec(&t) < G_config.kill_interval);
0463     
0464     if (CLT_STATE_STARTED!=c->dyn.cur_state)
0465         goto out;
0466     
0467     NDRX_LOG(log_warn, "%s/%s Did not react on SIGINT, continue with SIGTERM", 
0468             c->tag, c->subsect);
0469     
0470     /* TERM interval */
0471     if (c->stat.flags & CPM_F_KILL_LEVEL_HIGH)
0472     {
0473         ndrx_proc_children_get_recursive(&cltchildren, c->dyn.pid);
0474     }
0475     
0476     kill(c->dyn.pid, SIGTERM);
0477     
0478     if (c->stat.flags & CPM_F_KILL_LEVEL_HIGH)
0479     {
0480         ndrx_proc_kill_list(cltchildren);
0481         ndrx_string_list_free(cltchildren);
0482         cltchildren=NULL;
0483     }
0484     
0485     
0486     ndrx_stopwatch_reset(&t);
0487     do
0488     {
0489         /* if running from shared memory, do the check... */
0490         
0491         cpm_pidtest(c, NULL);
0492         if (CLT_STATE_STARTED==c->dyn.cur_state)
0493         {
0494             usleep(CLT_STEP_INTERVAL);
0495         }
0496     } while (CLT_STATE_STARTED==c->dyn.cur_state && 
0497             ndrx_stopwatch_get_delta_sec(&t) < G_config.kill_interval);
0498     
0499     if (CLT_STATE_STARTED!=c->dyn.cur_state)
0500         goto out;
0501     
0502     NDRX_LOG(log_warn, "%s/%s Did not react on SIGTERM, kill with -9", 
0503                         c->tag, c->subsect);
0504     
0505     /* KILL interval */
0506     
0507     /* OK we are here to kill -9, then we shall killall children processes too */
0508     
0509     /* if we kill with -9, then kill all children too
0510      * this is lengthly operation, thus only for emergency kill only 
0511      */
0512     
0513     if (c->stat.flags & CPM_F_KILL_LEVEL_LOW)
0514     {
0515         ndrx_proc_children_get_recursive(&cltchildren, c->dyn.pid);
0516     }
0517     
0518     kill(c->dyn.pid, SIGKILL);
0519     
0520     if (c->stat.flags & CPM_F_KILL_LEVEL_LOW)
0521     {
0522         ndrx_proc_kill_list(cltchildren);
0523         ndrx_string_list_free(cltchildren);
0524         cltchildren=NULL;
0525     }
0526 
0527     ndrx_stopwatch_reset(&t);
0528     do
0529     {
0530         if (CLT_STATE_STARTED==c->dyn.cur_state)
0531         {
0532             usleep(CLT_STEP_INTERVAL);
0533       
0534         }
0535     }
0536     while (CLT_STATE_STARTED==c->dyn.cur_state && 
0537             ndrx_stopwatch_get_delta_sec(&t) < G_config.kill_interval);
0538     
0539     if (CLT_STATE_STARTED!=c->dyn.cur_state)
0540         goto out;
0541     
0542     NDRX_LOG(log_warn, "%s/%s Did not react on SIGKILL, giving up...", 
0543                         c->tag, c->subsect);
0544     
0545 out:
0546     return ret;
0547 }
0548 
0549 /**
0550  * Boot the client
0551  * @param c
0552  * @return 
0553  */
0554 expublic int cpm_exec(cpm_process_t *c)
0555 {
0556     pid_t pid;
0557     char cmd_str[PATH_MAX];
0558     char *cmd[PATH_MAX]; /* splitted pointers.. */
0559     char *token;
0560     int numargs = 0;
0561     int fd_stdout;
0562     int fd_stderr;
0563     int fd;
0564     int ret = EXSUCCEED;
0565     char tmp[256];
0566 
0567     NDRX_LOG(log_warn, "*********processing for startup %s *********", 
0568             c->stat.command_line);
0569     
0570     c->dyn.was_started = EXTRUE; /* We tried to start...                */
0571     c->dyn.shm_read = EXFALSE;  /* We try to boot it, not attached      */
0572     /* clone our self */
0573     MUTEX_LOCK_V(M_forklock);
0574     pid = ndrx_fork();
0575 
0576     /* MUTEX_UNLOCK_V(M_forklock); mutex is not valid for child...
0577      * thus unlock bellow...
0578     */
0579     if( pid == 0)
0580     {
0581         /* close parent resources... Bug #176 
0582          * this will be closed by ndrx_atfork handler
0583         atmisrv_un_initialize(EXTRUE);*/
0584         
0585         /* reset signal handler so that for new processes there is scratch start */
0586         signal(SIGCHLD, SIG_DFL);
0587 
0588         /* some small delay so that parent gets time for PIDhash setup! */
0589         usleep(9000);
0590         
0591         
0592         /* unset server specific env variables... */
0593         
0594         unsetenv(CONF_NDRX_SVPROCNAME);
0595         unsetenv(CONF_NDRX_SVCLOPT);
0596         unsetenv(CONF_NDRX_SVPPID);
0597         unsetenv(CONF_NDRX_SVSRVID);
0598                 
0599         /* Add env variables for NDRX_CLTTAG / NDRX_CLTSUBSECT */
0600         
0601         /* Fixes for #367 */
0602         if (EXSUCCEED!=setenv(NDRX_CLTTAG, c->tag, EXTRUE))
0603         {
0604             NDRX_LOG(log_error, "Cannot set [%s] to [%s]: %s", 
0605                     NDRX_CLTTAG, c->tag, strerror(errno));
0606             userlog("Cannot set [%s] to [%s]: %s", 
0607                     NDRX_CLTTAG, c->tag, strerror(errno));
0608             exit(1);
0609         }
0610         
0611         /* Fixes for #367 */
0612         if (EXSUCCEED!=setenv(NDRX_CLTSUBSECT, c->subsect, EXTRUE))
0613         {
0614             NDRX_LOG(log_error, "Cannot set [%s] to [%s]: %s", 
0615                     NDRX_CLTSUBSECT, c->subsect, strerror(errno));
0616             userlog("Cannot set [%s] to [%s]: %s", 
0617                     NDRX_CLTSUBSECT, c->subsect, strerror(errno));
0618             exit(1);
0619         }
0620 
0621         if (c->stat.procgrp_no > 0)
0622         {
0623             snprintf(tmp, sizeof(tmp), "%d", c->stat.procgrp_no);
0624             if (EXSUCCEED!=setenv(CONF_NDRX_PROCGRP_NO, tmp, EXTRUE))
0625             {
0626                 NDRX_LOG(log_error, "Cannot set [%s] to [%s]: %s", 
0627                         CONF_NDRX_PROCGRP_NO, c->tag, strerror(errno));
0628                 userlog("Cannot set [%s] to [%s]: %s", 
0629                         CONF_NDRX_PROCGRP_NO, c->tag, strerror(errno));
0630                 exit(1);
0631             }
0632         }
0633 
0634         NDRX_STRCPY_SAFE(cmd_str, c->stat.command_line);
0635 
0636         token = ndrx_strtokblk(cmd_str, NDRX_CMDLINE_SEP, NDRX_CMDLINE_QUOTES);
0637         while( token != NULL )
0638         {
0639             cmd[numargs] = token;
0640             token = ndrx_strtokblk( NULL, NDRX_CMDLINE_SEP, NDRX_CMDLINE_QUOTES);
0641             numargs++;
0642         }
0643         cmd[numargs] = NULL;
0644     
0645         /*  Override environment, if there is such thing */
0646         if (EXEOS!=c->stat.env[0])
0647         {
0648             if (EXSUCCEED!=ndrx_load_new_env(c->stat.env))
0649             {
0650                 userlog("Failed to load custom env from: %s!", c->stat.env);
0651                 exit(1);
0652             }
0653         }
0654         
0655         if (EXEOS!=c->stat.cctag[0])
0656         {
0657             if (EXSUCCEED!=setenv(NDRX_CCTAG, c->stat.cctag, EXTRUE))
0658             {
0659                 userlog("Cannot set [%s] to [%s]: %s", 
0660                         NDRX_CCTAG, c->stat.cctag, strerror(errno));
0661                 exit(1);
0662             }
0663         }
0664         
0665         /* load envs from xml config */
0666         if (EXSUCCEED!=ndrx_ndrxconf_envs_apply(c->stat.envs))
0667         {
0668             userlog("Cannot load XML env for %s/%s: %s", 
0669                     NDRX_CCTAG, c->tag,c->subsect, strerror(errno));
0670             exit(1);
0671         }
0672         
0673         /* Change working dir */
0674         if (EXEOS!=c->stat.wd[0])
0675         {
0676             if (EXSUCCEED!=chdir(c->stat.wd))
0677             {
0678                 int err = errno;
0679                 
0680                 NDRX_LOG(log_error, "Failed to change working diretory: %s - %s!", 
0681                         c->stat.wd, strerror(err));
0682                 userlog("Failed to change working diretory: %s - %s!", 
0683                         c->stat.wd, strerror(err));
0684                 exit(1);
0685             }
0686         }
0687         
0688         /* make stdout go to file */
0689         if (EXEOS!=c->stat.log_stdout[0] &&
0690                 EXFAIL!=(fd_stdout = open(c->stat.log_stdout, 
0691                O_WRONLY| O_CREAT  | O_APPEND, S_IRUSR | S_IWUSR)))
0692         {
0693             dup2(fd_stdout, 1); 
0694             close(fd_stdout);
0695         }
0696         
0697         if (EXEOS!=c->stat.log_stderr[0] &&
0698                 EXFAIL!=(fd_stderr = open(c->stat.log_stderr, 
0699                 O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)))
0700         {
0701             dup2(fd_stderr, 2);   /* make stderr go to file */
0702             close(fd_stderr);
0703         }
0704 
0705         /* close stdin... */
0706         if (EXFAIL!=(fd = open("/dev/null", O_RDWR)))
0707         {
0708             dup2(fd, 0);
0709             close(fd);
0710         }
0711 
0712         if (EXEOS!=c->stat.log_stderr[0] && EXSUCCEED!=setenv(CONF_NDRX_DFLTLOG, 
0713                 c->stat.log_stderr, EXTRUE))
0714         {
0715             userlog("Failed to set client [%s] env: %s", CONF_NDRX_DFLTLOG, strerror(errno));
0716         }
0717 
0718         /**
0719          * todo: set NDRX_DFLTLOG to stderr file, so that logrotate
0720          * can be done on these
0721          */
0722         /* reset signal handlers */
0723         signal(SIGINT, SIG_DFL);
0724         signal(SIGTERM, SIG_DFL);
0725         
0726         if (EXSUCCEED != execvp (cmd[0], cmd))
0727         {
0728             int err = errno;
0729             NDRX_LOG(log_error, "Failed to start client, error: %d, %s", 
0730                     err, strerror(err));
0731             exit (err);
0732         }
0733     }
0734     else if (EXFAIL!=pid)
0735     {
0736         MUTEX_UNLOCK_V(M_forklock);
0737         cpm_set_cur_time(c);
0738         c->dyn.pid = pid;
0739         c->dyn.cur_state = CLT_STATE_STARTED;
0740         
0741         /* extract the procname from command line */
0742         
0743         /* updated shared memory... */
0744         if (EXSUCCEED!=ndrx_cltshm_setpos(c->key, c->dyn.pid, 
0745                 NDRX_CPM_MAP_ISUSED|NDRX_CPM_MAP_WASUSED|NDRX_CPM_MAP_CPMPROC, 
0746                 c->stat.procname))
0747         {
0748             NDRX_LOG(log_error, "Failed to register client in CPM SHM/mem full, check %s param", 
0749                     CONF_NDRX_CLTMAX);
0750             userlog("Failed to register client in CPM SHM/mem full, check %s param", 
0751                     CONF_NDRX_CLTMAX);
0752         }   
0753     }
0754     else
0755     {
0756         MUTEX_UNLOCK_V(M_forklock);
0757         userlog("Failed to fork: %s", strerror(errno));
0758     }
0759     
0760 out:
0761     return ret;
0762 }
0763 /* vim: set ts=4 sw=4 et smartindent: */