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