[ofa-general] Re: [PATCH] infiniband-diags: Add ibcli program and man page

Ira Weiny weiny2 at llnl.gov
Thu Aug 9 18:44:01 PDT 2007


Hey Sasha, Dave,

See below...

On Thu, 9 Aug 2007 00:42:39 +0300
Sasha Khapyorsky <sashak at voltaire.com> wrote:

> Hi,
> 
> On 09:41 Wed 01 Aug     , davem at systemfabricworks.com wrote:
> > 
> >    Add ibcli program and man page to infiniband-diags.
> 
> [snip...]
> 
> 
> > +.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.
> 
>  From this description it looks that ibcli is trying to replace some
> known tools like text editor, cp, etc.. Which is probably fine, but I
> still be unsure about how such new tool will be useful - unlike this
> standard set of unix/linux utils is well known, universal and greatly
> flexible. I would crearly choose standard tools, but it is just my
> opinion.

LLNL will not use a tool like this.  We completely agree with Sasha as we use
standard tools and prefer text editors and simple config files to anything
which requires specialized tools or knowledge to modify.


> 
> I think it would be good to get opinions from people who are dealing with
> clusters configurations/administration about potential usability of such
> tool (ibcli).
> 
> Anybody could comment?

I have CC'ed in this email our lead SysAdmin on our IB clusters and I think he
will agree.  Trent do you have any coments?

Ira

> 
> (Please feel free to forward this question to anybody who could provide
> useful input.)
> 
> Sasha
> 
> > +
> > +.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