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