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