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