[ofa-general] [PATCH] infiniband-diags: Add ibcli program and man page
davem at systemfabricworks.com
davem at systemfabricworks.com
Wed Aug 1 07:41:58 PDT 2007
Add ibcli program and man page to infiniband-diags.
Signed-off-by: David A. McMillen <davem at systemfabricworks.com>
---
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 <davem at systemfabricworks.com>
+
+ * src/ibcli.c, man/ibcli.8: Add program and man page
+
2007-07-10 Hal Rosenstock <halr at voltaire.com>
* 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 < davem at systemfabricworks.com >
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;
+}
More information about the general
mailing list