Back to home page

Enduro/X

 
 

    


0001 /**
0002  * @brief Routines for checking & starting daemon process
0003  *
0004  * @file exec.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 <errno.h>
0039 #include <fcntl.h>
0040 
0041 #include <sys_mqueue.h>
0042 #include <sys/param.h>
0043 #include <sys_mqueue.h>
0044 #include <sys/resource.h>
0045 #include <sys/wait.h>
0046 #include <unistd.h>
0047 #include <fcntl.h>
0048 
0049 #include <ndrstandard.h>
0050 #include <ndebug.h>
0051 
0052 #include "ndrx.h"
0053 #include <atmi_int.h>
0054 #include <sys_unix.h>
0055 #include <userlog.h>
0056 /*---------------------------Externs------------------------------------*/
0057 /*---------------------------Macros-------------------------------------*/
0058 /*---------------------------Enums--------------------------------------*/
0059 /*---------------------------Typedefs-----------------------------------*/
0060 /*---------------------------Globals------------------------------------*/
0061 /*---------------------------Statics------------------------------------*/
0062 /*---------------------------Prototypes---------------------------------*/
0063 
0064 
0065 /**
0066  * NDRXD process got sigterm.
0067  * @return
0068  */
0069 void sign_chld_handler(int sig)
0070 {
0071     pid_t chldpid;
0072     int stat_loc;
0073     struct rusage rusage;
0074 /*    NDRX_LOG(log_warn, "Got sigchld..."); - debug might cause locking?*/
0075 
0076     memset(&rusage, 0, sizeof(rusage));
0077 
0078     if (0!=(chldpid = wait3(&stat_loc, WNOHANG|WUNTRACED, &rusage)))
0079     {
0080 /*        NDRX_LOG(log_warn, "sigchld: PID: %d exit status: %d",
0081                                            chldpid, stat_loc); - this too 
0082         can cause some lockups... */
0083 
0084         /* TODO: If this is last pid, then set state in idle? */
0085         G_config.ndrxd_stat=NDRXD_STAT_NOT_STARTED;
0086 
0087     }
0088     else
0089     {
0090 /*        NDRX_LOG(log_error, "Got sigchild for unknown"); */
0091     }
0092 }
0093 
0094 /**
0095  * Quick probe for ndrxd (for faster startups)
0096  * Just monitors when ndrxd is ready and we can do a full probe.
0097  * @param pid pid to monitor
0098  */
0099 exprivate void ndrx_ndrxd_quick_probe(pid_t pid)
0100 {
0101     int i;
0102     
0103     /* nothing todo: */
0104     if ((mqd_t)EXFAIL!=G_config.ndrxd_q)
0105     {
0106         goto out;
0107     }
0108     
0109     /* probe while pid exists ... */
0110     for (i=0; i<6 && EXSUCCEED==kill(pid, 0); i++)
0111     {
0112         usleep(50000*i);
0113         
0114         G_config.ndrxd_q = ndrx_mq_open_at_wrp (G_config.ndrxd_q_str, O_WRONLY);
0115         
0116         /* in case if open OK, or error other than ENOENT, we do fail. */
0117         if((mqd_t)EXFAIL!=G_config.ndrxd_q || ENOENT!=errno)
0118         {
0119             break;
0120         }   
0121     }
0122     
0123     out:
0124     return;
0125 }
0126 
0127 /**
0128  * Check whether idle instance running?
0129  * @param p_pid PID to return
0130  * @param silent Do not print "not running" message
0131  * @return FALSE - not running
0132  *         TRUE - running or malfunction.
0133  */
0134 expublic int is_ndrxd_running(pid_t *p_pid, int silent)
0135 {
0136     int ret = EXFALSE;
0137     FILE *f = NULL;
0138     pid_t    pid;
0139     char    pidbuf[64] = {EXEOS};
0140     
0141     /* Reset to default - not running! */
0142     G_config.ndrxd_stat = NDRXD_STAT_NOT_STARTED;
0143 
0144     NDRX_LOG(log_info, "Probing for ndrxd...");
0145     
0146     /* Check queue first  */
0147     if ((mqd_t)EXFAIL==G_config.ndrxd_q)
0148         G_config.ndrxd_q = ndrx_mq_open_at_wrp (G_config.ndrxd_q_str, O_WRONLY);
0149 
0150     if ((mqd_t)EXFAIL==G_config.ndrxd_q)
0151     {
0152         if (ENOENT==errno)
0153         {
0154             NDRX_LOG(log_info, "ndrxd queue [%s] not found - continue probing", 
0155                     G_config.ndrxd_q_str);
0156         }
0157         else
0158         {
0159             NDRX_LOG(log_error, "Failed to open ndrxd queue [%s]: %s",
0160                     G_config.ndrxd_q_str, strerror(errno));
0161         }
0162     }
0163     
0164     /*
0165      * We will check:
0166      * - Is PID existing
0167      * - Is the name of the pid correct (check /proc/<PID>/cmdline) - should be ndrxd!
0168      * - Check for command queue
0169      */
0170     if (NULL==(f=NDRX_FOPEN(G_config.pid_file, "r")))
0171     {
0172         if (ENOENT==errno)
0173         {
0174             NDRX_LOG(log_info, "ndrxd PID file [%s] not found - continue probing", 
0175                     G_config.pid_file);
0176         }
0177         else
0178         {
0179             NDRX_LOG(log_error, "Failed to open ndrxd PID file: [%s]: %s",
0180                     G_config.pid_file, strerror(errno));
0181         }
0182         
0183         goto out;
0184     }
0185 
0186     /* Read the PID */
0187     if (NULL==fgets(pidbuf, sizeof(pidbuf), f))
0188     {
0189         NDRX_LOG(log_error, "Failed to read from PID file: [%s]: %s",
0190                 G_config.pid_file, strerror(errno));
0191         goto out;
0192     }
0193 
0194     /* Get pid value */
0195     fprintf(stderr, "* ndrxd PID (from PID file): %s\n", pidbuf);
0196 
0197     NDRX_FCLOSE(f);
0198     f = NULL;
0199 
0200     pid = atoi(pidbuf);
0201 
0202     if (ndrx_sys_is_process_running(pid, "ndrxd"))
0203     {
0204         if ((mqd_t)EXFAIL!=G_config.ndrxd_q)
0205         {
0206             ret=EXTRUE;
0207             G_config.ndrxd_stat = NDRXD_STAT_RUNNING;
0208         }
0209         else
0210         {
0211             /* NOT OK, malfunction */
0212             NDRX_LOG(log_error, "`ndrxd' is running, but queue [%s] is "
0213                     "not open - malfunction!",
0214                     G_config.ndrxd_q_str);
0215             G_config.ndrxd_stat = NDRXD_STAT_MALFUNCTION;
0216         }
0217     }
0218     
0219     if (NULL!=p_pid)
0220     {
0221         *p_pid = pid;
0222     }
0223 
0224 out:
0225 
0226     /* close any  */
0227     if (NULL!=f)
0228         NDRX_FCLOSE(f);
0229 
0230     if (!ret)
0231     {
0232         if (!silent)
0233         {
0234             fprintf(stderr, "* Enduro/X back-end (ndrxd) is not running\n");
0235         }
0236 
0237         if ((mqd_t)EXFAIL!=G_config.ndrxd_q)
0238         {
0239             ndrx_mq_close(G_config.ndrxd_q);
0240             G_config.ndrxd_q = (mqd_t)EXFAIL;
0241             
0242             if (ndrx_chk_ndrxd())
0243             {
0244                 /* Not sure this is safer, but we will remove that queue on behalf of user!
0245                  * if process is not running.
0246                  * Maybe do that if in reality no ndrxd for current user is not running?
0247                  */
0248                 G_config.ndrxd_stat = NDRXD_STAT_MALFUNCTION;
0249             }
0250             else
0251             {
0252                 /* remove resources as ndrxd is not running */
0253                 fprintf(stderr, "ndrxd is not running - system cleanup\n");
0254                 ndrx_mq_unlink(G_config.ndrxd_q_str);
0255             }
0256         }
0257     }
0258     else if ((mqd_t)EXFAIL!=G_config.ndrxd_q)
0259     {
0260         ndrx_mq_close(G_config.ndrxd_q);
0261         G_config.ndrxd_q=(mqd_t)EXFAIL;
0262     }
0263 
0264     NDRX_LOG(log_info, "%s returns: %s", __func__, (ret?"running":"not running"));
0265 
0266     return ret;
0267 }
0268 
0269 /**
0270  * Start idle instance of daemon process
0271  * @return
0272  */
0273 expublic int start_daemon_idle(void)
0274 {
0275     int ret=EXSUCCEED;
0276     pid_t pid;
0277     char    key[NDRX_MAX_KEY_SIZE+3+1];
0278     /* clone our self */
0279     pid = ndrx_fork();
0280     
0281     if( pid == 0)
0282     {
0283         FILE *f;
0284         int fd;
0285 
0286         /* TODO: For System V actually we do not need to close the queues
0287          * as these are not linked to system resources
0288          * then we need a poller extension to check q fork close, something like
0289          * if (ndrx_epoll_forkqclose())
0290          * {
0291          *     ... close the queues ...
0292          * }
0293          * As unnamed pipes still is going to live within the fork
0294          * for just use ifdef...
0295          * The proper way would be to use pthread_atfork() so that
0296          * we can re-init the admin thread. For particular case
0297          * we shall remove any resources allocated by admin thread
0298          * and re-init it again for child.
0299          */
0300         /*Bug #176 close resources */
0301         NDRX_LOG(log_debug, "forked close ndrxd_q %p", (void *)(long)G_config.ndrxd_q);
0302 
0303         if (G_config.ndrxd_q != (mqd_t)EXFAIL)
0304             ndrx_mq_close(G_config.ndrxd_q);
0305 
0306         NDRX_LOG(log_debug, "forked close reply_queue %p", 
0307                 (void *)(long)G_config.reply_queue);
0308         
0309         /* WELL!!! Seems parent gets this close!!! */
0310         if (G_config.reply_queue != (mqd_t)EXFAIL)
0311             ndrx_mq_close(G_config.reply_queue);
0312 
0313         /* this is child - start EnduroX back-end*/
0314         snprintf(key, sizeof(key), NDRX_KEY_FMT, ndrx_get_G_atmi_env()->rnd_key);
0315         char *cmd[] = { "ndrxd", key, (char *)0 };
0316 
0317         /* Open log file */
0318         if (NULL==(f=NDRX_FOPEN(G_config.ndrxd_logfile, "a")))
0319         {
0320             fprintf(stderr, "Failed to open ndrxd log file: %s\n",
0321                     G_config.ndrxd_logfile);
0322         }
0323         else
0324         {
0325         
0326         /* Bug #176 */
0327             if (EXSUCCEED!=fcntl(fileno(f), F_SETFD, FD_CLOEXEC))
0328             {
0329                 userlog("WARNING: Failed to set FD_CLOEXEC: %s", strerror(errno));
0330             }
0331         
0332             /* Redirect stdout, stderr to log file */
0333             close(1);
0334             close(2);
0335             if (EXFAIL==dup(fileno(f)))
0336             {
0337                 userlog("%s: Failed to dup(1): %s", __func__, strerror(errno));
0338             }
0339 
0340             if (EXFAIL==dup(fileno(f)))
0341             {
0342                 userlog("%s: Failed to dup(2): %s", __func__, strerror(errno));
0343             }
0344         }
0345 
0346         /* close stdin... */
0347         if (EXFAIL!=(fd = open("/dev/null", O_RDWR)))
0348         {
0349             dup2(fd, 0);
0350            close(fd);
0351         }
0352 
0353         /* Set new file permissions */
0354         umask(0);
0355         /* Deatch from terminal */
0356         setsid();
0357 
0358         if (EXSUCCEED != execvp ("ndrxd", cmd))
0359         {
0360             fprintf(stderr, "Failed to start server - ndrxd: %s\n",
0361                 strerror(errno));
0362             exit(1);
0363         }
0364     }
0365     else
0366     {
0367         int i;
0368         int started=EXFALSE;
0369         /* this is parent for child, wait 1 sec  
0370         sleep(1);
0371         */
0372         ndrx_ndrxd_quick_probe(pid);
0373         started=is_ndrxd_running(NULL, EXTRUE);
0374 
0375 #define MAX_WSLEEP  5
0376     /* give another 5 seconds... to start ndrxd */
0377     if (!started)
0378     {
0379             for (i=0; i<MAX_WSLEEP; i++)
0380             {
0381                 fprintf(stderr, "* Still not started, waiting %d/%d\n",
0382                             i+2, MAX_WSLEEP+1);
0383                 sleep(1);
0384                 started=is_ndrxd_running(NULL, EXTRUE);
0385                 if (started)
0386                         break;
0387             }
0388         }
0389 
0390         if (started)
0391         {
0392             fprintf(stderr, "* ndrxd idle instance started.\n");
0393             G_config.is_idle = EXTRUE;
0394         }
0395         else if (NDRXD_STAT_NOT_STARTED==G_config.ndrxd_stat)
0396         {
0397             fprintf(stderr, "* ndrxd idle instance not started (something failed?)!\n");
0398         }
0399         else
0400         {
0401             fprintf(stderr, "* ndrxd instance idle malfunction!\n");
0402         }
0403     }
0404     
0405     return ret;
0406 }
0407 
0408 /* vim: set ts=4 sw=4 et smartindent: */