File:  [Coherent Logic Development] / freem / src / mumps.c
Revision 1.22: download - view: text, annotated - select for diffs
Tue Apr 15 16:49:36 2025 UTC (3 months, 2 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Make use of logprintf throughout codebase

    1: /*
    2:  *   $Id: mumps.c,v 1.22 2025/04/15 16:49:36 snw Exp $
    3:  *    main module of freem
    4:  *
    5:  *  
    6:  *   Author: Serena Willis <snw@coherent-logic.com>
    7:  *    Copyright (C) 1998 MUG Deutschland
    8:  *    Copyright (C) 2020, 2025 Coherent Logic Development LLC
    9:  *
   10:  *
   11:  *   This file is part of FreeM.
   12:  *
   13:  *   FreeM is free software: you can redistribute it and/or modify
   14:  *   it under the terms of the GNU Affero Public License as published by
   15:  *   the Free Software Foundation, either version 3 of the License, or
   16:  *   (at your option) any later version.
   17:  *
   18:  *   FreeM is distributed in the hope that it will be useful,
   19:  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
   20:  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   21:  *   GNU Affero Public License for more details.
   22:  *
   23:  *   You should have received a copy of the GNU Affero Public License
   24:  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
   25:  *
   26:  *   $Log: mumps.c,v $
   27:  *   Revision 1.22  2025/04/15 16:49:36  snw
   28:  *   Make use of logprintf throughout codebase
   29:  *
   30:  *   Revision 1.21  2025/04/15 02:24:43  snw
   31:  *   Improve FreeM logging capabilities
   32:  *
   33:  *   Revision 1.20  2025/04/13 04:22:43  snw
   34:  *   Fix snprintf calls
   35:  *
   36:  *   Revision 1.19  2025/04/10 01:24:38  snw
   37:  *   Remove C++ style comments
   38:  *
   39:  *   Revision 1.18  2025/04/09 19:52:02  snw
   40:  *   Eliminate as many warnings as possible while building with -Wall
   41:  *
   42:  *   Revision 1.17  2025/04/04 21:28:16  snw
   43:  *   Remove custom_user and custom_group vars from freem and shed privileges per environment catalog settings
   44:  *
   45:  *   Revision 1.16  2025/04/04 19:43:18  snw
   46:  *   Switch to using environment catalog to determine user and group for environment, and remove -u and -g flags from freem
   47:  *
   48:  *   Revision 1.15  2025/04/03 20:48:14  snw
   49:  *   Improve daemon error diagnostics and bump to 0.63.0-rc3
   50:  *
   51:  *   Revision 1.14  2025/04/02 04:50:49  snw
   52:  *   Allow vendor routines to be upgraded
   53:  *
   54:  *   Revision 1.13  2025/04/02 03:26:22  snw
   55:  *   Don't corrupt the terminal if FreeM runs before fmadm configure has been run
   56:  *
   57:  *   Revision 1.12  2025/04/02 02:16:27  snw
   58:  *   Add fmadm status environment command and move journals to a better location
   59:  *
   60:  *   Revision 1.11  2025/04/01 23:21:45  snw
   61:  *   fmadm commands for stopping, starting, and restarting environments now functional
   62:  *
   63:  *   Revision 1.10  2025/04/01 20:11:46  snw
   64:  *   Further work on fmadm
   65:  *
   66:  *   Revision 1.9  2025/03/31 20:01:13  snw
   67:  *   Set d_uid in daemon
   68:  *
   69:  *   Revision 1.8  2025/03/24 16:10:48  snw
   70:  *   Print error message and exit on OS/2 if daemon is run without --nofork
   71:  *
   72:  *   Revision 1.7  2025/03/24 16:07:55  snw
   73:  *   Force daemon into foreground on OS/2
   74:  *
   75:  *   Revision 1.6  2025/03/24 16:04:49  snw
   76:  *   Force daemon into foreground on OS/2
   77:  *
   78:  *   Revision 1.5  2025/03/22 21:44:32  snw
   79:  *   Make the startup messages fewer and add environment name to direct-mode prompt
   80:  *
   81:  *   Revision 1.4  2025/03/09 19:50:47  snw
   82:  *   Second phase of REUSE compliance and header reformat
   83:  *
   84:  *
   85:  * SPDX-FileCopyrightText:  (C) 2025 Coherent Logic Development LLC
   86:  * SPDX-License-Identifier: AGPL-3.0-or-later
   87:  **/
   88: 
   89: #define _GNU_SOURCE
   90: #include <stdlib.h>
   91: #include <stddef.h>
   92: #include "mpsdef.h"
   93: #include "errmsg.h"
   94: #include "iniconf.h"
   95: #include "namespace.h"
   96: #include "transact.h"
   97: #include "init.h"
   98: #include "consttbl.h"
   99: #include <setjmp.h>
  100: #include <stdio.h>
  101: #include <signal.h>
  102: #include <time.h>
  103: #include <unistd.h>
  104: #include <sys/types.h>
  105: #include <sys/wait.h>
  106: #include <sys/stat.h>
  107: #include <limits.h>
  108: #include <sys/types.h>
  109: #include <pwd.h>
  110: #include <string.h>
  111: #include <fcntl.h>
  112: #include "version.h"
  113: #include "shmmgr.h"
  114: #include "jobtab.h"
  115: #include <errno.h>
  116: #include <pwd.h>
  117: #include <grp.h>
  118: #include "log.h"
  119: 
  120: #if defined(HAVE_GETOPT_H)
  121: # include <getopt.h>
  122: #endif
  123: 
  124: #if !defined(PATH_MAX) && defined(_SCO_DS)
  125: # define PATH_MAX 1024
  126: #endif
  127: 
  128: extern int xecline(int typ);
  129: extern char *getenv(const char *name);
  130: void freem_usage(void);
  131: void freem_print_version(void);
  132: void init_ztrap(void);
  133: 
  134: int main (int argc, char **argv, char **envp)
  135: {
  136:     pid_t fork_pid = 0;
  137:     short dx_mode = 0;
  138:     
  139:     int c;
  140:     int import_env = FALSE;
  141:     short skip_init = 0;
  142:     
  143:     int option_index = 0;
  144: 
  145:     char fm_initialized = FALSE;
  146:     
  147:     char dx_mcode[512];
  148:     char startup_routine[256];
  149:     short routine_mode;
  150:     char m_dialect[50];
  151: 
  152:     char nsnbuf[256];
  153: 
  154:     char d_username[40];
  155:     char d_groupname[40];
  156:     struct group *d_grp;
  157:     struct passwd *d_user;
  158:     gid_t d_gid;
  159:     uid_t d_uid;
  160: 
  161: #if defined(HAVE_GETOPT_LONG)
  162:     struct option long_options[] = {       
  163:         {"help", no_argument, 0, 'h'},
  164:         {"filter", no_argument, &frm_filter, TRUE},
  165:         {"standard", required_argument, 0, 's'},
  166:         {"import", no_argument, &import_env, TRUE},
  167:         {"quiet", no_argument, &quiet_mode, TRUE},
  168:         {"restricted", no_argument, &restricted_mode, TRUE},
  169:         {"routine", required_argument, 0, 'r'},
  170:         {"namespace", required_argument, 0, 'n'},
  171:         {"environment", required_argument, 0, 'e'},
  172: 	{"version", no_argument, 0, 'v'},
  173: 	{"execute", required_argument, 0, 'x'},
  174:         {"daemon", no_argument, 0, 'd'},
  175:         {"nofork", no_argument, 0, 'k'},
  176:         {"shmsize", required_argument, 0, 'S'},
  177:         {"user", required_argument, 0, 'u'},
  178:         {"group", required_argument, 0, 'g'},
  179:         {0, 0, 0, 0}
  180:     };
  181: #endif
  182:     
  183:     char **env;      
  184:     char *varname = (char *) calloc(STRLEN, sizeof(char));
  185:     char *varval = (char *) calloc(STRLEN, sizeof(char));
  186: 
  187:     char *symname = (char *) calloc(STRLEN, sizeof(char));
  188:     char *symval = (char *) calloc(STRLEN, sizeof(char));
  189:     
  190:     char cli_rtn_path[PATH_MAX] = {0};
  191:     char *cli_rtn_file;
  192:     char cli_rtn_name[256];
  193: 
  194:     char env_ena[25];
  195:     
  196:     routine_mode = FALSE;
  197:     strcpy (m_dialect, "FREEM");
  198:     
  199:     m_argc = argc;			/* save arguments count     */
  200:     m_argv = argv;			/* save arguments string    */
  201:     m_envp = envp;			/* save environment pointer */
  202: 
  203:     strncpy (shm_env, "DEFAULT", 8);    /* establish default environment name */
  204:     
  205:     strncpy (d_username, "freem", 40);
  206:     strncpy (d_groupname, "freem", 40);
  207:     
  208:     strcpy (zb, argv[0]);		/* name with which mumps has been called */
  209:     stcnv_c2m (zb);
  210:     strcpy (stack0, argv[0]);
  211:     stcnv_c2m (stack0);
  212: 
  213: 
  214:     /* strncpy (config_file, SYSCONFDIR"/freem.conf", 100); */
  215: 
  216: 
  217: 
  218: #if defined(HAVE_GETOPT_LONG)
  219:     
  220:     extern char *optarg;
  221:     extern int optind, optopt;
  222:     
  223:     while (1) {
  224:         
  225:         c = getopt_long (argc, argv, "hsfiqRr:n:e:vx:dkpS", long_options, &option_index);
  226: 
  227:         if (c == -1) break;
  228: 	if (c == '?') freem_usage ();
  229: 	
  230:         switch (c) {
  231:             case 'h':
  232:                 freem_usage ();
  233:                 break;
  234: 
  235:             case 'f':
  236:                 frm_filter = TRUE;
  237:                 break;
  238: 
  239:             case 'i':
  240:                 import_env = TRUE;
  241:                 break;
  242: 
  243:             case 'q':
  244:                 quiet_mode = TRUE;
  245:                 break;
  246: 
  247:             case 'e': /* specify FreeM environment */
  248:                 strncpy (shm_env, optarg, sizeof (shm_env) - 1);
  249:                 break;
  250: 
  251:             case 'R':
  252:                 restricted_mode = TRUE;
  253:                 break;
  254: 
  255:             case 'r': /* startup routine */
  256:                 direct_mode = FALSE;
  257:                 usermode = 0;
  258: 
  259:                 strcpy (startup_routine, optarg);
  260:                 startup_routine[strlen (startup_routine)] = '\201';
  261: 
  262:                 routine_mode = TRUE;
  263:                 break;
  264: 
  265:             case 'n': /* namespace */
  266:             {
  267:                 if (validate_namespace (optarg) == TRUE) {                
  268:                     strcpy (nsname, optarg);
  269:                 }
  270:                 else {
  271:                     fprintf (stderr, "freem:  invalid namespace '%s'\n", optarg);
  272:                     exit (1);
  273:                 }
  274:                 
  275:                 break;
  276:             }
  277: 	    case 'v': /* version */
  278: 		freem_print_version ();
  279: 		break;
  280: 
  281: 	    case 'x': /* execute */
  282: 		direct_mode = FALSE;
  283: 		usermode = 0;
  284: 		dx_mode = 1;
  285: 		
  286: 		strncpy (dx_mcode, optarg, 512 - 1);
  287: 
  288: 		stcnv_c2m (dx_mcode);
  289: 
  290: 		break;
  291: 
  292:             case 's': /* --standard */
  293: 
  294:                 if (strcmp (optarg, "M77") == 0) {
  295:                     standard = D_M77;
  296:                     strcpy (m_dialect, "M 1977");
  297:                 }
  298:                 else if (strcmp (optarg, "M84") == 0) {
  299:                     standard = D_M84;
  300:                     strcpy (m_dialect, "M 1984");
  301:                 }
  302:                 else if (strcmp (optarg, "M90") == 0) {
  303:                     standard = D_M90;
  304:                     strcpy (m_dialect, "M 1990");
  305:                 }
  306:                 else if (strcmp (optarg, "M95") == 0) {
  307:                     standard = D_M95;
  308:                     strcpy (m_dialect, "M 1995");
  309:                 }
  310:                 else if (strcmp (optarg, "MDS") == 0) {
  311:                     standard = D_MDS;
  312:                     strcpy (m_dialect, "Millennium Draft Standard");
  313:                 }
  314:                 else if (strcmp (optarg, "M5") == 0) {
  315:                     standard = D_M5;
  316:                     strcpy (m_dialect, "M5");
  317:                 }
  318:                 else if (strcmp (optarg, "FREEM") == 0) {
  319:                     standard = D_FREEM;
  320:                     strcpy (m_dialect, "FREEM");
  321:                 }
  322:                 else {
  323:                     freem_usage ();
  324:                 }
  325:                 
  326:                 break;                
  327: 
  328:             case 'd': /* --daemon */
  329:                 run_daemon = TRUE;                
  330:                 break;
  331: 
  332:             case 'k': /* --nofork */
  333: 		nofork = TRUE;
  334:                 break;
  335: 
  336:             case 'S': /* --shmsize */
  337:                 shm_init_size = atol (optarg);
  338:                 break;                
  339: 
  340:         } 
  341: 
  342:     }
  343: 
  344: #else
  345:     
  346:     {
  347:         extern char *optarg;
  348:         extern int optind, optopt;
  349: 
  350:         while ((c = getopt (argc, argv, "hsfiqRr:n:e:vx:dkS:")) != -1) {
  351: 
  352: 	    if (c == '?') freem_usage ();
  353: 	    
  354:             switch (c) {
  355: 
  356: 		case 'h':
  357: 		    freem_usage ();
  358: 		    break;
  359: 		
  360:                 case 'f':
  361:                     frm_filter = TRUE;
  362:                     break;
  363: 
  364:                 case 'i':
  365:                     import_env = TRUE;
  366:                     break;
  367: 
  368:                 case 'q':
  369:                     quiet_mode = TRUE;
  370:                     break;
  371: 
  372:                 case 'e':  /* specify FreeM environment */
  373:                     strncpy (shm_env, optarg, sizeof (shm_env) - 1);
  374:                     break;
  375: 
  376:                 case 'R':
  377:                     restricted_mode = TRUE;
  378:                     break;
  379:                     
  380:                 case 'r': /* startup routine */
  381:                     direct_mode = FALSE;
  382:                     usermode = 0;
  383: 
  384:                     strcpy (startup_routine, optarg);
  385:                     startup_routine[strlen (startup_routine)] = '\201';
  386: 
  387:                     routine_mode = TRUE;
  388:                     break;
  389: 
  390:                 case 'n': /* namespace */
  391:                     strcpy (nsname, optarg);               
  392:                     break;
  393: 
  394: 		case 'v':
  395: 		    freem_print_version ();
  396: 		    break;
  397: 
  398: 		case 'x': /* execute */
  399: 		    direct_mode = FALSE;
  400: 		    usermode = 0;
  401: 		    dx_mode = 1;
  402: 		    
  403: 		    strncpy (dx_mcode, optarg, 512 - 1);
  404: 		    
  405: 		    stcnv_c2m (dx_mcode);
  406: 
  407:                     break;
  408:                     
  409:                 case 'd': /* --daemon */
  410:                     run_daemon = TRUE;
  411:                     break;
  412: 
  413:                 case 'k': /* --nofork */
  414:                     nofork = TRUE;
  415:                     break;
  416: 
  417:                 case 's': /* --standard */
  418:                     
  419:                     if (strcmp (optarg, "M77") == 0) {
  420:                         standard = D_M77;
  421:                         strcpy (m_dialect, "M 1977");
  422:                     }
  423:                     else if (strcmp (optarg, "M84") == 0) {
  424:                         standard = D_M84;
  425:                         strcpy (m_dialect, "M 1984");
  426:                     }
  427:                     else if (strcmp (optarg, "M90") == 0) {
  428:                         standard = D_M90;
  429:                         strcpy (m_dialect, "M 1990");
  430:                     }
  431:                     else if (strcmp (optarg, "M95") == 0) {
  432:                         standard = D_M95;
  433:                         strcpy (m_dialect, "M 1995");
  434:                     }
  435:                     else if (strcmp (optarg, "MDS") == 0) {
  436:                         standard = D_MDS;
  437:                         strcpy (m_dialect, "Millennium Draft Standard");
  438:                     }
  439:                     else if (strcmp (optarg, "M5") == 0) {
  440:                         standard = D_M5;
  441:                         strcpy (m_dialect, "M5");
  442:                     }
  443:                     else if (strcmp (optarg, "FREEM") == 0) {
  444:                         standard = D_FREEM;
  445:                         strcpy (m_dialect, "FREEM");
  446:                     }
  447:                     else {
  448:                         freem_usage ();
  449:                     }
  450:                 
  451:                     break;                
  452: 
  453:                     
  454:                 case 'S': /* --shmsize */
  455:                     shm_init_size = atol (optarg);
  456:                     break;
  457: 
  458:             } 
  459:         }
  460:     }
  461: #endif
  462: 
  463: #if defined(__OS2__)
  464:     if (run_daemon == TRUE && nofork == FALSE) {
  465:         printf ("freem:  running on OS/2; daemon must be run with --nofork or -k\r\n");
  466:         exit (1);
  467:     }
  468: #endif
  469:     
  470:     snprintf (config_file, sizeof (config_file) - 1, "%s/freem/%s/freem.conf", SYSCONFDIR, shm_env);
  471:     snprintf (env_config_file, sizeof (config_file) - 1, "%s/freem/env.conf", SYSCONFDIR);    
  472:     
  473:     if (!file_exists (env_config_file)) {
  474:         logprintf (FM_LOG_FATAL, "freem:  environment catalog does not exist; may need to run fmadm configure");       
  475:     }
  476: 
  477:     if (!file_exists (config_file)) {
  478:         logprintf (FM_LOG_FATAL, "freem:  configuration file for %s does not exist; may need to run fmadm configure", shm_env);
  479:     }
  480: 
  481:     if (read_profile_string (env_config_file, shm_env, "user", d_username) == FALSE) {
  482:         logprintf (FM_LOG_FATAL, "freem:  could not determine owning user for environment %s", shm_env);
  483:     }
  484: 
  485:     if (read_profile_string (env_config_file, shm_env, "group", d_groupname) == FALSE) {
  486:         logprintf (FM_LOG_FATAL, "freem:  could not determine owning group for environment %s", shm_env);
  487:     }
  488: 
  489:     if (read_profile_string (env_config_file, shm_env, "enabled", env_ena) == FALSE) {
  490:         logprintf (FM_LOG_FATAL, "freem:  could not discover enabled state for environment %s", shm_env);     
  491:     }
  492: 
  493:     if (strcmp (env_ena, "true") != 0) {
  494:         logprintf (FM_LOG_FATAL, "freem:  environment %s is administratively disabled", shm_env);
  495:     }    
  496: 
  497:     d_grp = getgrnam (d_groupname);        
  498:     if (d_grp == NULL) {
  499:         logprintf (FM_LOG_FATAL, "freem:  invalid group '%s'", d_groupname);
  500:     }            
  501:     d_gid = d_grp->gr_gid;
  502:         
  503:     d_user = getpwnam (d_username);
  504:     if (d_user == NULL) {
  505:         logprintf (FM_LOG_FATAL, "freem:  invalid user '%s'", d_username);
  506:     }            
  507:     d_uid = d_user->pw_uid;
  508: 
  509:     
  510: #if defined(__linux__)    
  511:     if (run_daemon == FALSE && group_member (d_gid) == 0) {
  512:         logprintf (FM_LOG_FATAL, "freem:  you must be a member of the %s group to use environment %s", d_groupname, shm_env);
  513:     }        
  514: #endif    
  515:     
  516:     if (run_daemon == TRUE) {
  517:         if (geteuid () != 0 && nofork == FALSE) {
  518:             logprintf (FM_LOG_FATAL, "freem:  forking daemon must be run as root");
  519:         }                
  520:     }
  521:     
  522:     if ((nofork == TRUE) && (run_daemon == FALSE)) {
  523:         freem_usage ();
  524:         exit (1);
  525:     }
  526: 
  527:     if ((run_daemon == TRUE) && (nofork == FALSE)) {
  528: 
  529:         int fork_fd;
  530: 
  531:         init_log ();
  532:         
  533:         /* daemonize */
  534: 
  535:         fork_pid = fork ();
  536: 
  537:         if (fork_pid < 0) {
  538:             logprintf (FM_LOG_FATAL, "environment error: failure in initial fork()");
  539:         }
  540: 
  541:         if (fork_pid > 0) {
  542:             exit (0);
  543:         }
  544: 
  545:         if (setsid () < 0) {
  546:             logprintf (FM_LOG_FATAL, "environment error: failure in setsid()");
  547:         }
  548: 
  549:         signal (SIGCHLD, SIG_IGN);
  550: 
  551:         fork_pid = fork ();
  552: 
  553:         if (fork_pid < 0) {
  554:             logprintf (FM_LOG_FATAL, "environment error: failure in second fork()");
  555:         }
  556: 
  557:         if (fork_pid > 0) {
  558:             logprintf (FM_LOG_DEBUG, "environment: exiting from second fork()");
  559:             exit (0);
  560:         }
  561: 
  562:         umask (0);
  563: 
  564:         chdir ("/");       
  565: 
  566:         for (fork_fd = sysconf (_SC_OPEN_MAX); fork_fd > 0; fork_fd--) {
  567:             close (fork_fd);
  568:         }
  569:         
  570:         if (geteuid () == 0) {
  571: 
  572:             /* shed privileges */
  573:             logprintf (FM_LOG_INFO, "environment:  switching to group %s", d_groupname);
  574:                 
  575:             if (setgid (d_gid) == -1) {
  576:                 logprintf (FM_LOG_FATAL, "environment error:  failure switching GID");
  577:             }
  578:         
  579: 
  580: 
  581:             if (d_uid != geteuid ()) {
  582: 
  583:                 logprintf (FM_LOG_INFO, "environment:  switching to username %s", d_username);
  584:                 
  585:                 if (setuid (d_uid) == -1) {
  586:                     logprintf (FM_LOG_FATAL, "environment error: failure switching UID");
  587:                 }
  588:             }
  589: 
  590:         }
  591:         else {
  592:             logprintf (FM_LOG_INFO, "environment:  not running as superuser; not shedding privileges"); 
  593:         }
  594: 
  595: 	freopen ("/dev/null", "r", stdin);
  596: 	freopen ("/dev/null", "w+", stdout);
  597: 	freopen ("/dev/null", "w+", stderr);
  598: 
  599:         run_daemon = TRUE;
  600:         nofork = FALSE;        
  601:         
  602:         {
  603:             char pidfile_buf[256];
  604:             int errsav;
  605: 
  606:             pid_file_path = (char *) malloc (PATH_MAX * sizeof (char));
  607:             NULLPTRCHK(pid_file_path,"main");
  608:             
  609:             snprintf (pid_file_path, PATH_MAX - 1, "%s/freem/run/%s.pid", LOCALSTATEDIR, shm_env);
  610: 
  611:             pid_fd = open (pid_file_path, O_RDWR | O_CREAT, 0640);
  612:             errsav = errno;
  613:             
  614:             if (pid_fd < 0) {
  615:                 logprintf (FM_LOG_FATAL, "environment error:  could not open PID file %s [%s]", pid_file_path, strerror (errsav));
  616:             }
  617: 
  618:             if (lockf (pid_fd, F_TLOCK, 0) < 0) {
  619:                 errsav = errno;
  620:                 logprintf (FM_LOG_FATAL, "environment error:  could not lock PID file [%s]", strerror (errsav));
  621:             }
  622: 
  623:             sprintf (pidfile_buf, "%ld\n", (long) getpid ());
  624:             write (pid_fd, pidfile_buf, strlen (pidfile_buf));
  625: 
  626:         }
  627:        
  628:         
  629:     } /* END of daemonization */
  630:     
  631:     /* handle passing of an arbitrary .m file on the command line */
  632:     /* this is most often used for shebang-line scripts. */
  633:     if (optind < argc) {
  634: 
  635:         /* not valid for daemon mode */
  636:         if (run_daemon == TRUE) {
  637:             fprintf (stderr, "freem:  cannot pass --daemon flag in shebang line\r\n");
  638:             exit (1);
  639:         }
  640:         
  641:         /* bail if file does not exist */
  642:         if (access (argv[optind], F_OK) == -1) {
  643: 
  644:             set_io (UNIX);
  645:             
  646:             fprintf (stderr, "Routine %s does not exist.\n", argv[optind]);
  647: 
  648:             exit (1);
  649: 
  650:         }
  651: 
  652: 	skip_init = 1;
  653: 
  654: 	/* initialize FreeM environment */
  655: 	strncpy (nsnbuf, nsname, sizeof (nsnbuf));    
  656: 	if (init (nsnbuf) == FALSE) {
  657: 	    
  658: 	    if (fm_initialized) set_io (UNIX);
  659: 	    fprintf (stderr, "\nError initializing FreeM.\n");
  660: 	    
  661: 	    exit (1);            
  662: 	    
  663: 	}
  664:         else {
  665:             fm_initialized = TRUE;
  666:         }
  667: 
  668: 	
  669:         direct_mode = FALSE;
  670:         usermode = 0;
  671: 
  672:         /* was a path specified at all? */
  673:         if (strchr (argv[optind], '/') == NULL) {
  674: 	    
  675:             /* the entirety of argv[optind] is the filename */
  676:             cli_rtn_file = argv[optind];            
  677: 
  678:             /* use the current directory */
  679:             sprintf (cli_rtn_path, ".");
  680: 
  681:         }
  682:         else {
  683: 
  684:             /* isolate the filename from the path */
  685:             cli_rtn_file = strrchr (argv[optind], '/') + 1;
  686:             
  687:             /* isolate the routine name from the filename */
  688:             strncpy (cli_rtn_name, cli_rtn_file, strchr (cli_rtn_file, '.') - cli_rtn_file);
  689:             
  690:             /* isolate the path from the routine file */
  691:             strncpy (cli_rtn_path, argv[optind], strrchr (argv[optind], '/') - argv[optind]);      
  692: 	    
  693:         }
  694: 
  695:         /* do we have a file extension? */
  696:         if (strchr (cli_rtn_file, '.') != NULL) {
  697:             
  698:             /* if so, just remove it */
  699:             strncpy (cli_rtn_name, cli_rtn_file, strchr (cli_rtn_file, '.') - cli_rtn_file);
  700: 
  701:         }
  702:         else {
  703: 
  704:             /* otherwise, just take a direct copy */
  705:             strcpy (cli_rtn_name, cli_rtn_file);
  706: 
  707:         }
  708: 
  709:         /* make this the startup routine */
  710:         snprintf (startuprou, sizeof (startuprou) - 1, "^%s\201", cli_rtn_name);
  711: 
  712:         /* re-work the namespace config to search for the 
  713:            routine in the discovered path */
  714:         if (cli_rtn_name[0] == '%') {
  715: 
  716:             snprintf (rou0plib, sizeof (rou0plib) - 1, "%s\201", cli_rtn_path);
  717:             snprintf (rou1plib, sizeof (rou1plib) - 1, "%s\201", cli_rtn_path);
  718: 
  719:         }
  720:         else {
  721: 
  722:             snprintf (rou0path, sizeof (rou0path) - 1, "%s\201", cli_rtn_path);
  723:             snprintf (rou1path, sizeof (rou1path) - 1, "%s\201", cli_rtn_path);
  724: 
  725:         }
  726: 
  727:     }
  728: 
  729:     
  730:     if (!file_exists (config_file)) {
  731: 
  732:         if (fm_initialized == TRUE) set_io (UNIX);
  733:         
  734:         fprintf (stderr, "\nFreeM has not been configured. Please run 'fmadm configure'.\n\n\n\n");
  735:         
  736:         exit (2);
  737: 
  738:     }
  739: 
  740:     if (!skip_init) {
  741: 	/* initialize FreeM environment */
  742: 	strncpy (nsnbuf, nsname, sizeof (nsnbuf));    
  743: 	if (init (nsnbuf) == FALSE) {	    
  744: 	    set_io (UNIX);
  745: 	    fprintf (stderr, "\nError initializing FreeM.\n");
  746: 
  747: 	    exit (1);
  748: 	}
  749:         else {
  750:             fm_initialized = TRUE;
  751:         }
  752:     }
  753: 
  754:     if (first_process == TRUE) {
  755:         
  756:         char verstr[500];
  757:         pid_t stop_requester;
  758: 
  759:         if (run_daemon == FALSE) {
  760:             logprintf (FM_LOG_FATAL, "freem:  re-run with --daemon or -d command-line flags");
  761:         }
  762:         
  763:         stcpy (verstr, FREEM_VERSION_STR);
  764:         stcnv_m2c (verstr);
  765:         
  766:         fprintf (stderr, "Coherent Logic Development FreeM version %s\r\n", verstr);
  767:         logprintf (FM_LOG_INFO, "freem:  shared memory for environment %s initialized (%ld bytes of shared memory @ '%p')", shm_env, (long) shm_init_size, shm_config->dta);
  768:         
  769:         for (;;) {
  770: 
  771:             job_set_status (pid, JSTAT_HOUSEKEEPING);
  772:             
  773: 
  774:             if (shm_config->hdr->maintenance_mode == 1) {
  775: 
  776:                 job_slot_t *slot;
  777: 
  778:                 logprintf (FM_LOG_WARNING, "environment:  entering maintenance mode");
  779:                 
  780:                 for (slot = shm_config->hdr->jobtab_head; slot != NULL; slot = slot->next) {
  781: 
  782:                     if ((slot->pid != pid) && ((slot->flags & JFLG_FMADM) != JFLG_FMADM)) {
  783:                         kill (slot->pid, SIGINT);
  784:                     }
  785: 
  786:                 }
  787:                 
  788:             }
  789:             
  790:             if ((stop_requester = job_stop_requested (pid)) != 0) {
  791:                 int connected_jobs;
  792:                 
  793:                 job_set_status (pid, JSTAT_SHUTDOWN);
  794:                 
  795:                 connected_jobs = job_count ();
  796:                 
  797:                 logprintf (FM_LOG_INFO, "environment:  STOP requested by pid %ld", stop_requester);
  798:                 logprintf (FM_LOG_INFO, "environment:  there are %d job(s) connected to this environment", connected_jobs);
  799:                 
  800:                 if (connected_jobs > 1) {
  801:                     
  802:                     logprintf (FM_LOG_INFO, "environment:  asking non-daemon job(s) to disconnect and halt...");
  803:                     job_request_all_stop ();
  804: 
  805:                     logprintf (FM_LOG_INFO, "environment:  waiting 5 seconds for job(s) to disconnect...");
  806:                     sleep (5);
  807: 
  808:                     connected_jobs = job_count ();
  809:                     if (connected_jobs > 1) {                        
  810:                         logprintf (FM_LOG_INFO, "environment:  sending SIGTERM to %d job(s)...", connected_jobs);
  811:                         job_signal_all (SIGTERM);
  812:                         logprintf (FM_LOG_INFO, "environment:  waiting 5 seconds for job(s) to disconnect...");
  813:                         
  814:                         sleep (5);
  815:                     }
  816:                     
  817:                     connected_jobs = job_count ();
  818:                     if (connected_jobs > 1) {
  819:                         logprintf (FM_LOG_INFO, "environment:  sending SIGKILL to %d job(s)...", connected_jobs);
  820:                         job_signal_all (SIGKILL);
  821:                     }
  822:                    
  823:                     job_gc_mark ();
  824:                     job_gc_sweep ();
  825:                     
  826:                 }
  827: 
  828:                 logprintf (FM_LOG_INFO, "freem:  terminating");
  829:                 cleanup ();
  830:                 
  831:                 exit (0);
  832:                 
  833:             }
  834:             
  835:             job_gc_mark ();
  836:             job_set_status (pid, JSTAT_IDLE);
  837:             sleep (1);
  838:             
  839:             job_set_status (pid, JSTAT_HOUSEKEEPING);
  840:             job_gc_sweep ();
  841:             sleep (1);
  842:         }
  843:             
  844:     }
  845:         
  846:     
  847: #if !defined(_AIX)
  848:     if (import_env == TRUE) {
  849: 	int i_maxlen = 255;
  850: 	
  851:         for (env = envp; *env != 0; env++) {	                
  852:             varname = strtok (*env, "=");
  853:             varval = strtok (NULL, "=");
  854: 
  855:             if (varval != NULL) {
  856: 		snprintf (symname, i_maxlen, "ENV.%s\201\201", varname);
  857:                 strncpy (symval, varval, i_maxlen - 1);
  858: 		
  859:                 stcnv_c2m (symval);
  860: 
  861:                 symtab (set_sym, symname, symval);
  862:             }
  863:         }
  864:     }
  865: #endif    
  866: 
  867:     
  868:     if (direct_mode == TRUE && quiet_mode == FALSE) {
  869: 
  870: 	char verstr[500];
  871:         char version[256];
  872: 	
  873: 	stcpy (verstr, FREEM_VERSION_STR);
  874: 	stcnv_m2c (verstr);
  875: 	        
  876:         snprintf (version, sizeof (version) - 1, "\r\nCoherent Logic Development FreeM version %s [DIALECT: %s%s]\r\n\201", verstr, m_dialect, (restricted_mode == TRUE ? "/RESTRICTED" : ""));
  877: 	write_m (version);
  878: 
  879: 	snprintf (version, sizeof (version) - 1, "Copyright (C) 2014, 2020, 2021, 2023, 2025 Coherent Logic Development LLC\r\n\r\n\201");
  880: 	write_m (version);
  881: 
  882:         /*
  883:         printf ("Environment:       \t%s\r\n", shm_env);
  884:         printf ("Environment Daemon:\tPID %d\r\n", shm_config->hdr->first_process);
  885:         printf ("Interpreter Process:\tPID %d\r\n", pid);
  886:         */
  887: 
  888:     }
  889:     else {
  890:         write_m ("\r\n\r\n\201");
  891:     }
  892: 
  893:     if (dx_mode) {
  894: 	char k_buf[512];	
  895: 
  896: 	snprintf (k_buf, sizeof (k_buf) - 1, "%%TMPINITMCODE\201\201");
  897: 	symtab (set_sym, k_buf, dx_mcode);
  898:         const_define (k_buf, dx_mcode);
  899:     }
  900: 
  901:     if (routine_mode) {
  902:         char k_buf[512];
  903: 
  904:         snprintf (k_buf, sizeof (k_buf) - 1, "%%TMPINITROUTINE\201\201");
  905:         symtab (set_sym, k_buf, startup_routine);
  906:         const_define (k_buf, startup_routine);
  907:     }
  908:     
  909:     /* run mumps */
  910:     xecline (1);
  911: 
  912:     exit (0);	/* we should never reach that statement */
  913: 
  914: } /* end of main() */
  915: 
  916: void freem_usage(void)
  917: {
  918:     fprintf (stdout, "\nusage:  freem [OPTION...]\n\n");
  919:     
  920:     fprintf (stdout, "OPTIONS:\n\n");
  921:     
  922: #if defined(HAVE_GETOPT_LONG)
  923:     fprintf (stdout, "\t-h, --help\n\t\tdisplays this help message\n\n");
  924:     fprintf (stdout, "\t-i, --import\n\t\timports UNIX environment variables as M locals\n\n");
  925:     fprintf (stdout, "\t-e <environment-name>, --environment=<environment-name>\n\t\tsets active environment to <environment-name> (DEFAULT if unspecified)\n\n");
  926:     fprintf (stdout, "\t-f, --filter\n\t\tallows M code to be used as a filter\n\n");
  927:     fprintf (stdout, "\t-n <NAMESPACE>, --namespace=<NAMESPACE>\n\t\tselects <NAMESPACE> as the startup namespace instead of USER\n\n");
  928:     fprintf (stdout, "\t-q, --quiet\n\t\tdisables startup messages and prompt string\n\n");
  929:     fprintf (stdout, "\t-r <LABEL^ROUTINE>, --routine=<LABEL^ROUTINE>\n\t\texecute <LABEL^ROUTINE> on startup instead of entering direct mode\n\n");
  930:     fprintf (stdout, "\t-s, --standard\n\t\trestrict access to FreeM vendor extensions not present in relevant standards*\n\n");
  931:     fprintf (stdout, "\t-v, --version\n\t\tdisplay FreeM version information\n\n");
  932:     fprintf (stdout, "\t-x <MCODE>, --execute=<MCODE>\n\t\texecute M code <MCODE> on startup\n\n");
  933:     fprintf (stdout, "\t-d, --daemon\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n");
  934:     fprintf (stdout, "\t-k, --nofork\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n");
  935:     fprintf (stdout, "\t-S <BYTES>, --shmsize=<BYTES>\n\t\tsets the size of the shared memory segment where FreeM stores the job table, lock table, and IPC table.\n");
  936: #else
  937:     fprintf (stdout, "\t-h\n\t\tdisplays this help message\n\n");
  938:     fprintf (stdout, "\t-i\n\t\timports UNIX environment variables as M locals\n\n");
  939:     fprintf (stdout, "\t-e <environment-name>\n\t\tsets active environment to <environment-name> (DEFAULT if unspecified)\n\n");
  940:     fprintf (stdout, "\t-f\n\t\tallows M code to be used as a filter\n\n");
  941:     fprintf (stdout, "\t-n <NAMESPACE>\n\t\tselects <NAMESPACE> as the startup namespace instead of USER\n\n");
  942:     fprintf (stdout, "\t-q\n\t\tdisables startup messages and prompt string\n\n");
  943:     fprintf (stdout, "\t-r <LABEL^ROUTINE>\n\t\texecute <LABEL^ROUTINE> on startup instead of entering direct mode\n\n");
  944:     fprintf (stdout, "\t-s\n\t\trestrict access to FreeM vendor extensions not present in relevant standards*\n\n");
  945:     fprintf (stdout, "\t-v\n\t\tdisplay FreeM version information\n\n");
  946:     fprintf (stdout, "\t-x <MCODE>\n\t\texecute M code <MCODE> on startup\n\n");
  947:     fprintf (stdout, "\t-d\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n");
  948:     fprintf (stdout, "\t-k\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n");
  949:     fprintf (stdout, "\t-S <BYTES>\n\t\tsets the size of the shared memory segment where FreeM stores the job table, lock table, and IPC table.\n");
  950: #endif    
  951:     fprintf (stdout, "\t\t  - Each concurrent job takes %d bytes (1 page) of shared memory\n", PG_SIZE);
  952:     fprintf (stdout, "\t\t  - Each LOCK takes %d bytes (2 pages) of shared memory\n", PG_SIZE * 2);
  953:     fprintf (stdout, "\t\t  - Each IPC takes %d bytes (1 page) of shared memory\n\n", PG_SIZE);
  954:     fprintf (stdout, "\t* FreeM attempts to conform (at least loosely) to the Millennium Draft Standard when this mode is selected.\n\n\n");
  955:     fprintf (stdout, "Report bugs to: freem-bugs@coherent-logic.com\n");
  956:     fprintf (stdout, "FreeM home page: <https://freem.coherent-logic.com>\n\n");   
  957:     
  958:     exit (1);
  959: }
  960: 
  961: void freem_print_version(void)
  962: {
  963:     char verstr[500];
  964:     stcpy (verstr, FREEM_VERSION_STR);
  965:     stcnv_m2c (verstr);
  966:     
  967:     fprintf (stdout, "Coherent Logic Development FreeM %s\n", verstr);
  968:     fprintf (stdout, "Copyright (C) 2014, 2020, 2021, 2023 Coherent Logic Development LLC\n\n");
  969:     fprintf (stdout, "License AGPLv3+: GNU AGPL version 3 or later <https://gnu.org/license/agpl-3.0.html>\n");
  970:     fprintf (stdout, "This is free software: you are free to change and redistribute it.\n");
  971:     fprintf (stdout, "There is NO WARRANTY, to the extent permitted by law.\n");
  972:     
  973:     exit (0);
  974: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>