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