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