--- freem/src/fmadm.c	2025/04/02 14:37:57	1.26
+++ freem/src/fmadm.c	2025/04/04 19:43:18	1.31
@@ -1,5 +1,5 @@
 /*
- *   $Id: fmadm.c,v 1.26 2025/04/02 14:37:57 snw Exp $
+ *   $Id: fmadm.c,v 1.31 2025/04/04 19:43:18 snw Exp $
  *    FreeM Administration Tool
  *
  *  
@@ -24,6 +24,21 @@
  *   along with FreeM.  If not, see <https://www.gnu.org/licenses/>.
  *
  *   $Log: fmadm.c,v $
+ *   Revision 1.31  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.30  2025/04/03 20:48:14  snw
+ *   Improve daemon error diagnostics and bump to 0.63.0-rc3
+ *
+ *   Revision 1.29  2025/04/03 01:41:02  snw
+ *   New features frozen; prepare 0.63.0-rc1
+ *
+ *   Revision 1.28  2025/04/02 19:59:38  snw
+ *   Automatically modify env.conf from fmadm reconfigure
+ *
+ *   Revision 1.27  2025/04/02 15:36:25  snw
+ *   Do extensive result checking for environment stop/start/restart in fmadm
+ *
  *   Revision 1.26  2025/04/02 14:37:57  snw
  *   Improve environment control parts of fmadm
  *
@@ -168,6 +183,7 @@ extern char config_file[4096];
 extern char env_config_file[4096];
 extern char env_user[255];
 extern char env_group[255];
+extern char env_enabled[10];
 
 int fm_shell(void);
 void fm_checkperms(void);
@@ -177,6 +193,7 @@ int fm_daemonctl (short action, short ob
 void fm_write (FILE *file, char *buf);
 int fma_jobs_remove (int optc, char **opts);
 void set_permissions(char *path, char *user, char *grp, int mode);
+int fm_environment_running (char *env);
 extern int read_profile_string(char *file, char *section, char *key, char *value);
 
 int main (int argc, char **argv)
@@ -197,6 +214,7 @@ int main (int argc, char **argv)
     short got_action = FALSE;
     short got_object = FALSE;
 
+    strcpy (env_enabled, "true");
 
     /* snprintf (config_file, 4096, "%s/freem.conf", SYSCONFDIR); */
 
@@ -283,6 +301,31 @@ int main (int argc, char **argv)
                     base_arg++;
                     
                     break;
+
+                case 'E':
+                    if (argv[i][2] != '=') {
+                        fprintf (stderr, "fmadm:  missing equals sign in flag -%c\n", argv[i][1]);
+                        fmadm_usage ();
+                        exit (1);
+                    }
+
+                    k = 0;
+                    
+                    for (j = 3; j < strlen (argv[i]); j++) {
+                        env_enabled[k++] = argv[i][j];
+                    }
+                    env_enabled[k] = '\0';
+
+                    if ((strcmp (env_enabled, "true") != 0) && (strcmp (env_enabled, "false") != 0)) {
+                        fprintf (stderr, "fmadm:  -E (environment enabled) option must be either 'true' or 'false'\n");
+                        fmadm_usage ();
+                        exit (1);
+                    }
+                    
+                    base_arg++;
+                    
+                    break;
+                    
                     
                 case 'n':
                     if (argv[i][2] != '=') {
@@ -878,15 +921,14 @@ void fmadm_exit (int retval)
 int fmadm_usage (void)
 {
 
-    fprintf (stdout, "\nusage:  fmadm <action> <object> [-e=<environment] [-n=<namespace>] [OPTIONS]\n");
+    fprintf (stdout, "\nusage:  fmadm <action> <object> [-e=<environment] [-n=<namespace>] [-u=<user>] [-g=<group>] [-E=true|false] [OPTIONS]\n");
     fprintf (stdout, "        fmadm configure\n");
     fprintf (stdout, "        fmadm reconfigure\n");
-    /* fprintf (stdout, "        fmadm checkperms\n\n"); */
     
     fprintf (stdout, "        <action> can be one of:\n");
     fprintf (stdout, "            list, examine, verify, compact, repair, create, remove,\n");
     fprintf (stdout, "            import, export, backup, restore, migrate, edit, start,\n");
-    fprintf (stdout, "            stop, restart\n\n");
+    fprintf (stdout, "            stop, restart, status\n\n");
 
     fprintf (stdout, "        <object> can be one of:\n");
     fprintf (stdout, "            lock, journal, namespace, global, routine, job,\n");
@@ -1174,10 +1216,14 @@ int fm_validate_environment (char *env)
     return FALSE;
 }
 
-int fm_start_environment (char *env, char *e_user, char *e_grp)
+int fm_start_environment (char *env)
 {
     char basecmd[255];
     char cmd[4096];    
+
+    if (fm_environment_running (env) == TRUE) {
+        return TRUE;
+    }
     
 #if !defined(__OS2__)
     snprintf (basecmd, 254, "%s/bin/freem", PREFIX);
@@ -1186,29 +1232,70 @@ int fm_start_environment (char *env, cha
 #endif
 
 #if !defined(__OS2__)                    
-    snprintf (cmd, 4095, "%s -d -e %s -u %s -g %s", basecmd, cur_env, e_user, e_grp);
+    snprintf (cmd, 4095, "%s -d -e %s", basecmd, env);
 #else
-    sprintf (cmd, 4095, "%s -d -k -e %s -u %s -g %s", basecmd, cur_env, e_user, e_grp);
+    sprintf (cmd, 4095, "%s -d -k -e %s", basecmd, env);
 #endif
+
+    system (cmd);
+
+    sleep (1);
     
-    return (system (cmd));        
+    return (fm_environment_running (env));
 }
 
 int fm_stop_environment (char *env)
 {
     long epid;
 
-    epid = fm_get_pid (cur_env);
+    epid = fm_get_pid (env);
     if (epid > -1) {
-        fprintf (stderr, "fmadm:  stopping environment daemon pid %d\n", epid);
         kill (epid, SIGINT);
-        kill (epid, SIGTERM);
+        sleep (5);
+
+        if (fm_environment_running (env) == FALSE) {
+            return TRUE;
+        }
+        else {
+            kill (epid, SIGTERM);
+            sleep (5);
+            if (fm_environment_running (env) == FALSE) {
+                return TRUE;
+            }
+            else {
+                kill (epid, SIGKILL);
+                sleep (5);
+                if (fm_environment_running (env) == FALSE) {
+                    return TRUE;
+                }
+                else {
+                    return FALSE;
+                }
+            }
+        }
     }
     else {
-        fprintf (stderr, "fmadm:  could not obtain environment daemon pid\n");
+        return FALSE;
     }   
 }
 
+int fm_environment_running (char *env)
+{       
+    long epid;
+    int result;
+    
+    epid = fm_get_pid (env);
+
+    if (epid == -1) {
+        return FALSE;
+    }
+    else {
+        result = kill (epid, 0);
+
+        return ((result == 0) ? TRUE : FALSE);
+    }
+}
+
 int fm_daemonctl (short action, short object, int optc, char **options)
 {
     FILE *ef;
@@ -1224,7 +1311,9 @@ int fm_daemonctl (short action, short ob
     char *savptr;
     int result;
     long epid;
+    int retval;
    
+    retval = 0;
     
     switch (action) {
         case ACT_START:
@@ -1289,26 +1378,56 @@ int fm_daemonctl (short action, short ob
                 strcpy (e_grp, "freem");
             }
 
-            printf ("fmadm:  %s environment %s\n", verb, cur_env);
+            switch (action) {
+                case ACT_START:
+                case ACT_STOP:
+                case ACT_RESTART:
+                    fprintf (stderr, "fmadm:  %s environment %s... ", verb, cur_env);
+                    break;
+                case ACT_STATUS:
+                    fprintf (stderr, "fmadm:  %s environment %s\n", verb, cur_env);
+                    break;
+            }
 
             switch (action) {
                 
                 case ACT_START:
-                    result = fm_start_environment (cur_env, e_user, e_grp);
+                    result = fm_start_environment (cur_env);
+                    if (result == TRUE) {
+                        fprintf (stderr, "[OK]\n");
+                    }
+                    else {
+                        fprintf (stderr, "[FAIL]\n");
+                    }
                     break;
 
                 case ACT_STOP:
                     result = fm_stop_environment (cur_env);
+                    if (result == TRUE) {
+                        fprintf (stderr, "[OK]\n");
+                    }
+                    else {
+                        fprintf (stderr, "[FAIL]\n");
+                        retval++;
+                    }                    
                     break;
 
                 case ACT_RESTART:
-                    result = fm_stop_environment (cur_env);
-                    
-                    fprintf (stderr, "fmadm:  waiting 2 seconds\n");
-                    sleep (2);
-                    fprintf (stderr, "fmadm:  starting environment %s\n", cur_env);
+                    if (fm_stop_environment (cur_env) == TRUE) {
+                        result = fm_start_environment (cur_env);
+                        if (result == TRUE) {
+                            fprintf (stderr, "[OK]\n");
+                        }
+                        else {
+                            fprintf (stderr, "[FAIL]\n");
+                            retval++;
+                        }                        
+                    }
+                    else {
+                        fprintf (stderr, "[FAIL]\n");
+                        retval++;
+                    }                       
 
-                    result = fm_start_environment (cur_env, e_user, e_grp);
                     break;
                     
                 case ACT_STATUS:
@@ -1327,7 +1446,7 @@ int fm_daemonctl (short action, short ob
     } while ((cur_env = strtok_r (NULL, ",", &savptr)) != NULL);
 
     free (envlist);
-    exit (0);
+    exit (retval);
     
 } /* fm_daemonctl() */
 
@@ -1354,7 +1473,11 @@ void fm_reconfigure(void)
     fprintf (stderr, "fmadm:  reconfiguring FreeM with system defaults for %s...\n", FREEM_VERSION_CSTR);
     fprintf (stderr, "fmadm:  backing up %s to %s...\t", config_file, config_backup);    
 
-    retval = rename (config_file, config_backup);
+#if !defined(__OS2__)    
+    retval = cp (config_backup, config_file);
+#else
+    retval = DosCopy (config_file, config_backup);
+#endif    
 
     if (retval == 0) {
 	fprintf (stderr, "[OK]\n");
@@ -1371,6 +1494,22 @@ void fm_reconfigure(void)
     
 } /* fm_reconfigure() */
 
+void update_conf (char *file, char *section, char *key, char *new_value)
+{
+    char old_value[255];
+    char tbuf[255];
+
+    snprintf (tbuf, 254, "%s.%s", section, key);
+        
+    read_profile_string (file, section, key, old_value);
+    if (strcmp (old_value, new_value) != 0) {
+        modify_profile_string (file, section, key, new_value);
+        fprintf (stderr, "\t%-40s%-20s -> %s\n", tbuf, old_value, new_value);
+    }
+    else {
+        fprintf (stderr, "\t%-40s%-20s\n", tbuf, "no change");
+    }
+}
 
 void fm_configure (void)
 {
@@ -1396,6 +1535,8 @@ void fm_configure (void)
     char buf[4096];
     FILE *fp;
 
+    int reconfigure = FALSE;
+    
     struct stat etcstat;
     int stat_result;
 
@@ -1406,7 +1547,7 @@ void fm_configure (void)
 
     char *username = env_user;
     char *groupname = env_group;
-   
+    
 #if !defined(__OS2__)
     if (geteuid () != 0) {
         fprintf (stderr, "fmadm:  not superuser\n");
@@ -1449,11 +1590,9 @@ void fm_configure (void)
     }
     
     if (file_exists (config_file)) {
-        fprintf (stderr, "fmadm:  '%s' already exists; running fmadm reconfigure instead\n", config_file);
-        fm_reconfigure ();
-        return;
+        reconfigure = TRUE;
     }
-
+    
     gethostname (hostid, 4095);
     uuid_v4 (buf);
 
@@ -1481,15 +1620,22 @@ void fm_configure (void)
 
         chmod (dstfile, 0755);
     }
-#else
-    fprintf (stderr, "fmadm:  not running on OS/2\n");
 #endif
-    
-    printf ("\nFreeM Initial Configuration\n");
-    printf ("---------------------------\n\n");
 
-    printf ("This utility will create the initial configuration file for ");
-    printf ("FreeM environment '%s' (owned by %s:%s) in '%s'.\n\n", fma_environment, username, groupname, config_file);    
+    if (reconfigure == FALSE) {
+        printf ("\nFreeM Initial Environment Configuration\n");
+        printf ("---------------------------------------\n\n");
+
+        printf ("This utility will create the initial configuration files for ");
+        printf ("FreeM environment '%s' (owned by %s:%s) in '%s'.\n\n", fma_environment, username, groupname, config_file);    
+    }
+    else {
+        printf ("\nFreeM Environment Upgrade/Reconfiguration\n");
+        printf ("-----------------------------------------\n\n");
+
+        printf ("This utility will update the configuration files for ");
+        printf ("FreeM environment '%s' (owned by %s:%s) in '%s'.\n\n", fma_environment, username, groupname, config_file);
+    }
     
     /* check for existence of needed directories */
     if (stat (SYSCONFDIR, &etcstat) == -1) {
@@ -1537,8 +1683,13 @@ void fm_configure (void)
     
     snprintf (src_dir, 4095, "%s/freem/mlib", DATADIR);
     snprintf (dest_dir, 4095, "%s/freem/%s/SYSTEM/routines", LOCALSTATEDIR, fma_environment);
-    
-    fprintf (stderr, "fmadm:  populating new environment '%s'\n", fma_environment);
+
+    if (reconfigure == FALSE) {
+        fprintf (stderr, "fmadm:  populating new environment '%s'\n", fma_environment);
+    }
+    else {
+        fprintf (stderr, "fmadm:  upgrading environment '%s'\n", fma_environment);
+    }
     
     snprintf (buf, 4095, "%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
     mkdir (buf, 0775);
@@ -1603,88 +1754,134 @@ void fm_configure (void)
         
     }
 
-    fp = fopen (env_config_file, "a+");
-
-    fprintf (stderr, "Creating %s... ", env_config_file);
-
-    snprintf (buf, 4095, "[%s]", fma_environment);
-    fm_write (fp, buf);
+    if (fm_validate_environment (fma_environment) == FALSE) {
+        fp = fopen (env_config_file, "a+");
+        
+        fprintf (stderr, "Creating %s... ", env_config_file);
+        
+        snprintf (buf, 4095, "[%s]", fma_environment);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "user=%s", env_user);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "group=%s", env_group);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "enabled=%s", env_enabled);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "env_path=%s/freem/%s", LOCALSTATEDIR, fma_environment);
+        fm_write (fp, buf);
+        
+        fclose (fp);
+        fprintf (stderr, "[OK]\n");
+    }
+    else {
+        char modtmp[255];
+        
+        fprintf (stderr, "\nUpdating %s: \n", env_config_file);
 
-    snprintf (buf, 4095, "user=%s", env_user);
-    fm_write (fp, buf);
-    
-    snprintf (buf, 4095, "group=%s", env_group);
-    fm_write (fp, buf);
+        update_conf (env_config_file, fma_environment, "user", env_user);
+        update_conf (env_config_file, fma_environment, "group", env_group);
+        update_conf (env_config_file, fma_environment, "enabled", env_enabled);
+    }
 
-    snprintf (buf, 4095, "enabled=true");
-    fm_write (fp, buf);
-    
-    snprintf (buf, 4095, "env_path=%s/freem/%s", LOCALSTATEDIR, fma_environment);
-    fm_write (fp, buf);
-    
-    fclose (fp);
-    fprintf (stderr, "[OK]\n");
-   
-    fp = fopen (config_file, "a+");
-    
-    fprintf (stderr, "Creating %s... ", config_file); 
+    if (reconfigure == FALSE) {
+        fp = fopen (config_file, "a+");
+        
+        fprintf (stderr, "Creating %s... ", config_file); 
+        
+        snprintf (buf, 4095, "[SYSTEM]");
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "root=%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "routines_path=%s", sysrtn);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "globals_path=%s", sysgbl);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "journal_file=%s", jnlfile);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "journal_mode=%s", jnlmode);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "journal_host_id=%s", jnlhostid);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "journal_cut_threshold=%s", jnlcut);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "zdate_format=%%x");
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "ztime_format=%%X");
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "\n[USER]");
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "root=%s/freem/%s/USER", LOCALSTATEDIR, fma_environment);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "routines_path=%s", usrrtn);
+        fm_write (fp, buf);
+        
+        snprintf (buf, 4095, "globals_path=%s", usrgbl);
+        fm_write (fp, buf);
+        
+        fclose (fp);
+        set_permissions (config_file, username, groupname, 0755);
+        fprintf (stderr, "[OK]\n");
     
-    snprintf (buf, 4095, "[SYSTEM]");
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "root=%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "routines_path=%s", sysrtn);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "globals_path=%s", sysgbl);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "journal_file=%s", jnlfile);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "journal_mode=%s", jnlmode);
-    fm_write (fp, buf);
+        printf ("FreeM initial configuration is complete.\n\n");
+    }
+    else {
+        char tmpsd[255];
+        read_profile_string (config_file, "SYSTEM", "journal_host_id", tmpsd);
+        
+        /* existing configuration */
+        fprintf (stderr, "\nUpdating environment configuration for %s:\n", fma_environment);
 
-    snprintf (buf, 4095, "journal_host_id=%s", jnlhostid);
-    fm_write (fp, buf);
+        snprintf (buf, 4095, "%s/freem/%s/SYSTEM", LOCALSTATEDIR, fma_environment);
+        update_conf (config_file, "SYSTEM", "root", buf);
+        update_conf (config_file, "SYSTEM", "routines_path", sysrtn);       
+        update_conf (config_file, "SYSTEM", "globals_path", sysgbl);
+        update_conf (config_file, "SYSTEM", "journal_file", jnlfile);        
+        update_conf (config_file, "SYSTEM", "journal_mode", jnlmode);
+        update_conf (config_file, "SYSTEM", "journal_host_id", tmpsd);
+        update_conf (config_file, "SYSTEM", "journal_cut_threshold", jnlcut);
 
-    snprintf (buf, 4095, "journal_cut_threshold=%s", jnlcut);
-    fm_write (fp, buf);
+        snprintf (buf, 4095, "%%x");
+        update_conf (config_file, "SYSTEM", "zdate_format", buf);
+        
+        snprintf (buf, 4095, "%%X");
+        update_conf (config_file, "SYSTEM", "ztime_format", buf);
 
-    snprintf (buf, 4095, "zdate_format=%%x");
-    fm_write (fp, buf);
+        /* USER */        
+        snprintf (buf, 4095, "%s/freem/%s/USER", LOCALSTATEDIR, fma_environment);
+        update_conf (config_file, "USER", "root", buf);        
+        update_conf (config_file, "USER", "routines_path", usrrtn);
+        update_conf (config_file, "USER", "globals_path", usrgbl);
 
-    snprintf (buf, 4095, "ztime_format=%%X");
-    fm_write (fp, buf);
+    }
     
-    snprintf (buf, 4095, "\n[USER]");
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "root=%s/freem/%s/USER", LOCALSTATEDIR, fma_environment);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "routines_path=%s", usrrtn);
-    fm_write (fp, buf);
-
-    snprintf (buf, 4095, "globals_path=%s", usrgbl);
-    fm_write (fp, buf);
         
-    fclose (fp);
-    set_permissions (config_file, username, groupname, 0755);
-    fprintf (stderr, "[OK]\n");
-
-    printf ("FreeM initial configuration is complete.\n\n");
-
-    printf (" USER globals:                   %s\n", usrgbl);
-    printf (" USER routines:                  %s\n", usrrtn);
-    printf (" SYSTEM globals:                 %s\n", sysgbl);
-    printf (" SYSTEM routines:                %s\n", sysrtn);
-    printf (" After-image journal:            %s [%s]\n", jnlfile, jnlmode);
-    printf (" Journal cut threshold:          %s bytes\n", jnlcut);
-    printf (" Distributed journaling host ID: %s\n", jnlhostid);
+    printf ("\n\nUSER globals:                   %s\n", usrgbl);
+    printf ("USER routines:                  %s\n", usrrtn);
+    printf ("SYSTEM globals:                 %s\n", sysgbl);
+    printf ("SYSTEM routines:                %s\n", sysrtn);
+    printf ("After-image journal:            %s [%s]\n", jnlfile, jnlmode);
+    printf ("Journal cut threshold:          %s bytes\n", jnlcut);
+    printf ("Distributed journaling host ID: %s\n", jnlhostid);
 
+    if (reconfigure == TRUE) {
+        fprintf (stderr, "\nIf you previously defined environments other than '%s', you should run\n'fmadm reconfigure -e=<environment-name>' on each of them to ensure they have\nthe latest vendor routines and correct, updated settings.\n\n", fma_environment);
+    }
     
 } /* fm_configure */