/* $Id: seq.c,v 1.11 2001/08/13 21:06:36 garbled Exp $ */
/*
* Copyright (c) 1998, 1999, 2000
* Tim Rightnour. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Tim Rightnour.
* 4. The name of Tim Rightnour may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY TIM RIGHTNOUR ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL TIM RIGHTNOUR BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/wait.h>
#include "../common/common.h"
#if !defined(lint) && defined(__NetBSD__)
__COPYRIGHT(
"@(#) Copyright (c) 1998, 1999, 2000\n\
Tim Rightnour. All rights reserved\n");
__RCSID("$Id: seq.c,v 1.11 2001/08/13 21:06:36 garbled Exp $");
#endif
#ifndef __P
#define __P(protos) protos
#endif
/* externs */
extern int errno;
void do_command __P((char **, int, char *));
node_t * check_seq __P((void));
/* globals */
int debug, errorflag, exclusion, grouping;
int seqnumber;
char **rungroup;
char **lumplist;
node_t *nodelink;
group_t *grouplist;
char *progname;
/*
* seq is a cluster management tool based upon the IBM tool dsh.
* It allows a user, or system administrator to issue
* commands in paralell on a group of machines.
*/
int main(int argc, char *argv[])
{
extern char *optarg;
extern int optind;
int someflag, ch, i, allflag, showflag;
char *p, *group, *nodename, *username, *q;
char **exclude, **grouptemp;
someflag = 0;
seqnumber = -1;
showflag = 0;
exclusion = 0;
debug = 0;
errorflag = 0;
allflag = 0;
grouping = 0;
username = NULL;
nodename = NULL;
group = NULL;
rungroup = malloc(sizeof(char **) * GROUP_MALLOC);
if (rungroup == NULL)
bailout(__LINE__);
exclude = malloc(sizeof(char **) * GROUP_MALLOC);
if (exclude == NULL)
bailout(__LINE__);
progname = p = q = argv[0];
while (progname != NULL) {
q = progname;
progname = (char *)strsep(&p, "/");
}
progname = strdup(q);
while ((ch = getopt(argc, argv, "?adeiqg:l:w:x:")) != -1)
switch (ch) {
case 'a': /* set the allrun flag */
allflag = 1;
break;
case 'd': /* set the debug flag */
debug = 1;
break;
case 'e': /* we want stderr to be printed */
errorflag = 1;
break;
case 'i': /* we want tons of extra info */
debug = 1;
break;
case 'l': /* invoke me as some other user */
username = strdup(optarg);
break;
case 'q': /* just show me some info and quit */
showflag = 1;
break;
case 'g': /* pick a group to run on */
i = 0;
grouping = 1;
for (p = optarg; p != NULL; ) {
group = (char *)strsep(&p, ",");
if (group != NULL) {
if (((i+1) % GROUP_MALLOC) != 0) {
rungroup[i++] = strdup(group);
} else {
grouptemp = realloc(rungroup,
i*sizeof(char **) +
GROUP_MALLOC*sizeof(char *));
if (grouptemp != NULL)
rungroup = grouptemp;
else
bailout(__LINE__);
rungroup[i++] = strdup(group);
}
}
}
group = NULL;
break;
case 'x': /* exclude nodes, w overrides this */
exclusion = 1;
i = 0;
for (p = optarg; p != NULL; ) {
nodename = (char *)strsep(&p, ",");
if (nodename != NULL) {
if (((i+1) % GROUP_MALLOC) != 0) {
exclude[i++] = strdup(nodename);
} else {
grouptemp = realloc(exclude,
i*sizeof(char **) +
GROUP_MALLOC*sizeof(char *));
if (grouptemp != NULL)
exclude = grouptemp;
else
bailout(__LINE__);
exclude[i++] = strdup(nodename);
}
}
}
break;
case 'w': /* perform operation on these nodes */
someflag = 1;
i = 0;
for (p = optarg; p != NULL; ) {
nodename = (char *)strsep(&p, ",");
if (nodename != NULL)
(void)nodealloc(nodename);
}
break;
case '?': /* you blew it */
(void)fprintf(stderr,
"usage: %s [-aeiq] [-g rungroup1,...,rungroupN] "
"[-l username] [-x node1,...,nodeN] [-w node1,..,nodeN] "
"[command ...]\n", progname);
exit(EXIT_FAILURE);
break;
default:
break;
}
if (!someflag)
parse_cluster(exclude);
argc -= optind;
argv += optind;
if (showflag) {
do_showcluster(DEFAULT_FANOUT);
exit(EXIT_SUCCESS);
}
do_command(argv, allflag, username);
exit(EXIT_SUCCESS);
}
/* this should be atomic, but *hello* this is *userland* */
static void
test_and_set(void)
{
int i;
char *p, *seqfile;
char buf[MAXBUF];
FILE *sd;
node_t *nodeptr, *nodex;
p = buf;
i = 0;
seqfile = getenv("SEQ_FILE");
if (seqfile == NULL) {
(void)sprintf(buf, "/tmp/%d.%s", (int)getppid(), progname);
seqfile = strdup(buf);
}
sd = fopen(seqfile, "r");
if (sd == NULL)
sd = fopen(seqfile, "w");
else {
fscanf(sd, "%s", p);
seqnumber = atoi(p);
fclose(sd);
sd = fopen(seqfile, "w");
}
if (sd == NULL)
bailout(__LINE__);
nodeptr = check_seq();
for (nodex = nodelink; nodex != nodeptr; nodex = nodex->next)
i++;
(void)fprintf(sd, "%d", i);
fclose(sd);
seqnumber = i;
}
/* return the node number of the next node in the seqence. */
node_t *
check_seq()
{
int i, g;
node_t *nodeptr;
if (seqnumber == -1)
return(nodelink);
g = seqnumber;
i = 0;
for (nodeptr = nodelink; (nodeptr && i < g);
nodeptr = nodeptr->next)
i++;
if (nodeptr->next == NULL)
return(nodelink);
return(nodeptr->next);
}
/*
* Do the actual dirty work of the program, now that the arguments
* have all been parsed out.
*/
void
do_command(argv, allrun, username)
char **argv;
char *username;
int allrun;
{
FILE *fd, *in;
char buf[MAXBUF];
int status, i, piping;
char *p, *command, *rsh;
node_t *nodeptr;
i = 0;
piping = 0;
in = NULL;
if (debug) {
if (username != NULL)
(void)printf("As User: %s\n", username);
}
/* construct the command from the remains of argv */
command = (char *)malloc(MAXBUF * sizeof(char));
memcpy(command, "\0", MAXBUF * sizeof(char));
for (p = *argv; p != NULL; p = *++argv ) {
strcat(command, p);
strcat(command, " ");
}
if (debug) {
(void)printf("Do Command: %s\n", command);
}
if (strcmp(command,"") == 0) {
piping = 1;
if (isatty(STDIN_FILENO) && piping)
/* are we a terminal? then go interactive! */
(void)printf("%s>", progname);
in = fdopen(STDIN_FILENO, "r");
command = fgets(buf, sizeof(buf), in);
/* start reading stuff from stdin and process */
if (command != NULL)
if (strcmp(command,"\n") == 0)
command = NULL;
}
if (allrun)
test_and_set();
while (command != NULL) {
if (!allrun)
test_and_set();
i = seqnumber;
nodeptr = check_seq();
if (debug)
(void)printf("On node: %s\n", nodeptr->name);
pipe(nodeptr->out.fds);
/* we set up pipes for each node, to prepare for the
* oncoming barrage of data */
pipe(nodeptr->err.fds);
switch (fork()) { /* its the ol fork and switch routine eh? */
case -1:
bailout(__LINE__);
break;
case 0:
if (dup2(nodeptr->out.fds[1], STDOUT_FILENO) != STDOUT_FILENO)
bailout(__LINE__);
if (dup2(nodeptr->err.fds[1], STDERR_FILENO) != STDERR_FILENO)
bailout(__LINE__);
if (close(nodeptr->out.fds[0]) != 0)
bailout(__LINE__);
if (close(nodeptr->err.fds[0]) != 0)
bailout(__LINE__);
rsh = getenv("RCMD_CMD");
if (rsh == NULL)
rsh = strdup("rsh");
if (rsh == NULL)
bailout(__LINE__);
if (debug)
printf("%s %s %s\n", rsh, nodeptr->name, command);
if (username != NULL)
/* interestingly enough, this -l thing works great with ssh */
execlp(rsh, rsh, "-l", username, nodeptr->name,
command, (char *)0);
else
execlp(rsh, rsh, nodeptr->name, command, (char *)0);
bailout(__LINE__);
} /* end switch */
if (close(nodeptr->out.fds[1]) != 0)
/* now close off the useless stuff, and read the goodies */
bailout(__LINE__);
if (close(nodeptr->err.fds[1]) != 0)
bailout(__LINE__);
fd = fdopen(nodeptr->out.fds[0], "r"); /* stdout */
while ((p = fgets(buf, sizeof(buf), fd)))
(void)printf("%s:\t%s", nodeptr->name, p);
fclose(fd);
fd = fdopen(nodeptr->err.fds[0], "r"); /* stderr */
while ((p = fgets(buf, sizeof(buf), fd)))
if (errorflag)
(void)printf("%s:\t%s", nodeptr->name, p);
fclose(fd);
(void)wait(&status);
if (piping) {
if (isatty(STDIN_FILENO) && piping)
/* yes, this is code repetition, no need to adjust your monitor */
(void)printf("%s>", progname);
command = fgets(buf, sizeof(buf), in);
if (command != NULL)
if (strcmp(command,"\n") == 0)
command = NULL;
} else
command = NULL;
} /* while loop */
if (piping) {
/* I learned this the hard way */
fflush(in);
fclose(in);
}
}
syntax highlighted by Code2HTML, v. 0.9.1