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