File:  [Coherent Logic Development] / freem / src / mumps.c
Revision 1.10: download - view: text, annotated - select for diffs
Tue Apr 1 20:11:46 2025 UTC (6 months, 1 week ago) by snw
Branches: MAIN
CVS tags: HEAD
Further work on fmadm

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

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