File:  [Coherent Logic Development] / freem / src / mumps.c
Revision 1.28: download - view: text, annotated - select for diffs
Wed May 14 12:22:04 2025 UTC (2 months, 2 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Further work on shared memory

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

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