Add ibcli program and man page to infiniband-diags.

Signed-off-by: David A. McMillen <[EMAIL PROTECTED]>
---
 infiniband-diags/ChangeLog                |    4 +
 infiniband-diags/Makefile.am              |    7 +-
 infiniband-diags/man/ibcli.8              |  116 +++++
 infiniband-diags/src/ibcli.c              |  740 +++++++++++++++++++++++++++++
 4 files changed, 865 insertions(+), 2 deletions(-)

diff --git a/infiniband-diags/ChangeLog b/infiniband-diags/ChangeLog
index ad35140..a2d0563 100644
--- a/infiniband-diags/ChangeLog
+++ b/infiniband-diags/ChangeLog
@@ -1,3 +1,7 @@
+2007-07-31 Dave McMillen <[EMAIL PROTECTED]>
+
+       * src/ibcli.c, man/ibcli.8: Add program and man page
+
 2007-07-10 Hal Rosenstock <[EMAIL PROTECTED]>
 
        * 1.3.1 release of infiniband-diags
diff --git a/infiniband-diags/Makefile.am b/infiniband-diags/Makefile.am
index c90fed1..3ce38b2 100644
--- a/infiniband-diags/Makefile.am
+++ b/infiniband-diags/Makefile.am
@@ -15,7 +15,7 @@ endif
 sbin_PROGRAMS = src/ibaddr src/ibnetdiscover src/ibping src/ibportstate \
                src/ibroute src/ibstat src/ibsysstat src/ibtracert \
                src/perfquery src/sminfo src/smpdump src/smpquery \
-               src/saquery src/vendstat
+               src/saquery src/vendstat src/ibcli
 
 sbin_SCRIPTS = scripts/ibcheckerrs scripts/ibchecknet scripts/ibchecknode \
                scripts/ibcheckport scripts/ibhosts scripts/ibstatus \
@@ -133,6 +133,9 @@ src_vendstat_CFLAGS = -Wall $(DBGFLAGS)
 #                    $(libdir)/libibmad.la
 #src_vendstat_LDFLAGS = -Wl,--rpath -Wl,$(libdir)
 
+src_ibcli_SOURCES = src/ibcli.c
+src_ibcli_CFLAGS = -Wall $(DBGFLAGS)
+
 #src_mcm_rereg_test_SOURCES = src/mcm_rereg_test.c
 #src_mcm_rereg_test_CFLAGS = -Wall $(DBGFLAGS)
 #sbin_PROGRAMS += src/mcm_rereg_test
@@ -149,7 +152,7 @@ man_MANS = man/ibaddr.8 man/ibcheckerrors.8 
man/ibcheckerrs.8 \
        man/iblinkinfo.8 man/ibqueryerrors.8 man/ibswportwatch.8 \
        man/ibprintswitch.8 man/ibprintca.8 man/ibfindnodesusing.8 \
        man/ibdatacounts.8 man/ibdatacounters.8 \
-       man/ibrouters.8 man/ibprintrt.8 man/ibidsverify.8
+       man/ibrouters.8 man/ibprintrt.8 man/ibidsverify.8 man/ibcli.8
 
 EXTRA_DIST = scripts include infiniband-diags.spec.in $(man_MANS)
 
diff --git a/infiniband-diags/infiniband-diags.spec.in 
b/infiniband-diags/infiniband-diags.spec.in
diff --git a/infiniband-diags/man/ibcli.8 b/infiniband-diags/man/ibcli.8
new file mode 100644
index 0000000..9bc5ebe
--- /dev/null
+++ b/infiniband-diags/man/ibcli.8
@@ -0,0 +1,116 @@
+.TH IBCLI 8 "July 31, 2007" "OpenIB" "OpenIB Diagnostics"
+
+.SH NAME
+ibcli \- InfiniBand Command Line Interface
+
+.SH SYNOPSIS
+.B ibcli
+get [archive] srp|opensm|opensm.opts|sdp|openib [?|param]
+
+.B ibcli
+set [archive] srp|opensm|opensm.opts|sdp|openib param=?|value
+
+.B ibcli
+edit [archive] srp|opensm|opensm.opts|sdp|openib [editor]
+
+.B ibcli
+save archive
+
+.B ibcli
+restore archive
+
+.B ibcli
+copy source-archive archive
+
+.SH DESCRIPTION
+.PP
+ibcli is a command line interface (CLI) designed to simplify and unify
+the use of various OFED software components.  There are several parts
+to this program:
+
+.PP
+.TP
+Configuration file management/maintenance/inquiry
+
+The various configuration files are accessed through simple names
+within the CLI, referencing the actual files in the appropriate
+locations depending on where and how the OFED software was installed.
+
+Extraction and modification of individual parameter/value pairs is
+easily done, and all configuration files are modified using a rename()
+system call.  This means that either the old or new version in it's
+entirety will be seen by any program that opens and reads a file, even
+though it is unsynchronized with the modification program.  Unlike an
+editor that rewrites a file, where there is a possibility that another
+program will see some but not all changes, the method used here will
+never present an inconsistent configuration.
+
+Also, the complete set of configuration files can be saved, duplicated,
+modified separate from the "live" versions, and restored.
+
+.SH OPTIONS
+
+.PP
+.TP
+\fBget\fR
+Fetches the contents of a configuration file.  With no param specified, the
+entire contents of the file are returned.  If the param is specified as ?
+then the file is returned with comments stripped.  If a specific param is
+named, then only the line with that parameter is returned.
+
+.TP
+\fBset\fR
+Edits the contents of a configuration file.  The line with the specific named
+parameter is modified to contain the new value specified.  If the new value is
+specified as ? then no modification takes place, but the comments leading up
+to the parameter are displayed.
+
+.TP
+\fBedit\fR
+Edits the contents of a configuration file using an external editor.  The
+specified configuration file is copied to a temporary file for editing.  If
+the editor parameter was specified, it is used as the name of the editor
+program.  Otherwise, if the VISUAL environment variable is set, it will be
+used as the editor program name.  Otherwise, if the EDITOR environment
+variable is set, it will be used as the editor program name.  Otherwise,
+vi is used.
+
+.TP
+\fBsave\fR
+The set of all configuration files are saved in the named archive.
+
+.TP
+\fBrestore\fR
+The set of all configuration files are restored from the named archive.
+
+.TP
+\fBcopy\fR
+The set of all configuration files in the source archive are copied to the
+named archive.
+
+.SH NOTES
+
+.PP
+Locking configuration files:  A parallel file is created with the same name as
+the file to be locked, with the CONFIG_LOCK_SUFFIX (-locked)  appended to the
+name.  This will contain the PID of the program that did the lock.  If a
+program dies holding the lock, this is detected, and the lock file removed.
+This method is reportedly broken for the general case on NFS filesystems, but
+seems to work when all locking accesses are from the same system.
+
+.PP
+Save and restore commands
+move all configuration files (as listed in the config_files array) between
+their normal location and a subdirectory in the saved config directory
+(/etc/infiniband/saved_configs).  This subdirectory name is the name of
+the archive listed in the command, and is created during a save if it
+does not exist.  If it does exist, it is renamed with a new suffix
+(~) to keep one previous copy.  If there is already a
+subdirectory with that suffix, it (and it's contents) are deleted.
+A restore will first rename each target config file with the suffix,
+deleting any previous one with that name, and put the restored copy in.
+
+.SH AUTHORS
+.TP
+Dave McMillen
+.RI < [EMAIL PROTECTED] >
diff --git a/infiniband-diags/src/ibcli.c b/infiniband-diags/src/ibcli.c
new file mode 100644
index 0000000..44b762c
--- /dev/null
+++ b/infiniband-diags/src/ibcli.c
@@ -0,0 +1,740 @@
+/*
+ * Copyright (c) 2007 System Fabric Works, Inc.  All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses.  You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ *     Redistribution and use in source and binary forms, with or
+ *     without modification, are permitted provided that the following
+ *     conditions are met:
+ *
+ *      - Redistributions of source code must retain the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer.
+ *
+ *      - Redistributions in binary form must reproduce the above
+ *        copyright notice, this list of conditions and the following
+ *        disclaimer in the documentation and/or other materials
+ *        provided with the distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+/*     ibcli.c
+
+       This is a command line interface (CLI) designed to simplify and unify
+       the use of various OFED software components.  There are several parts
+       to this program:
+
+      * Configuration file management/maintenance/inquiry
+
+       The various configuration files are accessed through simple names
+       within the CLI, referencing the actual files in the appropriate
+       locations depending on where and how the OFED software was installed.
+
+       Extraction and modification of individual parameter/value pairs is
+       easily done, and all configuration files are modified using a rename()
+       system call.  This means that either the old or new version in it's
+       entirety will be seen by any program that opens and reads a file, even
+       though it is unsynchronized with the modification program.  Unlike an
+       editor that rewrites a file, where there is a possibility that another
+       program will see some but not all changes, the method used here will
+       never present an inconsistent configuration.
+
+       Also, the complete set of configuration files can be saved, duplicated,
+       modified separate from the "live" versions, and restored.
+
+      * 
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <getopt.h>
+#include <errno.h>
+#include <dirent.h>
+#include <signal.h>
+
+#define SAVED_CONFIGS_DIR "/etc/infiniband/saved_configs"
+#define CONFIG_LOCK_SUFFIX "-locked"
+#define CONFIG_PREV_SUFFIX "~"
+#define CONFIG_NEW_SUFFIX "+"
+#define MAX_LOCK_ATTEMPTS 20
+#define DEFAULT_EDITOR "vi"
+
+#define CMD_SAVE "save"
+#define CMD_RESTORE "restore"
+#define CMD_COPY "copy"
+#define CMD_GET "get"
+#define CMD_SET "set"
+#define CMD_EDIT "edit"
+
+struct config_files {
+    char *cmd_name;    /* Name used in commands to reference this */
+    char *file_name;   /* Absolute path to configuration file */
+    int usespace;      /* Param/value pairs do not have = between them */
+    };
+
+struct config_files config_files[] = { /* TODO: Pick up installed places */
+    {"srp", "/etc/srp_daemon.conf", -1},
+    {"opensm", "/etc/opensm.conf", 0},
+    {"opensm.opts", "", 1},
+    {"sdp", "/etc/libsdp.conf", -1},
+    {"openib", "/etc/infiniband/openib.conf", 0},
+    { NULL, NULL } };
+
+char *argv0;           /* Remembers the argv[0] for the main() program */
+
+char inbuf[16384];     /* Used for input and temporary purposes */
+char savebuf[16384];   /* Saves commentary for later printout */
+char *savebufp;                /* Remembers how far we have filled savebuf */
+
+/*  For configuration files that may be at a user-defined place, figure out
+    where they are located and return that location. */
+
+char *find_cfg_file(char *cmd_name) {
+    if (strcmp(cmd_name, "opensm.opts") == 0) {
+       return "/var/cache/osm/opensm.opts";  /* TODO: find real path value */
+    }
+    fprintf(stderr, "ERROR: %s internal error, unknown config file for %s\n",
+       argv0, cmd_name);
+    exit(2);
+}
+
+/* Find the last non-/ character in a string */
+
+char *base_pointer(char *path) {
+    int l = strlen(path);
+    while (l > 0 && path[l-1] != '/') l--;
+    return path + l;
+}
+
+/*  Return a malloc()'d string with the full file name of the configuration
+    file, given the archive name and standard running full filename */
+
+char *working_cfg_file(char *archive, char *filename) {
+    char *rv, *fv;
+    int srv;
+
+    if (archive == NULL) {
+       rv = (char *)malloc(strlen(filename) + strlen(CONFIG_PREV_SUFFIX) + 1);
+       strcpy(rv, filename);
+       return rv;
+    }
+    fv = base_pointer(filename);
+    srv = strlen(SAVED_CONFIGS_DIR) + 1 + strlen(archive) + 1 + strlen(fv)
+                                       + strlen(CONFIG_PREV_SUFFIX) + 1;
+    rv = (char *)malloc(srv);
+    strcpy(rv, SAVED_CONFIGS_DIR);
+    strcat(rv, "/");
+    strcat(rv, archive);
+    strcat(rv, "/");
+    strcat(rv, fv);
+    return rv;
+}
+
+/* Tell the user how to invoke this program */
+
+void usage() {
+    int cf_index;
+    char sep = ' ';
+
+    fprintf(stderr, "Usage:\n");
+    fprintf(stderr, " %s get [archive]", argv0);
+    for (cf_index = 0; config_files[cf_index].cmd_name != NULL; cf_index++) {
+       fprintf(stderr, "%c%s", sep, config_files[cf_index].cmd_name);
+       sep = '|';
+    }
+    fprintf(stderr, " [?|param]\n");
+    sep = ' ';
+    fprintf(stderr, " %s set [archive]", argv0);
+    for (cf_index = 0; config_files[cf_index].cmd_name != NULL; cf_index++) {
+       fprintf(stderr, "%c%s", sep, config_files[cf_index].cmd_name);
+       sep = '|';
+    }
+    fprintf(stderr, " param=?|value\n");
+    sep = ' ';
+    fprintf(stderr, " %s edit [archive]", argv0);
+    for (cf_index = 0; config_files[cf_index].cmd_name != NULL; cf_index++) {
+       fprintf(stderr, "%c%s", sep, config_files[cf_index].cmd_name);
+       sep = '|';
+    }
+    fprintf(stderr, " [editor]\n");
+    fprintf(stderr, " %s save|restore archive\n", argv0);
+    fprintf(stderr, " %s copy source-archive archive\n", argv0);
+    exit(1);
+}
+
+/* Lock a configuration file.  A parallel file is created with the same name as
+   the file to be locked, with the CONFIG_LOCK_SUFFIX appended to the name.
+   This will contain the PID of the program that did the lock.  If a program
+   dies holding the lock, this is detected, and the lock file removed.  This
+   method is reportedly broken for the general case on NFS filesystems, but
+   seems to work when all locking accesses are from the same system. */
+
+void lock_config_file(char *filename) {
+    int i, rc, attempts = MAX_LOCK_ATTEMPTS;
+    long int lpid;
+    char *lfn = (char *)malloc(strlen(filename)+strlen(CONFIG_LOCK_SUFFIX) +1);
+
+    strcpy(lfn, filename);
+    strcat(lfn, CONFIG_LOCK_SUFFIX);
+    while (--attempts >= 0) {
+       i = open(lfn, O_WRONLY | O_CREAT | O_EXCL, 0644);
+       if (i != -1) {
+           sprintf(inbuf, "%d", getpid());
+           write(i, inbuf, strlen(inbuf));
+           close(i);
+           free(lfn);
+           return;
+       }
+       if (errno != EEXIST) {
+               perror(lfn);
+               fprintf(stderr, "%s: Could not open interlock file\n", argv0);
+               free(lfn);
+               exit(3);
+       }
+       i = open(lfn, O_RDONLY);
+       if (i != -1) {
+           rc = read(i, inbuf, sizeof(inbuf) - 1);
+           close(i);
+           if (rc) {
+               lpid = strtol(inbuf, NULL, 10);
+               if (kill(lpid, 0) == -1 && errno == ESRCH) unlink(lfn);
+           }
+       }
+       sleep(1);
+    }
+    fprintf(stderr, "%s: Could not establish interlock file %s\n", argv0, lfn);
+    free(lfn);
+    exit(3);
+}
+
+/* Unlock a configuration file - simply remove the lock file */
+
+void unlock_config_file(char *filename) {
+    int i;
+    char *lfn = (char *)malloc(strlen(filename)+strlen(CONFIG_LOCK_SUFFIX) +1);
+
+    strcpy(lfn, filename);
+    strcat(lfn, CONFIG_LOCK_SUFFIX);
+    i = unlink(lfn);
+    if (i) {
+       perror(lfn);
+       fprintf(stderr, "%s: Could not remove interlock file\n", argv0);
+       exit(3);
+    }
+    free(lfn);
+}
+
+/* Routine to copy a complete set of configuration files */
+
+void copy_configs(char *source, char *destination) {
+    int cf_index;
+    char *ifn, *ofn, *tfn;
+    FILE *input, *output;
+
+    for (cf_index = 0; config_files[cf_index].cmd_name != NULL; cf_index++) {
+       ofn = config_files[cf_index].file_name;
+       if (ofn[0] == 0)
+               ofn = find_cfg_file(config_files[cf_index].cmd_name);
+       ifn = working_cfg_file(source, ofn);
+       ofn = working_cfg_file(destination, ofn);
+       tfn = (char *)malloc(strlen(ofn)
+               + strlen(CONFIG_PREV_SUFFIX) + strlen(CONFIG_NEW_SUFFIX));
+       lock_config_file(ifn);
+       lock_config_file(ofn);
+       input = fopen(ifn, "r");
+       if (!input) {
+           int ern = errno;
+           unlock_config_file(ofn);
+           unlock_config_file(ifn);
+           free(ofn);
+           free(ifn);
+           if (ern == ENOENT) continue;   /* This config file not present */
+           fprintf(stderr, "%s: Cannot open to copy from %s\n", argv0, ifn);
+           exit(2);
+       }
+       strcpy(tfn, ofn);
+       strcat(tfn, CONFIG_PREV_SUFFIX);
+       unlink(tfn);    /* Remove any previous previous version */
+       link(ofn, tfn); /* Create link for new previous version */
+       strcpy(tfn, ofn);
+       strcat(tfn, CONFIG_NEW_SUFFIX);
+       output = fopen(tfn, "w");
+       if (!output) {
+           fprintf(stderr, "%s: Cannot open to write into %s\n", argv0, tfn);
+           exit(2);
+       }
+       while (fgets(inbuf, sizeof(inbuf), input))
+           if (fputs(inbuf, output) == EOF) {
+               fprintf(stderr, "%s: Error writing copy to %s\n", argv0, tfn);
+               exit(2);
+           }
+       fclose(input);
+       fclose(output);
+       if (rename(tfn, ofn)) {
+           perror(ofn);
+           fprintf(stderr, "%s: Cannot rename temporary copy %s\n",
+                                       argv0, tfn);
+           exit(2);
+       }
+       unlock_config_file(ofn);
+       unlock_config_file(ifn);
+       free(ofn);
+       free(ifn);
+    }
+}
+
+/* Main program entry point (start here) */
+
+int main(int argc, char **argv) {
+    int i, a, setting, editing, query, found, psize, cf_index;
+    int cmd_restore, cmd_copy;
+    char *archive = NULL;
+    char *use_cfg, *cp, *np, *param, *value, *tfn;
+    struct stat stb;
+    struct dirent *ne;
+    FILE *input, *output;
+    DIR *rmd;
+
+    argv0 = base_pointer(*argv);
+    if (argc < 3) usage();
+
+
+    /* save and restore commands
+     *
+     * move all configuration files (as listed in the config_files array)
+     * between their normal location and a subdirectory in the saved config
+     * directory (SAVED_CONFIGS_DIR).  This subdirectory name is the name of
+     * the archive listed in the command, and is created during a save if it
+     * does not exist.  If it does exist, it is renamed with a new suffix
+     * (CONFIG_PREV_SUFFIX) to keep one previous copy.  If there is already a
+     * subdirectory with that suffix, it (and it's contents) are deleted.
+     * A restore will first rename each target config file with the suffix,
+     * deleting any previous one with that name, and put the restored copy in.
+     */
+
+    cmd_restore = strcmp(argv[1], CMD_RESTORE) == 0;
+    cmd_copy = strcmp(argv[1], CMD_COPY) == 0;
+    setting = strcmp(argv[1], CMD_SET) == 0;
+    editing = strcmp(argv[1], CMD_EDIT) == 0;
+    if (strcmp(argv[1], CMD_SAVE) == 0 || cmd_restore || cmd_copy) {
+       if (cmd_copy) {
+           if (argc != 4) usage();
+           a = 3;
+       } else {
+           if (argc != 3) usage();
+           a = 2;
+       }
+       if (strcmp(argv[a], ".") == 0
+        || strncmp(argv[a], "..", 2) == 0
+        || strchr(argv[a], '/')) {
+           fprintf(stderr, "%s: Saved configuration name \"%s\" cannot be "
+                       "., start with .., or contain a / character\n",
+                       argv0, argv[a]);
+           return 2;
+       }
+       archive = (char *)malloc(strlen(SAVED_CONFIGS_DIR) + 1
+                       + strlen(argv[a]) + 1);
+       strcpy(archive, SAVED_CONFIGS_DIR);
+       strcat(archive, "/");
+       strcat(archive, argv[a]);
+       i = stat(archive, &stb);
+
+       /* restore from archive */
+
+       if (cmd_restore) {
+           if (i || !(S_ISDIR(stb.st_mode))) {
+               fprintf(stderr,
+                   "%s: There is no saved configuration named \"%s\"\n",
+                       argv0, argv[a]);
+               free(archive);
+               return 2;
+           }
+           copy_configs(argv[a], NULL);
+
+       /* save to archive, or copy archive-src to archive */
+
+       } else {
+           if (cmd_copy) {     /* Validate existence of archive1 */
+               np = (char *)malloc(strlen(SAVED_CONFIGS_DIR) + 1
+                       + strlen(argv[2]) + 1);
+               strcpy(np, SAVED_CONFIGS_DIR);
+               strcat(np, "/");
+               strcat(np, argv[2]);
+               if (stat(np, &stb) || !(S_ISDIR(stb.st_mode))) {
+                   fprintf(stderr,
+                       "%s: There is no saved configuration named \"%s\"\n",
+                               argv0, argv[2]);
+                   free(np);
+                   return 2;
+               }
+               free(np);
+           }
+           if (!i) {   /* create suffixed directory if previous save done */
+               cp = (char *)malloc(strlen(archive) + 2);
+               strcpy(cp, archive);
+               strcat(cp, CONFIG_PREV_SUFFIX);
+               rmd = opendir(cp);
+               if (rmd) {
+                   while ((ne = readdir(rmd))) {
+                       if (strcmp(ne->d_name, ".") == 0
+                        || strcmp(ne->d_name, "..") == 0) continue;
+                       np = (char *)malloc(strlen(cp) + 1
+                                       + strlen(ne->d_name) + 1);
+                       strcpy(np, cp);
+                       strcat(np, "/");
+                       strcat(np, ne->d_name);
+                       unlink(np);   /* Errors will report with rmdir() */
+                       free(np);
+                   }
+                   closedir(rmd);
+                   errno = ENOENT;
+                   rmdir(cp);
+               }
+               if (errno != ENOENT) {
+                   perror(cp);
+                   fprintf(stderr, "%s: Cannot remove backup copy\n",
+                           argv0);
+                   free(cp);
+                   free(archive);
+                   return 2;
+               }
+               i = rename(archive, cp);
+               if (i) {
+                   perror(archive);
+                   fprintf(stderr,
+                       "%s: Cannot rename existing copy to backup copy\n",
+                           argv0);
+                   free(cp);
+                   free(archive);
+                   return 2;
+               }
+               free(cp);
+           }
+           for (cf_index=0; config_files[cf_index].cmd_name!=NULL; cf_index++)
+               if (strcmp(argv[a], config_files[cf_index].cmd_name) == 0) {
+                   fprintf(stderr, "%s: Cannot use reserved name \"%s\" for"
+                                       " a saved archive\n", argv0, argv[a]);
+                   return 2;
+               }
+           i = mkdir(archive, 0755);
+           if (i) {
+               mkdir(SAVED_CONFIGS_DIR, 0755);  /* In case it is missing */
+               i = mkdir(archive, 0755);
+           }
+           if (i) {
+               perror(archive);
+               fprintf(stderr, "%s: Cannot create directory for saving\n",
+                           argv0);
+               free(archive);
+               return 2;
+           }
+           if (cmd_copy)
+               copy_configs(argv[2], argv[a]);
+           else
+               copy_configs(NULL, argv[a]);
+       }
+       free(archive);
+
+
+    /* set and get commands
+     */
+
+    } else if (setting || strcmp(argv[1], CMD_GET) == 0 || editing) {
+       for (a = 2; a <= 3; a++) {
+           if (argc <= a) usage();
+           for (cf_index=0; config_files[cf_index].cmd_name!=NULL; cf_index++)
+               if (strcmp(argv[a], config_files[cf_index].cmd_name) == 0)
+                   break;
+           if (config_files[cf_index].cmd_name != NULL) break;
+           archive = argv[a];
+       }
+       if (config_files[cf_index].cmd_name == NULL) usage();
+       use_cfg = config_files[cf_index].file_name;
+       if (use_cfg[0] == 0)
+           use_cfg = find_cfg_file(config_files[cf_index].cmd_name);
+       use_cfg = working_cfg_file(archive, use_cfg);
+
+       /* get with no parameter means get whole file */
+
+       if (!editing && argc <= (a+1)) {
+           if (setting) usage();
+           input = fopen(use_cfg, "r");
+           if (input == NULL) {
+               if (errno == ENOENT)
+                   fprintf(stderr,
+                       "%s: There is no saved configuration named \"%s\"\n",
+                       argv0, archive);
+               else
+                   perror(use_cfg);
+               return 2;
+           }
+           while (fgets(inbuf, sizeof(inbuf), input))
+               fputs(inbuf, stdout);
+           fclose(input);
+           free(use_cfg);
+           return 0;
+       }
+
+       /* Verify that we can use simple parameter/value parsing on this */
+
+       if (!editing && config_files[cf_index].usespace < 0) {
+           if (setting)
+               fprintf(stderr, "%s: %s file format too complicated, use edit "
+                       "command\n", argv0, config_files[cf_index].cmd_name);
+           else
+               fprintf(stderr, "%s: %s file format too complicated, use get "
+                       "command with no param\n", argv0,
+                       config_files[cf_index].cmd_name);
+           free(use_cfg);
+           return 2;
+       }
+       if (editing) {
+           if (argc <= (a+1)) {
+               if (((cp = getenv("VISUAL")) == NULL || *cp == '\0') &&
+                   ((cp = getenv("EDITOR")) == NULL || *cp == '\0'))
+                   cp = DEFAULT_EDITOR;
+               param = (char *)malloc(strlen(cp)+1);
+               strcpy(param, cp);
+           } else {
+               if (argc != (a+2)) usage();
+               param = (char *)malloc(strlen(argv[a+1])+1);
+               strcpy(param, argv[a+1]);
+           }
+       } else {
+           param = (char *)malloc(strlen(argv[a+1])+1);
+           strcpy(param, argv[a+1]);
+       }
+
+       /* if setting, figure out if new value is in the same "word" as the
+        * parameter name with an = to delineate them
+        */
+       if (setting) {
+           for (cp = param; *cp ; cp++) if (*cp == '=') break;
+           if (*cp && cp[1] == 0) *cp = 0;
+           if (*cp) {
+               if (argc != (a+2)) usage();
+               value = cp + 1;
+           } else {
+               if (argc == (a+2)) {  /* if no value, use ? as default */
+                   value = "?";
+               } else {        /* otherwise value follows */
+                   if (argc == (a+3)) {
+                       value = argv[a+2];
+                       if (*value == '=') value++;
+                   } else if (argc == (a+4) && strcmp(argv[a+2], "=") == 0) {
+                       value = argv[a+3];
+                   } else
+                       usage();
+               }
+           }
+           while (cp > param && (cp[-1] == ' ' || cp[-1] == '\t')) cp--;
+           *cp = 0;
+           while(*value == ' ' || *value == '\t') value++;
+
+       /* not set, must be get or edit */
+       } else {
+           /* If doing get, make sure there are no extra arguments */
+           if (!editing && argc != (a+2)) usage();
+       }
+
+       /* Check for query.  For get this means list all values, while for set
+        * this means show the comments that are in the file before the value.
+        */
+       if (setting)
+           query = !(strcmp("?", value));
+       else
+           query = !(strcmp("?", param));
+
+       /* Now scan the file */
+
+       found = 0;
+       input = fopen(use_cfg, "r");
+       if (!input) {
+           fprintf(stderr, "%s: Cannot open to copy from %s\n", argv0,use_cfg);
+           exit(2);
+       }
+
+       /* If set/edit command, we create a link to the current file as previous
+        * version (so when we remove the current one, we have the backup), and
+        * create a new file with a different name, which will get renamed to
+        * the normal name at the end.
+        */
+       if ((setting && !query) || editing) {
+           lock_config_file(use_cfg);
+           tfn = (char *)malloc(strlen(use_cfg)
+                   + strlen(CONFIG_PREV_SUFFIX) + strlen(CONFIG_NEW_SUFFIX));
+           strcpy(tfn, use_cfg);
+           strcat(tfn, CONFIG_PREV_SUFFIX);
+           unlink(tfn);        /* Remove any previous previous version */
+           link(use_cfg, tfn); /* Create link for new previous version */
+           strcpy(tfn, use_cfg);
+           strcat(tfn, CONFIG_NEW_SUFFIX);
+           output = fopen(tfn, "w");
+           if (!output) {
+               unlock_config_file(use_cfg);
+               fprintf(stderr, "%s: Cannot open to write into %s\n",
+                                               argv0, tfn);
+               exit(2);
+           }
+       }
+
+       /* Now we scan the file */
+
+       psize = strlen(param);
+       savebufp = savebuf;
+       while (fgets(inbuf, sizeof(inbuf), input)) {
+           cp = inbuf + strlen(inbuf);
+
+           /* Strip trailing NL/CR */
+
+           while (cp > inbuf &&
+               (cp[-1] == '\n' || cp[-1] == '\r')) cp--;
+           *cp = 0;
+
+           /* Strip leading spaces/tabs */
+
+           cp = inbuf;
+           while (*cp == ' ' || *cp == '\t') cp++;
+
+           /* See if this is a comment or empty line */
+
+           if (*cp == '#' || *cp == 0) {
+               if (setting && query && *cp == '#') {
+                   cp++;       /* Save comments for set query output */
+                   i = strlen(cp);
+                   if (i < (sizeof(savebuf) - (savebufp - savebuf) - 2)) {
+                       strcpy(savebufp, cp);
+                       savebufp += i;
+                       *savebufp++ = '\n';
+                   }
+               }
+
+           /* Not a comment or empty line, must be part of a param/value */
+
+           } else if (!editing) {
+
+               /* if get command with ? for param, show all param lines */
+
+               if (!setting && query) {
+                   puts(cp);
+
+               /* get or set with param to look for, see if it is there */
+
+               } else {
+                   if (strncmp(param, cp, psize) == 0) {
+                       if (cp[psize] == 0 || cp[psize] == '='
+                        || cp[psize] == ' ' || cp[psize] == '\t') {
+
+                           found++;
+                           if (setting) {
+                               if (query) {
+
+                                   /* Found param, this a set query */
+
+                                   *savebufp = 0;
+                                   fputs(savebuf, stdout);
+                               } else {
+
+                                   /* Found param, this a set with value */
+
+                                   np = cp + psize;
+                                   while (*np == ' ' || *np == '\t') np++;
+                                   if (*np == '=') {
+                                       np++;
+                                       while (*np == ' ' || *np == '\t') np++;
+                                   }
+
+                                   /* Put new value where old value was */
+
+                                   i = (np - inbuf) + strlen(value) + 2;
+                                   if (i > sizeof(inbuf)) {
+                                       fprintf(stderr, "%s: New value for "
+                                               "\"%s\" is %d bytes too long\n",
+                                               argv0, param,
+                                               (int)(sizeof(inbuf)-i));
+                                       exit(2);
+                                   } else
+                                       strcpy(np, value);
+                               }
+
+                           /* Found param and doing a get so just print it */
+
+                           } else {
+                               puts(cp);
+                           }
+                       }
+                   }
+               }
+               savebufp = savebuf;     /* Saw new param, toss old comments */
+           }
+
+           /* If doing set or edit, copy all lines to replacement file */
+
+           if (editing || (setting && !query)) {
+               if (fprintf(output, "%s\n", inbuf) < 0) {
+                   unlock_config_file(use_cfg);
+                   fprintf(stderr, "%s: Error writing copy to %s\n",
+                                               argv0, tfn);
+               exit(2);
+               }
+           }
+       }
+       fclose(input);
+
+       /* If doing set, output is done, rename file to make it the real one */
+
+       if ((setting && !query) || editing) {
+           if (!found && !editing) {   /* Did not find param so we add it */
+               if (config_files[cf_index].usespace)
+                   fprintf(output, "%s %s\n", param, value);
+               else
+                   fprintf(output, "%s = %s\n", param, value);
+           }
+           fclose(output);
+           if (editing) {
+               cp = (char *)malloc(strlen(param) + 2 + strlen(tfn) + 2);
+               strcpy(cp, param);
+               strcat(cp, " '");
+               strcat(cp, tfn);
+               strcat(cp, "'");
+               system(cp);
+               free(cp);
+           }
+           if (rename(tfn, use_cfg)) {
+               perror(use_cfg);
+               unlock_config_file(use_cfg);
+               fprintf(stderr, "%s: Cannot rename temporary copy %s\n",
+                                       argv0, tfn);
+               exit(2);
+           }
+           unlock_config_file(use_cfg);
+           free(tfn);
+       }
+       free(param);
+       free(use_cfg);
+
+    /* Unknown command, just tell caller how to use the program */
+
+    } else
+       usage();
+
+    return 0;
+}
_______________________________________________
general mailing list
[email protected]
http://lists.openfabrics.org/cgi-bin/mailman/listinfo/general

To unsubscribe, please visit http://openib.org/mailman/listinfo/openib-general

Reply via email to