/* Copyright (C) 1995,96,97 Free Software Foundation, Inc. This file is part of GNU cfengine - written and maintained by Mark Burgess, Dept of Computing and Engineering, Oslo College, Dept. of Theoretical physics, University of Oslo This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /*******************************************************************/ /* */ /* Cfengine : remote client example */ /* */ /* Mark Burgess 1997 */ /* modified : Bas van der Vlies 1998 */ /* modified : Andrew Mayhew 2000 */ /* */ /*******************************************************************/ #define INET 1 /* * Bas */ #define FileVerbose if (VERBOSE || DEBUG || D2) fprintf #include "cf.defs.h" #include "cf.extern.h" /* * Bas */ struct CFrunItem { char *id; char *hostname; char *options; int port; struct CFrunItem *next; }; /* * Bas */ int MAXCHILD = 1; int FileFlag = 0; char OUTPUTDIR[bufsize]; struct Item *VCFRUNCLASSES = NULL; struct CFrunItem *VCFRUNHOSTLIST = NULL; char VCFRUNHOSTS[bufsize] = "cfrun.hosts"; char CFRUNOPTIONS[bufsize]; char CFLOCK[bufsize] = "dummy"; /*******************************************************************/ /* Functions internal to cfrun.c */ /*******************************************************************/ void CheckOptsAndInit(int argc,char **argv); int ConnectToServer(struct CFrunItem *cfip, int StoreInFile); void SendClassData(int sd, char *sendbuffer); void CheckAccess(char *users); void cfrunSyntax(); int CheckHostOptions(char *options); /* * Bas van der Vlies */ void AppendCFrunItem(struct CFrunItem **liststart, char *hostname, int port, char *options); int IsCFrunItemIn(struct CFrunItem *list, char *hostname, int port); /*******************************************************************/ /* Level 0 : Main */ /*******************************************************************/ int main (argc,argv) int argc; char **argv; { struct CFrunItem *cfip; int i=0; int status; int pid; CheckOptsAndInit(argc,argv); /* * Bas */ cfip = VCFRUNHOSTLIST; while (cfip != NULL ) { if (i < MAXCHILD) { if (fork() == 0) /* child */ { printf("cfrun: calling %s - %d\n",cfip->hostname, i); Debug("pid = %d i = %d\n", getpid(), i); if (ConnectToServer(cfip, FileFlag)) { printf("Connection %s done...\n", cfip->id); } else { printf("Connection %s refused...\n", cfip->id); } exit(0); } else { /* parent */ i++; } } else { pid = wait(&status); Debug("wait result pid = %d number %d\n", pid, i); i--; } if ( i < MAXCHILD ) { cfip = cfip->next; } } // end forking while while (i > 0 ) { pid = wait(&status); Debug("Child pid = %d ended\n", pid); i--; } exit(0); return 0; /* Keep ANSI compilers happy */ } /********************************************************************/ /* Level 1 */ /********************************************************************/ /* * Bas */ int CheckHostOptions(options) char *options; { char *ptr1, rest[bufsize], dummy[bufsize]; int port; bzero(rest, bufsize); bzero(dummy, bufsize); ptr1 = strstr(options, "port="); if ( ptr1 != NULL ) { sscanf(ptr1," port=%d %[^#\n]" , &port, rest ); Debug("cfrun: using port = %d\n", port); /* * remove port=%d from option string */ strncpy(dummy, options, strlen(options) - strlen(ptr1)); strcpy(options, dummy); strcat(options, rest); return(port); } else return(0); } void CheckOptsAndInit(argc,argv) int argc; char **argv; { char buffer[maxvarsize], options[bufsize], line[bufsize], filename[bufsize], * sp; struct Item *ip; struct CFrunItem *cfip; FILE *fp; int i; int optgroup = 0; int port; /* Separate command args into options and classes */ bzero(CFRUNOPTIONS,bufsize); for (i = 1; i < argc; i++) { if (optgroup == 0) { if (strncmp(argv[i],"-h",2) == 0) { cfrunSyntax(); } else if (strncmp(argv[i],"-f",2) == 0) { i++; if ((i >= argc) || (strncmp(argv[i],"-",1) == 0)) { printf("Error: No filename listed after -f option.\n"); cfrunSyntax(); exit(0); } bzero(VCFRUNHOSTS,bufsize); strcat(VCFRUNHOSTS,argv[i]); Debug("cfrun: cfrun file = %s\n",VCFRUNHOSTS); } else if (strncmp(argv[i],"-d",2) == 0) { DEBUG = true; VERBOSE = true; } else if (strncmp(argv[i],"-v",2) == 0) { VERBOSE=true; } else if (strncmp(argv[i],"-S",2) == 0) { SILENT = true; } else if (strncmp(argv[i],"--",2) == 0) { optgroup++; } else { printf("Error: Unknown option.\n"); cfrunSyntax(); exit(0); } } else if (optgroup == 1) { if (strncmp(argv[i],"--",2) == 0) { optgroup++; } else { strcat(CFRUNOPTIONS,argv[i]); strcat(CFRUNOPTIONS," "); } } else { AppendItem(&VCFRUNCLASSES,argv[i],""); } } Debug("CFRUNOPTIONS string: %s\n",CFRUNOPTIONS); for (ip = VCFRUNCLASSES; ip != NULL; ip=ip->next) { Debug("Class item: %s\n",ip->name); } if (uname(&VSYSNAME) == -1) { perror("uname "); printf("cfrun: uname couldn't get kernel name info!!\n"); exit(1); } if ((strlen(VDOMAIN) > 0) && !strchr (VSYSNAME.nodename, '.')) { sprintf(VFQNAME,"%s.%s",VSYSNAME.nodename,VDOMAIN); } else { sprintf(VFQNAME,"%s",VSYSNAME.nodename); } /* Read hosts file */ if ( ((sp=getenv(CFINPUTSVAR)) != NULL) && (!strchr(VCFRUNHOSTS, '/')) ) { strcpy(filename,sp); if (filename[strlen(filename)-1] != '/') { strcat(filename,"/"); } } strcat(filename,VCFRUNHOSTS); if ((fp = fopen(filename,"r")) == NULL) /* Open root file */ { printf("Unable to open %s\n",filename); return; } while (!feof(fp)) { bzero(buffer,maxvarsize); bzero(options,bufsize); bzero(line,bufsize); ReadLine(line,bufsize,fp); if (strncmp(line,"domain",6) == 0) { sscanf(line,"domain = %295[^# \n]",VDOMAIN); Verbose("Domain name = %s\n",VDOMAIN); continue; } if (strncmp(line,"maxchild", strlen("maxchild")) == 0) { sscanf(line,"maxchild = %295[^# \n]", buffer); if ( (MAXCHILD = atoi(buffer)) == 0 ) MAXCHILD = 1; Verbose("cfrun: maxchild = %d\n", MAXCHILD); continue; } if (strncmp(line,"outputdir", strlen("outputdir")) == 0) { sscanf(line,"outputdir = %295[^# \n]", OUTPUTDIR); Verbose("cfrun: outputdir = %s\n", OUTPUTDIR); if ( opendir(OUTPUTDIR) == NULL) { printf("Directory %s does not exists\n", OUTPUTDIR); exit(1); } FileFlag=1; continue; } if (strncmp(line,"access",6) == 0) { for (sp = line; (*sp != '=') && (*sp != '\0'); sp++) { } if (*sp == '\0' || *(++sp) == '\0') { continue; } CheckAccess(sp); continue; } sscanf(line,"%295s %[^#\n]",buffer,options); if (buffer[0] == '#') { continue; } if (strlen(buffer) == 0) { continue; } if ((!strstr(buffer,".")) && (strlen(VDOMAIN) > 0)) { strcat(buffer,"."); strcat(buffer,VDOMAIN); } /* Bas van der Vlies */ port = CheckHostOptions(options); if (! IsCFrunItemIn(VCFRUNHOSTLIST, buffer, port)) { AppendCFrunItem(&VCFRUNHOSTLIST,buffer,port,options); } } /* end while */ for (cfip = VCFRUNHOSTLIST; cfip != NULL; cfip=cfip->next) { Debug("host item: %s %d (%s)\n",cfip->hostname, cfip->port, cfip->options); } fclose(fp); } /* * Bas */ void AppendCFrunItem(liststart, hostname, port, options) struct CFrunItem **liststart; char *hostname, *options; int port; { struct CFrunItem *ip, *lp; char *sp,*spe; char *id; EditVerbose("Appending [%s,%d]\n",hostname, port); if ((ip = (struct CFrunItem *)malloc(sizeof(struct CFrunItem))) == NULL) { CfLog(cferror,"","malloc"); FatalError(""); } if ((id = malloc(strlen(hostname)+30)) == NULL) { CfLog(cferror,"","malloc"); FatalError(""); } if ((sp = malloc(strlen(hostname)+extra_space)) == NULL) { CfLog(cferror,"","malloc"); FatalError(""); } if (*liststart == NULL) { *liststart = ip; } else { for (lp = *liststart; lp->next != NULL; lp=lp->next) { } lp->next = ip; } if ((options!= NULL) && (spe = malloc(strlen(options)+2)) == NULL) { CfLog(cferror,"","malloc"); FatalError(""); } strcpy(sp,hostname); /* this a uniq id */ sprintf(id,"%s:%d", hostname, port); ip->hostname = sp; ip->port = port; ip->next = NULL; ip->id = id; if (options != NULL) { strcpy(spe,options); ip->options = spe; } else { ip->options = NULL; } } /* * Bas */ int IsCFrunItemIn(list, hostname, port) struct CFrunItem *list; char *hostname; int port; { struct CFrunItem *ptr; char id[bufsize]; /* this a uniq id */ sprintf(id,"%s:%d", hostname, port); if ((hostname == NULL) || (strlen(hostname) == 0)) { return(true); } for (ptr = list; ptr != NULL; ptr=ptr->next) { if (strcmp(ptr->id,id) == 0) { return(true); } } return(false); } /********************************************************************/ int ConnectToServer(ptr, StoreInFile) struct CFrunItem *ptr; int StoreInFile; { struct hostent *hp; struct sockaddr_in raddr; char sendbuffer[bufsize]; char recvbuffer[bufsize]; char filebuffer[bufsize]; struct servent *server; int sd,err,n_read; char *sp; FILE *fp; if (StoreInFile) { if (ptr->port) { sprintf(filebuffer, "%s/%s_%d", OUTPUTDIR, ptr->hostname, ptr->port); } else { sprintf(filebuffer, "%s/%s", OUTPUTDIR, ptr->hostname); } fp = fopen(filebuffer, "w"); } else { fp = stdout; } FileVerbose(fp, "Connecting to server %s to port %d with options %s %s\n", ptr->hostname, ptr->port, ptr->options,CFRUNOPTIONS); if ((hp = gethostbyname(ptr->hostname)) == NULL) { printf("Unknown host: %s\n", ptr->hostname); printf("Make sure that fully qualified names can be looked up at your site!\n"); printf("i.e. prep.ai.mit.edu, not just prep. If you use NIS or /etc/hosts\n"); printf("make sure that the full form is registered too as an alias!\n"); exit(1); } bzero(&raddr,sizeof(raddr)); /* * Bas: If port is set then use else default one */ if (ptr->port) { raddr.sin_port = ntohs(ptr->port); } else { if ((server = getservbyname(CFENGINE_SERVICE,"tcp")) == NULL) { perror("getservbyname"); exit (1); } else { raddr.sin_port = (unsigned int) server->s_port; } } raddr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; raddr.sin_family = AF_INET; Debug("Trying to connect to %s = %s, port h=%d n=%d\n", ptr->hostname, inet_ntoa(raddr.sin_addr), ntohs(raddr.sin_port), raddr.sin_port); if ((sd = socket(AF_INET,SOCK_STREAM,0)) == -1) { perror("socket"); exit(1); } signal(SIGALRM,(void *)TimeOut); alarm(15); /* 15 second timeout */ if (err=connect(sd,(void *) &raddr,sizeof(raddr)) == -1) { alarm(0); /* perror("connect"); */ signal(SIGALRM,SIG_DFL); printf("Host %s isn't talking to anyone\n\n", ptr->id); return false; } alarm(0); signal(SIGALRM,SIG_DFL); if (! IdentifyForVerification(sd,VFQNAME)) { printf("%s: server registration procedure failed",VPREFIX); errno = EPERM; return false; } sprintf(sendbuffer,"EXEC %s %s",ptr->options,CFRUNOPTIONS); if (send(sd,sendbuffer,bufsize,0) == -1) { printf("Transmission rejected"); close(sd); return false; } SendClassData(sd,sendbuffer); FileVerbose(fp, "\n%s replies..\n\n", ptr->hostname); while (true) { bzero(recvbuffer,bufsize); /* * Bas: without MSG_WAITALL we did not got all the output, was 0 */ if ((n_read = recv(sd, recvbuffer, bufsize, MSG_WAITALL)) == -1) { if (errno == EINTR) { continue; } close(sd); return true;; } if ((sp = strstr(recvbuffer,CFD_TERMINATOR)) != NULL) { *sp = '\0'; fprintf(fp,"%s",recvbuffer); break; } if ((sp = strstr(recvbuffer,"BAD:")) != NULL) { *sp = '\0'; fprintf(fp,"%s",recvbuffer); break; } if (n_read == 0) { break; } if (strlen(recvbuffer) == 0) { continue; } if (strstr(recvbuffer,"too soon")) { printf("%s",recvbuffer); } if (strstr(recvbuffer,"cfd:")) /* Don't print messages fom cfd unless -v */ { FileVerbose(fp, "%s",recvbuffer); sleep(1); continue; } FileVerbose(fp, "%s",recvbuffer); } close(sd); FileVerbose(fp,"\n========================================================================\n"); return true; } /********************************************************************/ /* Level 2 */ /********************************************************************/ void SendClassData(sd, sendbuffer) int sd; char *sendbuffer; { struct Item *ip; int used; char *sp; sp = sendbuffer; used = 0; bzero(sendbuffer,bufsize); for (ip = VCFRUNCLASSES; ip != NULL; ip = ip->next) { if (used + strlen(ip->name) +2 > bufsize) { if (send(sd,sendbuffer,bufsize,0) == -1) { perror("send"); return; } used = 0; sp = sendbuffer; bzero(sendbuffer,bufsize); } strcat(sendbuffer,ip->name); strcat(sendbuffer," "); sp += strlen(ip->name)+1; used += strlen(ip->name)+1; } if (used + strlen(CFD_TERMINATOR) +2 > bufsize) { if (send(sd,sendbuffer,bufsize,0) == -1) { perror("send"); return; } used = 0; sp = sendbuffer; bzero(sendbuffer,bufsize); } sprintf(sp,CFD_TERMINATOR); if (send(sd,sendbuffer,bufsize,0) == -1) { perror("send"); return; } } /********************************************************************/ void CheckAccess(users) char *users; { char id[maxvarsize], *sp; struct passwd *pw; int uid,myuid; myuid = getuid(); if (myuid == 0) { return; } for (sp = users; *sp != '\0'; sp++) { id[0] = '\0'; sscanf(sp,"%295[^,:]",id); sp += strlen(id); if (isalpha(id[0])) { if ((pw = getpwnam(id)) == NULL) { printf("cfrun: No such user (%s) in password database\n",id); exit(0); } if (pw->pw_uid == myuid) { return; } } else { uid = atoi(id); if (uid == myuid) { return; } } } printf("cfrun: you have not been granted permission to run cfrun\n"); exit(0); } void cfrunSyntax() { printf("Usage: cfrun [-f cfrun.hosts|-h|-d|-S|-v] [-- OPTIONS [-- CLASSES]]\n"); printf("-f cfrun.hosts\tcfrun file to read in list of hosts (see below for syntax.)\n"); printf("-h\t\tGet this help message.\n"); printf("-d\t\tDebug mode, turns on verbose as well.\n"); printf("-S\t\tSilent mode.\n"); printf("-v\t\tVerbose mode.\n"); printf("-- OPTIONS\tArguments to be passed to host application.\n"); printf("-- CLASSES\tClasses to be defined for the hosts.\n\n"); printf("cfrun.hosts file syntax:\n"); printf("# starts a comment\n"); printf("domain = [domain]\t# Domain to use for connection(s).\n"); printf("maxchild = [num]\t# Maximum number of children to spawn during run.\n"); printf("outputdir = [dir]\t# Directory where to put host output files.\n"); printf("access = [user]\t\t# User allowed to do cfrun?\n"); printf("[host]\t\t\t# One host per line list to cycle through.\n"); printf("\t\t\t# Only the hosts are required for cfrun to operate.\n"); exit(0); } /********************************************************************/ /* Toolkit */ /********************************************************************/ void FatalError(s) char *s; { exit(1); } void AddMultipleClasses(classlist) char *classlist; { /* hack */ } /* EOF */