Annotation of freem/src/mumps.c, revision 1.28

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

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