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

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

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