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