|
|
| version 1.2, 2025/02/28 23:28:32 | version 1.18, 2025/04/09 19:52:02 |
|---|---|
| Line 1 | Line 1 |
| /* | /* |
| * * | * $Id$ |
| * * * | |
| * * * | |
| * *************** | |
| * * * * * | |
| * * MUMPS * | |
| * * * * * | |
| * *************** | |
| * * * | |
| * * * | |
| * * | |
| * | |
| * mumps.c | |
| * main module of freem | * main module of freem |
| * | * |
| * | * |
| * Author: Serena Willis <jpw@coherent-logic.com> | * Author: Serena Willis <snw@coherent-logic.com> |
| * Copyright (C) 1998 MUG Deutschland | * Copyright (C) 1998 MUG Deutschland |
| * Copyright (C) 2020 Coherent Logic Development LLC | * Copyright (C) 2020, 2025 Coherent Logic Development LLC |
| * | * |
| * | * |
| * This file is part of FreeM. | * This file is part of FreeM. |
| Line 35 | Line 23 |
| * You should have received a copy of the GNU Affero Public License | * You should have received a copy of the GNU Affero Public License |
| * along with FreeM. If not, see <https://www.gnu.org/licenses/>. | * along with FreeM. If not, see <https://www.gnu.org/licenses/>. |
| * | * |
| * $Log$ | |
| * Revision 1.18 2025/04/09 19:52:02 snw | |
| * Eliminate as many warnings as possible while building with -Wall | |
| * | |
| * Revision 1.17 2025/04/04 21:28:16 snw | |
| * Remove custom_user and custom_group vars from freem and shed privileges per environment catalog settings | |
| * | |
| * Revision 1.16 2025/04/04 19:43:18 snw | |
| * Switch to using environment catalog to determine user and group for environment, and remove -u and -g flags from freem | |
| * | |
| * Revision 1.15 2025/04/03 20:48:14 snw | |
| * Improve daemon error diagnostics and bump to 0.63.0-rc3 | |
| * | |
| * Revision 1.14 2025/04/02 04:50:49 snw | |
| * Allow vendor routines to be upgraded | |
| * | |
| * Revision 1.13 2025/04/02 03:26:22 snw | |
| * Don't corrupt the terminal if FreeM runs before fmadm configure has been run | |
| * | |
| * Revision 1.12 2025/04/02 02:16:27 snw | |
| * Add fmadm status environment command and move journals to a better location | |
| * | |
| * Revision 1.11 2025/04/01 23:21:45 snw | |
| * fmadm commands for stopping, starting, and restarting environments now functional | |
| * | |
| * Revision 1.10 2025/04/01 20:11:46 snw | |
| * Further work on fmadm | |
| * | |
| * Revision 1.9 2025/03/31 20:01:13 snw | |
| * Set d_uid in daemon | |
| * | |
| * Revision 1.8 2025/03/24 16:10:48 snw | |
| * Print error message and exit on OS/2 if daemon is run without --nofork | |
| * | |
| * Revision 1.7 2025/03/24 16:07:55 snw | |
| * Force daemon into foreground on OS/2 | |
| * | |
| * Revision 1.6 2025/03/24 16:04:49 snw | |
| * Force daemon into foreground on OS/2 | |
| * | |
| * Revision 1.5 2025/03/22 21:44:32 snw | |
| * Make the startup messages fewer and add environment name to direct-mode prompt | |
| * | |
| * Revision 1.4 2025/03/09 19:50:47 snw | |
| * Second phase of REUSE compliance and header reformat | |
| * | |
| * | |
| * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC | |
| * SPDX-License-Identifier: AGPL-3.0-or-later | |
| **/ | **/ |
| #define _GNU_SOURCE | |
| #include <stdlib.h> | #include <stdlib.h> |
| #include <stddef.h> | #include <stddef.h> |
| #include "mpsdef.h" | #include "mpsdef.h" |
| Line 93 int main (int argc, char **argv, char ** | Line 131 int main (int argc, char **argv, char ** |
| int option_index = 0; | int option_index = 0; |
| char fm_initialized = FALSE; | |
| char dx_mcode[512]; | char dx_mcode[512]; |
| char startup_routine[256]; | char startup_routine[256]; |
| short routine_mode; | short routine_mode; |
| Line 107 int main (int argc, char **argv, char ** | Line 147 int main (int argc, char **argv, char ** |
| gid_t d_gid; | gid_t d_gid; |
| uid_t d_uid; | uid_t d_uid; |
| short custom_user = FALSE; | |
| short custom_group = FALSE; | |
| #if defined(HAVE_GETOPT_LONG) | #if defined(HAVE_GETOPT_LONG) |
| struct option long_options[] = { | struct option long_options[] = { |
| {"help", no_argument, 0, 'h'}, | {"help", no_argument, 0, 'h'}, |
| Line 125 int main (int argc, char **argv, char ** | Line 162 int main (int argc, char **argv, char ** |
| {"execute", required_argument, 0, 'x'}, | {"execute", required_argument, 0, 'x'}, |
| {"daemon", no_argument, 0, 'd'}, | {"daemon", no_argument, 0, 'd'}, |
| {"nofork", no_argument, 0, 'k'}, | {"nofork", no_argument, 0, 'k'}, |
| {"pidfile", required_argument, 0, 'p'}, | |
| {"shmsize", required_argument, 0, 'S'}, | {"shmsize", required_argument, 0, 'S'}, |
| {"user", required_argument, 0, 'u'}, | {"user", required_argument, 0, 'u'}, |
| {"group", required_argument, 0, 'g'}, | {"group", required_argument, 0, 'g'}, |
| Line 139 int main (int argc, char **argv, char ** | Line 175 int main (int argc, char **argv, char ** |
| char *symname = (char *) calloc(STRLEN, sizeof(char)); | char *symname = (char *) calloc(STRLEN, sizeof(char)); |
| char *symval = (char *) calloc(STRLEN, sizeof(char)); | char *symval = (char *) calloc(STRLEN, sizeof(char)); |
| int namelen; | |
| int vallen; | |
| char cli_rtn_path[PATH_MAX] = {0}; | char cli_rtn_path[PATH_MAX] = {0}; |
| char *cli_rtn_file; | char *cli_rtn_file; |
| char cli_rtn_name[256]; | char cli_rtn_name[256]; |
| char env_ena[25]; | |
| routine_mode = FALSE; | routine_mode = FALSE; |
| strcpy (m_dialect, "FREEM"); | strcpy (m_dialect, "FREEM"); |
| Line 176 int main (int argc, char **argv, char ** | Line 211 int main (int argc, char **argv, char ** |
| while (1) { | while (1) { |
| c = getopt_long (argc, argv, "hsfiqRr:n:e:vx:dkpS:u:g:", long_options, &option_index); | c = getopt_long (argc, argv, "hsfiqRr:n:e:vx:dkpS", long_options, &option_index); |
| if (c == -1) break; | if (c == -1) break; |
| if (c == '?') freem_usage (); | if (c == '?') freem_usage (); |
| Line 287 int main (int argc, char **argv, char ** | Line 322 int main (int argc, char **argv, char ** |
| nofork = TRUE; | nofork = TRUE; |
| break; | break; |
| case 'p': /* --pidfile */ | |
| pid_file_path = strdup (optarg); | |
| break; | |
| case 'S': /* --shmsize */ | case 'S': /* --shmsize */ |
| shm_init_size = atol (optarg); | shm_init_size = atol (optarg); |
| break; | break; |
| case 'u': /* --user */ | |
| strncpy (d_username, optarg, 40); | |
| custom_user = TRUE; | |
| break; | |
| case 'g': /* --group */ | |
| strncpy (d_groupname, optarg, 40); | |
| custom_group = TRUE; | |
| break; | |
| } | } |
| Line 316 int main (int argc, char **argv, char ** | Line 336 int main (int argc, char **argv, char ** |
| extern char *optarg; | extern char *optarg; |
| extern int optind, optopt; | extern int optind, optopt; |
| while ((c = getopt (argc, argv, "hsfiqRr:n:e:vx:dkpS:u:g:")) != -1) { | while ((c = getopt (argc, argv, "hsfiqRr:n:e:vx:dkS:")) != -1) { |
| if (c == '?') freem_usage (); | if (c == '?') freem_usage (); |
| Line 383 int main (int argc, char **argv, char ** | Line 403 int main (int argc, char **argv, char ** |
| nofork = TRUE; | nofork = TRUE; |
| break; | break; |
| case 'p': /* --pidfile */ | |
| pid_file_path = strdup (optarg); | |
| break; | |
| case 's': /* --standard */ | case 's': /* --standard */ |
| if (strcmp (optarg, "M77") == 0) { | if (strcmp (optarg, "M77") == 0) { |
| Line 428 int main (int argc, char **argv, char ** | Line 444 int main (int argc, char **argv, char ** |
| shm_init_size = atol (optarg); | shm_init_size = atol (optarg); |
| break; | break; |
| case 'u': /* --user */ | |
| strncpy (d_username, optarg, 40); | |
| custom_user = TRUE; | |
| break; | |
| case 'g': /* --group */ | |
| strncpy (d_groupname, optarg, 40); | |
| custom_group = TRUE; | |
| break; | |
| } | } |
| } | } |
| } | } |
| #endif | #endif |
| #if defined(__OS2__) | |
| if (run_daemon == TRUE && nofork == FALSE) { | |
| printf ("freem: running on OS/2; daemon must be run with --nofork or -k\r\n"); | |
| exit (1); | |
| } | |
| #endif | |
| snprintf (config_file, 4096, "%s/freem/%s/freem.conf", SYSCONFDIR, shm_env); | snprintf (config_file, 4096, "%s/freem/%s/freem.conf", SYSCONFDIR, shm_env); |
| snprintf (env_config_file, 4096, "%s/freem/env.conf", SYSCONFDIR); | |
| if (run_daemon == TRUE && geteuid() == 0) { | if (!file_exists (env_config_file)) { |
| fprintf (stderr, "freem: environment catalog does not exist; may need to run fmadm configure\n"); | |
| exit (1); | |
| } | |
| if (custom_group) { | if (!file_exists (config_file)) { |
| d_grp = getgrnam (d_groupname); | fprintf (stderr, "freem: configuration file for %s does not exist; may need to run fmadm configure\n", shm_env); |
| exit (1); | |
| } | |
| if (d_grp == NULL) { | if (read_profile_string (env_config_file, shm_env, "user", d_username) == FALSE) { |
| fprintf (stderr, "freem: invalid group '%s'\n", d_groupname); | fprintf (stderr, "freem: could not determine owning user for environment %s\n", shm_env); |
| exit (1); | exit (1); |
| } | } |
| d_gid = d_grp->gr_gid; | |
| } | |
| if (custom_user) { | if (read_profile_string (env_config_file, shm_env, "group", d_groupname) == FALSE) { |
| d_user = getpwnam (d_username); | fprintf (stderr, "freem: could not determine owning group for environment %s\n", shm_env); |
| exit (1); | |
| } | |
| if (d_user == NULL) { | if (read_profile_string (env_config_file, shm_env, "enabled", env_ena) == FALSE) { |
| fprintf (stderr, "freem: invalid user '%s'\n", d_username); | fprintf (stderr, "freem: could not discover enabled state for environment %s\n", shm_env); |
| exit (1); | exit (1); |
| } | } |
| d_uid = d_user->pw_uid; | if (strcmp (env_ena, "true") != 0) { |
| } | fprintf (stderr, "freem: environment %s is administratively disabled\n", shm_env); |
| exit (1); | |
| } | |
| d_grp = getgrnam (d_groupname); | |
| if (d_grp == NULL) { | |
| fprintf (stderr, "freem: invalid group '%s'\n", d_groupname); | |
| exit (1); | |
| } | |
| d_gid = d_grp->gr_gid; | |
| d_user = getpwnam (d_username); | |
| if (d_user == NULL) { | |
| fprintf (stderr, "freem: invalid user '%s'\n", d_username); | |
| exit (1); | |
| } | |
| d_uid = d_user->pw_uid; | |
| #if defined(__linux__) | |
| if (run_daemon == FALSE && group_member (d_gid) == 0) { | |
| fprintf (stderr, "freem: you must be a member of the %s group to use environment %s\n", d_groupname, shm_env); | |
| exit (1); | |
| } | |
| #endif | |
| if (run_daemon == TRUE) { | |
| if (geteuid () != 0 && nofork == FALSE) { | |
| fprintf (stderr, "freem: forking daemon must be run as root\n"); | |
| exit (1); | |
| } | |
| } | } |
| if ((nofork == TRUE) && (run_daemon == FALSE)) { | if ((nofork == TRUE) && (run_daemon == FALSE)) { |
| Line 526 int main (int argc, char **argv, char ** | Line 573 int main (int argc, char **argv, char ** |
| if (geteuid () == 0) { | if (geteuid () == 0) { |
| /* shed privileges */ | /* shed privileges */ |
| if (custom_group) { | |
| fprintf (stderr, "freem: switching to group %s\n", d_groupname); | fprintf (stderr, "freem: switching to group %s\n", d_groupname); |
| m_log (1, "switching groups"); | m_log (1, "switching groups"); |
| if (setgid (d_gid) == -1) { | if (setgid (d_gid) == -1) { |
| fprintf (stderr, "freem: failure switching GID\n"); | fprintf (stderr, "freem: failure switching GID\n"); |
| m_log (1, "failure switching GIDs"); | m_log (1, "failure switching GIDs"); |
| exit (1); | exit (1); |
| } | |
| } | } |
| if (custom_user) { | if (d_uid != geteuid ()) { |
| fprintf (stderr, "freem: switching to username %s\n", d_username); | fprintf (stderr, "freem: switching to username %s\n", d_username); |
| m_log (1, "switching users"); | m_log (1, "switching users"); |
| Line 547 int main (int argc, char **argv, char ** | Line 594 int main (int argc, char **argv, char ** |
| m_log (1, "failure switching UIDs"); | m_log (1, "failure switching UIDs"); |
| exit (1); | exit (1); |
| } | } |
| if (chdir (d_user->pw_dir) == -1) { | |
| fprintf (stderr, "freem: chdir failure\n"); | |
| m_log (1, "failure in chdir"); | |
| exit (1); | |
| } | |
| } | } |
| } | } |
| Line 566 int main (int argc, char **argv, char ** | Line 608 int main (int argc, char **argv, char ** |
| run_daemon = TRUE; | run_daemon = TRUE; |
| nofork = FALSE; | nofork = FALSE; |
| if (pid_file_path == NULL) { | |
| /* no PID file specified. choose one. */ | |
| uid_t pid_uid; | |
| char *home_directory; | |
| pid_file_path = (char *) calloc (PATH_MAX, sizeof (char)); | |
| NULLPTRCHK(pid_file_path,"main"); | |
| home_directory = (char *) calloc (PATH_MAX, sizeof (char)); | |
| NULLPTRCHK(home_directory,"main"); | |
| pid_uid = geteuid (); | |
| if (pid_uid == 0) { | |
| /* we're running as root */ | |
| strcpy (pid_file_path, "/var/run/freem.pid"); | |
| } | |
| else { | |
| /* our user is a normie */ | |
| struct passwd *pw = getpwuid (pid_uid); | |
| if (pw == NULL) { | |
| m_log (1, "main: failure in getpwuid()"); | |
| } | |
| strcpy (home_directory, pw->pw_dir); | |
| snprintf (pid_file_path, PATH_MAX - 1, "%s/.freem.pid", home_directory); | |
| } | |
| free (home_directory); | |
| } | |
| { | { |
| char pidfile_buf[256]; | char pidfile_buf[256]; |
| int errsav; | int errsav; |
| m_log (1, pid_file_path); | pid_file_path = (char *) malloc (PATH_MAX * sizeof (char)); |
| NULLPTRCHK(pid_file_path,"main"); | |
| snprintf (pid_file_path, PATH_MAX - 1, "%s/freem/run/%s.pid", LOCALSTATEDIR, shm_env); | |
| pid_fd = open (pid_file_path, O_RDWR | O_CREAT, 0640); | pid_fd = open (pid_file_path, O_RDWR | O_CREAT, 0640); |
| errsav = errno; | errsav = errno; |
| if (pid_fd < 0) { | if (pid_fd < 0) { |
| fprintf (stderr, "freem: could not open PID file %s [%s]\n", pid_file_path, strerror (errsav)); | |
| m_log (1, "freem: could not open PID file"); | m_log (1, "freem: could not open PID file"); |
| m_log (1, strerror (errsav)); | m_log (1, strerror (errsav)); |
| exit (1); | exit (1); |
| Line 616 int main (int argc, char **argv, char ** | Line 629 int main (int argc, char **argv, char ** |
| if (lockf (pid_fd, F_TLOCK, 0) < 0) { | if (lockf (pid_fd, F_TLOCK, 0) < 0) { |
| errsav = errno; | errsav = errno; |
| fprintf (stderr, "freem: could not lock PID file [%s]\n", strerror (errsav)); | |
| m_log (1, "freem: could not lock PID file - perhaps already running?"); | m_log (1, "freem: could not lock PID file - perhaps already running?"); |
| m_log (1, strerror (errsav)); | m_log (1, strerror (errsav)); |
| exit (1); | exit (1); |
| } | } |
| sprintf (pidfile_buf, "%d\n", getpid ()); | sprintf (pidfile_buf, "%ld\n", (long) getpid ()); |
| write (pid_fd, pidfile_buf, strlen (pidfile_buf)); | write (pid_fd, pidfile_buf, strlen (pidfile_buf)); |
| } | } |
| Line 656 int main (int argc, char **argv, char ** | Line 670 int main (int argc, char **argv, char ** |
| strncpy (nsnbuf, nsname, 255); | strncpy (nsnbuf, nsname, 255); |
| if (init (nsnbuf) == FALSE) { | if (init (nsnbuf) == FALSE) { |
| set_io (UNIX); | if (fm_initialized) set_io (UNIX); |
| fprintf (stderr, "\nError initializing FreeM.\n"); | fprintf (stderr, "\nError initializing FreeM.\n"); |
| exit (1); | exit (1); |
| } | } |
| else { | |
| fm_initialized = TRUE; | |
| } | |
| direct_mode = FALSE; | direct_mode = FALSE; |
| Line 688 int main (int argc, char **argv, char ** | Line 705 int main (int argc, char **argv, char ** |
| /* isolate the path from the routine file */ | /* isolate the path from the routine file */ |
| strncpy (cli_rtn_path, argv[optind], strrchr (argv[optind], '/') - argv[optind]); | strncpy (cli_rtn_path, argv[optind], strrchr (argv[optind], '/') - argv[optind]); |
| /* set_io (UNIX); | |
| printf ("cli_rtn_name = '%s' cli_rtn_path = '%s'\n", cli_rtn_name, cli_rtn_path); | |
| set_io (MUMPS); | |
| */ | |
| } | } |
| /* do we have a file extension? */ | /* do we have a file extension? */ |
| Line 728 int main (int argc, char **argv, char ** | Line 741 int main (int argc, char **argv, char ** |
| } | } |
| if (!file_exists (config_file)) { | if (!file_exists (config_file)) { |
| set_io (UNIX); | if (fm_initialized == TRUE) set_io (UNIX); |
| fprintf (stderr, "\nFreeM has not been configured. Please run 'fmadm configure'.\n\n\n\n"); | fprintf (stderr, "\nFreeM has not been configured. Please run 'fmadm configure'.\n\n\n\n"); |
| exit (2); | exit (2); |
| Line 741 int main (int argc, char **argv, char ** | Line 755 int main (int argc, char **argv, char ** |
| if (!skip_init) { | if (!skip_init) { |
| /* initialize FreeM environment */ | /* initialize FreeM environment */ |
| strncpy (nsnbuf, nsname, 255); | strncpy (nsnbuf, nsname, 255); |
| if (init (nsnbuf) == FALSE) { | if (init (nsnbuf) == FALSE) { |
| set_io (UNIX); | set_io (UNIX); |
| fprintf (stderr, "\nError initializing FreeM.\n"); | fprintf (stderr, "\nError initializing FreeM.\n"); |
| exit (1); | exit (1); |
| } | } |
| else { | |
| fm_initialized = TRUE; | |
| } | |
| } | } |
| if (first_process == TRUE) { | if (first_process == TRUE) { |
| Line 830 int main (int argc, char **argv, char ** | Line 845 int main (int argc, char **argv, char ** |
| fprintf (stderr, "freem: terminating\r\n"); | fprintf (stderr, "freem: terminating\r\n"); |
| cleanup (); | cleanup (); |
| exit (0); | exit (0); |
| } | } |
| Line 847 int main (int argc, char **argv, char ** | Line 863 int main (int argc, char **argv, char ** |
| #if !defined(_AIX) | #if !defined(_AIX) |
| if(import_env == TRUE) { | if (import_env == TRUE) { |
| int i_maxlen = 255; | int i_maxlen = 255; |
| for(env = envp; *env != 0; env++) { | for (env = envp; *env != 0; env++) { |
| varname = strtok (*env, "="); | |
| namelen = 0; | varval = strtok (NULL, "="); |
| vallen = 0; | |
| varname = strtok(*env, "="); | |
| varval = strtok(NULL, "="); | |
| if(varval != NULL) { | |
| namelen = strlen (varname); | |
| vallen = strlen (varval); | |
| if (varval != NULL) { | |
| snprintf (symname, i_maxlen, "ENV.%s\201\201", varname); | snprintf (symname, i_maxlen, "ENV.%s\201\201", varname); |
| strncpy (symval, varval, i_maxlen); | strncpy (symval, varval, i_maxlen); |
| Line 886 int main (int argc, char **argv, char ** | Line 894 int main (int argc, char **argv, char ** |
| snprintf (version, 255, "\r\nCoherent Logic Development FreeM version %s [DIALECT: %s%s]\r\n\201", verstr, m_dialect, (restricted_mode == TRUE ? "/RESTRICTED" : "")); | snprintf (version, 255, "\r\nCoherent Logic Development FreeM version %s [DIALECT: %s%s]\r\n\201", verstr, m_dialect, (restricted_mode == TRUE ? "/RESTRICTED" : "")); |
| write_m (version); | write_m (version); |
| snprintf (version, 255, "Copyright (C) 2014, 2020, 2021, 2023 Coherent Logic Development LLC\r\n\r\n\201"); | snprintf (version, 255, "Copyright (C) 2014, 2020, 2021, 2023, 2025 Coherent Logic Development LLC\r\n\r\n\201"); |
| write_m (version); | write_m (version); |
| /* | |
| printf ("Environment: \t%s\r\n", shm_env); | printf ("Environment: \t%s\r\n", shm_env); |
| printf ("Environment Daemon:\tPID %d\r\n", shm_config->hdr->first_process); | printf ("Environment Daemon:\tPID %d\r\n", shm_config->hdr->first_process); |
| printf ("Interpreter Process:\tPID %d\r\n", pid); | printf ("Interpreter Process:\tPID %d\r\n", pid); |
| */ | |
| } | } |
| else { | else { |
| Line 941 void freem_usage(void) | Line 950 void freem_usage(void) |
| fprintf (stdout, "\t-x <MCODE>, --execute=<MCODE>\n\t\texecute M code <MCODE> on startup\n\n"); | fprintf (stdout, "\t-x <MCODE>, --execute=<MCODE>\n\t\texecute M code <MCODE> on startup\n\n"); |
| fprintf (stdout, "\t-d, --daemon\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n"); | fprintf (stdout, "\t-d, --daemon\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n"); |
| fprintf (stdout, "\t-k, --nofork\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n"); | fprintf (stdout, "\t-k, --nofork\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n"); |
| fprintf (stdout, "\t-p <PIDFILE>, --pidfile=<PIDFILE>\n\t\tuse <PIDFILE> to record the PID of the FreeM daemon\n\n\n"); | |
| 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"); | 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"); |
| #else | #else |
| fprintf (stdout, "\t-h\n\t\tdisplays this help message\n\n"); | fprintf (stdout, "\t-h\n\t\tdisplays this help message\n\n"); |
| Line 956 void freem_usage(void) | Line 964 void freem_usage(void) |
| fprintf (stdout, "\t-x <MCODE>\n\t\texecute M code <MCODE> on startup\n\n"); | fprintf (stdout, "\t-x <MCODE>\n\t\texecute M code <MCODE> on startup\n\n"); |
| fprintf (stdout, "\t-d\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n"); | fprintf (stdout, "\t-d\n\t\trun the FreeM daemon (one and only one FreeM daemon must always be running)\n\n"); |
| fprintf (stdout, "\t-k\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n"); | fprintf (stdout, "\t-k\n\t\trun the FreeM daemon in foreground (requires --daemon)\n\n"); |
| fprintf (stdout, "\t-p <PIDFILE>\n\t\tuse <PIDFILE> to record the PID of the FreeM daemon\n\n\n"); | |
| 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"); | 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"); |
| #endif | #endif |
| fprintf (stdout, "\t\t - Each concurrent job takes %d bytes (1 page) of shared memory\n", PG_SIZE); | fprintf (stdout, "\t\t - Each concurrent job takes %d bytes (1 page) of shared memory\n", PG_SIZE); |