/* 

        Copyright (C) 1995-2000
        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

*/

/*****************************************************************************/
/*                                                                           */
/* File: do.c                                                                */
/*                                                                           */
/*****************************************************************************/

#define INET

#include "cf.defs.h"
#include "cf.extern.h"

/*******************************************************************/

void GetHomeInfo()

{ DIR *dirh;
  struct dirent *dirp;
  struct Item *ip;

if (!IsPrivileged())                            
   {
   Debug("Not root, so skipping GetHomeInfo()\n");
   return;
   }

if (!MountPathDefined())
   {
   return;
   }


for (ip = VMOUNTLIST; ip != NULL; ip=ip->next)
   {
   if (IsExcluded(ip->classes))
      {
      continue;
      }
   
   if ((dirh = opendir(ip->name)) == NULL)
      {
      sprintf(OUTPUT,"INFO: Host %s seems to have no (additional) local disks except the OS\n",VDEFAULTBINSERVER.name);
      CfLog(cfverbose,OUTPUT,"");
      sprintf(OUTPUT,"      mounted under %s\n\n",ip->name);
      CfLog(cfverbose,OUTPUT,"");
      return;
      }

   for (dirp = readdir(dirh); dirp != NULL; dirp = readdir(dirh))
      {
      if (!SensibleFile(dirp->d_name,ip->name,NULL))
         {
         continue;
         }

      strcpy(VBUFF,ip->name);
      AddSlash(VBUFF);
      strcat(VBUFF,dirp->d_name);

      if (IsHomeDir(VBUFF))
         {
         sprintf(OUTPUT,"Host defines a home directory %s\n",VBUFF);
	 CfLog(cfverbose,OUTPUT,"");
         }
      else
         {
         sprintf(OUTPUT,"Host defines a potential mount point %s\n",VBUFF);
	 CfLog(cfverbose,OUTPUT,"");
         }

      sprintf(CURRENTPATH,"%s%s",ip->name,dirp->d_name);
      sprintf(CURRENTITEM,"%s:%s",VDEFAULTBINSERVER.name,CURRENTPATH);

      if (! IsItemIn(VMOUNTED,CURRENTITEM))
         {
         if ( MOUNTCHECK && ! RequiredFileSystemOkay(CURRENTPATH) && VERBOSE)
            {
            sprintf(OUTPUT,"Found a mountpoint %s but there was\n",CURRENTPATH);
	    CfLog(cfinform,OUTPUT,"");
            CfLog(cfinform,"nothing mounted on it.\n\n","");
            }
         }
      }
   closedir(dirh);
   }
}

/*******************************************************************/

void GetMountInfo ()  /* This is, in fact, the most portable way to read the mount info! */
                      /* Depressing, isn't it? */
{ FILE *pp;
  struct Item *mp;
  char buf1[bufsize],buf2[bufsize],buf3[bufsize];
  char host[maxvarsize], mounton[bufsize];
  int i;

if (!GetLock(ASUniqueName("mountinfo"),"",0,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
   {
   return;
   }

Banner("Building list of currently mounted filesystems");

  /* sscanf(VMOUNTCOMM[VSYSTEMHARDCLASS],"%s",buf1); */
  /* Old BSD scanf crashes here! Why!? workaround: */

for (i=0; VMOUNTCOMM[VSYSTEMHARDCLASS][i] != ' '; i++)
   {
   buf1[i] =  VMOUNTCOMM[VSYSTEMHARDCLASS][i];
   }

buf1[i] = '\0';

signal(SIGALRM,(void *)TimeOut);
alarm(RPCTIMEOUT);

if ((pp = cfpopen(buf1,"r")) == NULL)
   {
   sprintf(OUTPUT,"%s: Can't open %s\n",VPREFIX,buf1);
   CfLog(cferror,OUTPUT,"popen");
   return;
   }

do
   {
   VBUFF[0] = buf1[0] = buf2[0] = buf3[0] = '\0';

   if (ferror(pp))  /* abortable */
      {
      GOTMOUNTINFO = false;
      CfLog(cferror,"Error getting mount info\n","ferror");
      break;
      }
   
   ReadLine(VBUFF,bufsize,pp);

   if (ferror(pp))  /* abortable */
      {
      GOTMOUNTINFO = false;
      CfLog(cferror,"Error getting mount info\n","ferror");
      break;
      }
   
   sscanf(VBUFF,"%s%s%s",buf1,buf2,buf3);

   if (VBUFF[0] == '\n')
      {
      break;
      }

   if (strstr(VBUFF,"not responding"))
      {
      printf("%s: %s\n",VPREFIX,VBUFF);
      }

   if (strstr(VBUFF,"be root"))
      {
      CfLog(cferror,"Mount access is denied. You must be root.\n","");
      CfLog(cferror,"Use the -n option to run safely.","");
      }

   if (strstr(VBUFF,"retrying") || strstr(VBUFF,"denied") || strstr(VBUFF,"backgrounding"))
      {
      continue;
      }

   if (strstr(VBUFF,"exceeded") || strstr(VBUFF,"busy"))
      {
      continue;
      }

   if (strstr(VBUFF,"RPC"))
      {
      if (! SILENT)
         {
         CfLog(cfinform,"There was an RPC timeout. Aborting mount operations.\n","");
         CfLog(cfinform,"Session failed while trying to talk to remote host\n","");
         sprintf(OUTPUT,"%s\n",VBUFF);
	 CfLog(cfinform,OUTPUT,"");
         }

      GOTMOUNTINFO = false;
      ReleaseCurrentLock();
      cfpclose(pp);
      return;
      }

   switch (VSYSTEMHARDCLASS)
      {
      case sun4:
      case sun3:
      case ultrx: 
      case irix:
      case irix4:
      case irix64:
      case linuxx:
      case GnU:
      case unix_sv:
      case freebsd:
      case netbsd:
      case openbsd:
      case bsd_i:
      case nextstep:
      case bsd4_3:
      case newsos:
      case aos:
      case osf:
      case crayos:
                    if (buf1[0] == '/')
                       {
                       strcpy(host,VDEFAULTBINSERVER.name);
                       strcpy(mounton,buf3);
                       }
                    else
                       {
                       sscanf(buf1,"%[^:]",host);
                       strcpy(mounton,buf3);
                       }

                    break;
      case solaris:
      case solarisx86:
      case hp10:
      case hp:      
                    if (buf3[0] == '/')
                       {
                       strcpy(host,VDEFAULTBINSERVER.name);
                       strcpy(mounton,buf1);
                       }
                    else
                       {
                       sscanf(buf3,"%[^:]",host);
                       strcpy(mounton,buf1);
                       }

                    break;
      case aix:
                   /* skip header */

                    if (buf1[0] == '/')
                       {
                       strcpy(host,VDEFAULTBINSERVER.name);
                       strcpy(mounton,buf2);
                       }
                    else
                       {
                       strcpy(host,buf1);
                       strcpy(mounton,buf3);
                       }
                    break;

      case cfnt:    strcpy(mounton,buf2);
	            strcpy(host,buf1);
                    break;
      case unused1:
      case unused2:
      case unused3:
                    break;

      case cfsco: CfLog(cferror,"Don't understand SCO mount format, no data","");

      default:
                    printf("cfengine software error: case %d = %s\n",VSYSTEMHARDCLASS,CLASSTEXT[VSYSTEMHARDCLASS]);
                    FatalError("System error in GetMountInfo - no such class!");
      }

   InstallMountedItem(host,mounton);
   }

while (!feof(pp));

alarm(0);
signal(SIGALRM,SIG_DFL);
ReleaseCurrentLock();
cfpclose(pp);
}

/*******************************************************************/

void MakePaths()

{ struct File *ptr;
  struct Item *ip1,*ip2;
  char pathbuff[bufsize],basename[bufsize];
  
Banner("Checking directories:");

for (ptr = VMAKEPATH; ptr != NULL; ptr=ptr->next)
   {
   if (IsExcluded(ptr->classes))
      {
      continue;
      }

   ResetOutputRoute(ptr->log,ptr->inform);
      
   if (strncmp(ptr->path,"home/",5) == 0) /* home/subdir */
      {
      if (*(ptr->path+4) != '/')
	 {
	 sprintf(OUTPUT,"Illegal use of home in directories: %s\n",ptr->path);
	 CfLog(cferror,OUTPUT,"");
	 continue;
	 }
      
      for (ip1 = VHOMEPATLIST; ip1 != NULL; ip1=ip1->next)
	 {
	 for (ip2 = VMOUNTLIST; ip2 != NULL; ip2=ip2->next)
	    {
	    if (IsExcluded(ip2->classes))
	       {
	       continue;
	       }
	    
	    pathbuff[0]='\0';
	    basename[0]='\0';	    
	    
	    strcpy(pathbuff,ip2->name);
	    AddSlash(pathbuff);
	    strcat(pathbuff,ip1->name);
	    AddSlash(pathbuff);
	    strcat(pathbuff,"*/");   
	    strcat(pathbuff,ptr->path+5);
	    
	    ExpandWildCardsAndDo(pathbuff,basename,DirectoriesWrapper,ptr);	 
	    }
	 }
      }
   else
      {
      Verbose("MakePath(%s)\n",ptr->path);
      pathbuff[0]='\0';
      basename[0]='\0';	        

      ExpandWildCardsAndDo(ptr->path,basename,DirectoriesWrapper,ptr);
      }
   ResetOutputRoute('d','d');
   }
}

/*******************************************************************/

void MakeChildLinks()     /* <binserver> should expand to a best fit filesys */

{ struct Link *lp;
  char *sp, *bp;
  struct Item *ip;
  int matched,varstring;
  char to[bufsize],from[bufsize];
  struct stat statbuf;
  short saveenforce;
  short savesilent;

if (NOLINKS)
   {
   return;
   }

ACTION = links; 

Banner("Checking multiple childlinks:");

for (lp = VCHLINK; lp != NULL; lp = lp->next)
   {
   if (IsExcluded(lp->classes))
      {
      continue;
      }

   ExpandVarstring(lp->from,from,NULL); 
   ExpandVarstring(lp->to,to,NULL); 

   saveenforce = ENFORCELINKS;
   ENFORCELINKS = ENFORCELINKS || lp->force;

   savesilent = SILENT;
   SILENT = SILENT || lp->silent;

   ResetOutputRoute(lp->log,lp->inform);
   
   matched = varstring = false;

   for(ip = VBINSERVERS; ip != NULL && (!matched); ip = ip->next)
      {
      CURRENTPATH[0] = CURRENTITEM[0] = '\0';

      if (strcmp(to,"linkchildren") == 0)           /* linkchildren */
         {
         if (stat(from,&statbuf) == -1)
            {
            sprintf(OUTPUT,"Makechildlinks() can't stat %s\n",from);
	    CfLog(cferror,OUTPUT,"stat");
	    ResetOutputRoute('d','d');
            continue;
            }
         LinkChildren(from,lp->type,&statbuf,0,0,lp->inclusions,lp->exclusions,lp->copy,lp->nofile,lp);
         break;
         }

      varstring = ExpandVarbinserv(to,CURRENTPATH,ip->name);

      if (lp->recurse != 0)
	 {
	 matched = RecursiveLink(lp,from,CURRENTPATH,lp->recurse);
         }
      else if (LinkChildFiles(from,CURRENTPATH,lp->type,lp->inclusions,lp->exclusions,lp->copy,lp->nofile,lp))
         {
         matched = true;
         }
      else if (! varstring)
         {
         sprintf(OUTPUT,"Error while trying to childlink %s -> %s\n",from,CURRENTPATH);
	 CfLog(cferror,OUTPUT,"");
         sprintf(OUTPUT,"The directory %s does not exist. Can't link.\n",CURRENTPATH);
	 CfLog(cferror,OUTPUT,"");	 
         }

      if (! varstring)                       /* don't iterate over binservers if not var */
         {
         break;
         }
      }

   ENFORCELINKS = saveenforce;
   SILENT = savesilent;
   ResetOutputRoute('d','d');

   if (matched == false && ip == NULL)
      {
      sprintf(OUTPUT,"ChildLink didn't find any server to match %s -> %s\n",from,to);
      CfLog(cferror,OUTPUT,"");
      }
   }
}

/*******************************************************************/

void MakeLinks()     /* <binserver> should expand to a best fit filesys */

{ struct Link *lp;
  char *sp, *bp,from[bufsize],to[bufsize];
  struct Item *ip;
  int matched,varstring;
  struct stat statbuf;
  short saveenforce;
  short savesilent;
  int (*linkfiles) ARGLIST((char *from, char *to, struct Item *inclusions, struct Item *exclusions, struct Item *copy, short int nofile, struct Link *ptr));

if (NOLINKS)
   {
   return;
   }

ACTION = links; 
Banner("Checking links:");

for (lp = VLINK; lp != NULL; lp = lp->next)
   {
   if (IsExcluded(lp->classes))
      {
      continue;
      }

   ExpandVarstring(lp->from,from,NULL);
   ExpandVarstring(lp->to,to,NULL); 

   ResetOutputRoute(lp->log,lp->inform);
   
   switch (lp->type)
      {
      case 's':
                linkfiles = LinkFiles;
                break;
      case 'r':
	        linkfiles = RelativeLink;
		break;
      case 'a':
	        linkfiles = AbsoluteLink;
                break;
      case 'h':
                linkfiles = HardLinkFiles;
                break;
      default:
                printf("%s: internal error, link type was [%c]\n",VPREFIX,lp->type);
                continue;
      }

   saveenforce = ENFORCELINKS;
   ENFORCELINKS = ENFORCELINKS || lp->force;

   savesilent = SILENT;
   SILENT = SILENT || lp->silent;

   matched = varstring = false;

   for( ip = VBINSERVERS; ip != NULL && (!matched); ip = ip->next)
      {
      CURRENTPATH[0] = CURRENTITEM[0] = '\0';

      varstring = ExpandVarbinserv(to,CURRENTPATH,ip->name);

      if ((*linkfiles)(from,CURRENTPATH,lp->inclusions,lp->exclusions,lp->copy,lp->nofile,lp))
         {
         matched = true;
         }
      else if (! varstring)
         {
         sprintf(OUTPUT,"Error while trying to link %s -> %s\n",from,CURRENTPATH);
	 CfLog(cfinform,OUTPUT,"");
         }

      if (! varstring)                       /* don't iterate over binservers if not var */
         {
         break;
         }
      }

   ENFORCELINKS = saveenforce;
   SILENT = savesilent;

   ResetOutputRoute('d','d');
   
   if (matched == false && ip == NULL)
      {
      sprintf(OUTPUT,"Links didn't find any file to match %s -> %s\n",from,to);
      CfLog(cferror,OUTPUT,"");
      }
   }
}

/*******************************************************************/

void MailCheck()

{ char mailserver[bufsize];
  char mailhost[maxvarsize];
  char rmailpath[maxvarsize];
  char lmailpath[maxvarsize];


if (VMAILSERVER[0] == '\0')
   {
   FatalError("Program does not define a mailserver for this host");
   }

Banner("Checking mail spool directory");

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can alter the mail configuration.\n","");
   return;
   }

sscanf (VMAILSERVER,"%[^:]:%s",mailhost,rmailpath);

if (VMAILSERVER[0] == '\0')
   {
   CfLog(cferror,"\n%s: Host has no defined mailserver!\n","");
   return;
   }

if (strcmp(VDEFAULTBINSERVER.name,mailhost) == 0) /* Is this the mailserver ?*/
   {
   MailCheckMore(rmailpath);
   return;
   }

sprintf(lmailpath,"%s:%s",mailhost,VMAILDIR[VSYSTEMHARDCLASS]);


if (IsItemIn(VMOUNTED,lmailpath))                             /* Remote file system mounted on */
   {                                                          /* local mail dir - correct      */
   Verbose("%s: Mail spool area looks ok\n",VPREFIX);
   return;
   }

strcpy(mailserver,VMAILDIR[VSYSTEMHARDCLASS]);
AddSlash(mailserver);
strcat(mailserver,".");

MakeDirectoriesFor(mailserver);                                  /* Check directory is in place */

if (IsItemIn(VMOUNTED,VMAILSERVER))
   {
   if (!SILENT)
      {
      Verbose("%s: Warning - the mail directory seems to be mounted as on\n",VPREFIX);
      Verbose("%s: the remote mailserver and not on the correct local directory\n",VPREFIX);
      Verbose("%s: Should strictly mount on %s\n",VPREFIX,VMAILDIR[VSYSTEMHARDCLASS]);
      }
   return;
   }

if (MatchStringInFstab("mail"))
   {
   if (!SILENT)
      {
      Verbose("%s: Warning - the mail directory seems to be mounted\n",VPREFIX);
      Verbose("%s: in a funny way. I can find the string <mail> in %s\n",VPREFIX,VFSTAB[VSYSTEMHARDCLASS]);
      Verbose("%s: but nothing is mounted on %s\n\n",VPREFIX,VMAILDIR[VSYSTEMHARDCLASS]);
      }
   return;
   }

printf("\n%s: Trying to mount %s\n",VPREFIX,VMAILSERVER);

if (! DONTDO)
   {
   AddToFstab(mailhost,rmailpath,VMAILDIR[VSYSTEMHARDCLASS],"rw",false);
   }
else
   {
   printf("%s: Need to mount %s:%s on %s\n",VPREFIX,mailhost,rmailpath,mailserver);
   }
}

/*******************************************************************/

void MailCheckMore(rmailpath)

char *rmailpath;

{ struct stat statbuf;

Verbose("%s: Maildir is %s on %s\n",VPREFIX,rmailpath,VMAILSERVER); 

if (strncmp(VMAILSERVER,VFQNAME,strlen(VMAILSERVER)) != 0)
   { DIR *dirh;
     struct dirent *dirp;
     struct stat statbuf;

   Verbose("%s: Looking closely at the mail directory...\n",VPREFIX);

   if ((dirh = opendir(rmailpath)) == NULL)
      {
      sprintf(OUTPUT,"Can't open directory %s which checking mail directory",rmailpath);
      CfLog(cferror,OUTPUT,"opendir");
      return;
      }

   for (dirp = readdir(dirh); dirp != NULL; dirp = readdir(dirh))
      {
      if (!SensibleFile(dirp->d_name,rmailpath,NULL))
         {
         continue;
         }

      strcpy(VBUFF,rmailpath);
      AddSlash(VBUFF);
      strcat(VBUFF,dirp->d_name);

      if (stat(VBUFF,&statbuf) != -1)
	 {
	 if (getpwuid(statbuf.st_uid) == NULL)
	    {
	    if (TrueVar("WarnNonOwnerMail"))
	       {
	       sprintf(OUTPUT,"File %s in mail dir %s is not owned by any user",dirp->d_name,rmailpath);
	       CfLog(cferror,OUTPUT,"");
	       }
	    
	    if (TrueVar("DeleteNonOwnerMail"))
	       {
	       if (DONTDO)
		  {
		  printf("%s: Delete file %s\n",VPREFIX,VBUFF);
		  }
	       else
		  {
		  sprintf(OUTPUT,"Deleting file %s in mail dir %s not owned by any user",dirp->d_name,rmailpath);
		  CfLog(cferror,OUTPUT,"");

		  if (unlink(VBUFF) == -1)
		     {
		     CfLog(cferror,"","unlink");
		     }
		  }
	       }
	    }
	 }

      if (strstr(dirp->d_name,"lock") || strstr(dirp->d_name,".tmp"))
	 {
	 Verbose("Ignoring non mailbox file %s\n",dirp->d_name);
	 continue;
	 }

      if (getpwnam(dirp->d_name) == NULL)
	 {
	 if (TrueVar("WarnNonUserMail"))
	    {
	    sprintf(OUTPUT,"File %s in mail dir %s is not the name of a user",dirp->d_name,rmailpath);
	    CfLog(cferror,OUTPUT,"");
	    }
	 
	 if (TrueVar("DeleteNonUserMail"))
	    {
	    if (DONTDO)
	       {
	       printf("%s: Delete file %s\n",VPREFIX,VBUFF);
	       }
	    else
	       {
	       sprintf(OUTPUT,"Deleting file %s in mail dir %s (not a username)",dirp->d_name,rmailpath);
	       CfLog(cferror,OUTPUT,"");
	       
	       if (unlink(VBUFF) == -1)
		  {
		  CfLog(cferror,"","unlink");
		  }	       
	       }
	    }	
	 }
      }
   closedir(dirh);
   Verbose("%s: Done with mail directory\n",VPREFIX);
   } 
}

/*******************************************************************/

void MountFileSystems()

{ FILE *pp;
  int fd;
  struct stat statbuf;

if (! GOTMOUNTINFO || DONTDO)
   {
   return;
   }

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can mount filesystems.\n","");
   return;
   }

Banner("Mounting filesystems");

signal(SIGALRM,(void *)TimeOut);
alarm(RPCTIMEOUT);

if (VSYSTEMHARDCLASS == cfnt)
   {
   /* This is a shell script. Make sure it hasn't been compromised. */
   if (stat("/etc/fstab",&statbuf) == -1)
      {
      if ((fd = creat("/etc/fstab",0755)) > 0)
	 {
	 write(fd,"#!/bin/sh\n\n",10);
	 close(fd);
	 }
      else
	 {
	 if (statbuf.st_mode & (S_IWOTH | S_IWGRP))
	    {
	    CfLog(cferror,"File /etc/fstab was insecure. Cannot mount filesystems.\n","");
	    GOTMOUNTINFO = false;
	    return;
	    }
	 }
      }
   }
 
if ((pp = cfpopen(VMOUNTCOMM[VSYSTEMHARDCLASS],"r")) == NULL)
   {
   sprintf(OUTPUT,"Failed to open pipe from %s\n",VMOUNTCOMM[VSYSTEMHARDCLASS]);
   CfLog(cferror,OUTPUT,"popen");
   return;
   }

while (!feof(pp))
   {
   if (ferror(pp))  /* abortable */
      {
      CfLog(cferror,"Error mounting filesystems\n","ferror");
      break;
      }
   
   ReadLine(VBUFF,bufsize,pp);

   if (ferror(pp))  /* abortable */
      {
      CfLog(cferror,"Error mounting filesystems\n","ferror");
      break;
      }

   if (strstr(VBUFF,"already mounted") || strstr(VBUFF,"exceeded") || strstr(VBUFF,"determined"))
      {
      continue;
      }

   if (strstr(VBUFF,"not supported"))
      {
      continue;
      }

   if (strstr(VBUFF,"denied") || strstr(VBUFF,"RPC"))
      {
      CfLog(cferror,"There was a mount error, trying to mount one of the filesystems on this host.\n","");
      sprintf(OUTPUT,"%s\n",VBUFF);
      CfLog(cferror,OUTPUT,"");
      GOTMOUNTINFO = false;
      break;
      }

   if (strstr(VBUFF,"trying") && !strstr(VBUFF,"NFS version 2"))
      {
      CfLog(cferror,"Aborted because MountFileSystems() went into a retry loop.\n","");
      GOTMOUNTINFO = false;
      break;
      }
   }

alarm(0);
signal(SIGALRM,SIG_DFL);
cfpclose(pp);
}

/*******************************************************************/

void CheckRequired()

{ struct Disk *rp;
  struct Item *ip;
  int matched,varstring,missing = 0;

Banner("Checking required filesystems");

 ACTION=required;
 
for (rp = VREQUIRED; rp != NULL; rp = rp->next)
   {
   if (IsExcluded(rp->classes))
      {
      continue;
      }

   ResetOutputRoute(rp->log,rp->inform);
   matched = varstring = false;

   for(ip = VBINSERVERS; ip != NULL && (!matched); ip = ip->next)
      {
      CURRENTPATH[0] = CURRENTITEM[0] = '\0';

      ExpandVarstring(rp->name,CURRENTITEM,NULL);
      varstring = ExpandVarbinserv(CURRENTITEM,CURRENTPATH,ip->name);

      if (RequiredFileSystemOkay(CURRENTPATH))  /* simple or reduced item */
         {
         Verbose("Filesystem %s looks sensible\n",CURRENTPATH);
         matched = true;
	 
	 if (rp->freespace == -1)
	    {
	    AddMultipleClasses(rp->elsedef);
	    }
         }
      else if (! varstring)
         {
         sprintf(OUTPUT,"The file %s does not exist or is suspicious.\n\n",CURRENTPATH);
	 CfLog(cferror,OUTPUT,"");
	 
         /* Define the class if there was no freespace option. */
         if (rp->freespace == -1)
	    {
	    AddMultipleClasses(rp->define);
	    }
         }

      if (! varstring)                       /* don't iterate over binservers if not var */
         {
         break;
         }
      }

   if ((rp->freespace != -1))
     {
     if (!CheckFreeSpace(CURRENTPATH,rp->freespace))
	{
	Verbose("Free space below %d, defining %s\n",rp->freespace, rp->define);
	AddMultipleClasses(rp->define);
	}
     else
	{
	Verbose("Free space above %d, defining %s\n",rp->freespace, rp->elsedef);
	AddMultipleClasses(rp->elsedef);
	}
     }

   if (matched == false && ip == NULL)
      {
      printf(" didn't find any file to match the required filesystem %s\n",rp->name);
      missing++;
      }
   }

if (missing)

   { time_t tloc;;

   if ((tloc = time((time_t *)NULL)) == -1)
      {
      printf("Couldn't read system clock\n");
      }
   sprintf(OUTPUT,"MESSAGE at %s\n\n",ctime(&tloc));
   CfLog(cferror,OUTPUT,"");
   sprintf(OUTPUT,"There are %d required file(system)s missing on host <%s>\n",missing,VDEFAULTBINSERVER.name);
   CfLog(cferror,OUTPUT,"");   
   CfLog(cferror,"even after all mounts have been attempted.\n","");
   CfLog(cferror,"This may be caused by a failure to mount a network filesystem (check exports)\n","");
   sprintf(OUTPUT,"or because no valid server was specified in the program %s\n\n",VINPUTFILE);
   CfLog(cferror,OUTPUT,"");

   ResetOutputRoute('d','d');
   }
}

/*******************************************************************/

void TidyFiles()

   /* Here we start by scanning for any absolute path wildcards */
   /* After that's finished, we go snooping around the homedirs */

{ char basename[bufsize],pathbuff[bufsize];
  struct TidyPattern *tlp;
  struct Tidy *tp;
  struct Item *ip1,*ip2;
  struct stat statbuf;
  int homesearch = 0;

Banner("Tidying by directory");

for (tp = VTIDY; tp != NULL; tp=tp->next)
   {
   if (strncmp(tp->path,"home",4)==0)
      {
      for (tlp = tp->tidylist; tlp != NULL; tlp=tlp->next)
	 {
	 if (!IsExcluded(tlp->classes))
	    {
	    homesearch = 1;
	    break;
	    }
	 }
      continue;
      }

   for (tlp = tp->tidylist; tlp != NULL; tlp=tlp->next)
      {
      Verbose("Directory %s\n",tp->path);
      strcpy(VBUFF,tp->path);
      AddSlash(VBUFF);
      strcat(VBUFF,tlp->pattern);

      if (stat(VBUFF,&statbuf) != -1)   /* not lstat - could confuse user */
         {
         if (S_ISDIR(statbuf.st_mode))
            {
	    if (! tlp->rmdirs)          /* Are we allowed to rm empty dirs? */
	       {
               Verbose("%s: will not delete directories (matching %s)!\n",VPREFIX,tlp->pattern);
	       Verbose("%s: Applies to %s\n",VPREFIX,VBUFF);
               DeleteTidyList(tp->tidylist);
	       tp->tidylist = NULL;
               continue;
	       }
            }
         }
      }

   basename[0] = '\0';

   ExpandWildCardsAndDo(tp->path,basename,TidyWrapper,tp);
   
   DeleteTidyList(tp->tidylist);
   tp->tidylist = NULL;
   }


Debug2("End PATHTIDY:\n");

if (!homesearch)                           /* If there are "home" wildcards */
   {                                 /* Don't go rummaging around the disks */
   Verbose("No home patterns to search\n");
   return;
   }

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can delete others' files.\n","");
   return;
   }


CfLog(cfinform,"Tidying home directories","");

if (!MountPathDefined())
   {
   return;
   }

for (ip1 = VHOMEPATLIST; ip1 != NULL; ip1=ip1->next)
   {
   for (ip2 = VMOUNTLIST; ip2 != NULL; ip2=ip2->next)
      {
      if (IsExcluded(ip2->classes))
	 {
	 continue;
	 }
      pathbuff[0]='\0';
      basename[0]='\0';
      strcpy(pathbuff,ip2->name);
      AddSlash(pathbuff);
      strcat(pathbuff,ip1->name);

      ExpandWildCardsAndDo(pathbuff,basename,RecHomeTidyWrapper,NULL);
      }
   }

CfLog(cfinform,"Done with home directories","");
}

/*******************************************************************/

void Scripts()

{ struct ShellComm *ptr;
  char line[bufsize];
  char comm[20], *sp;
  char execstr[bufsize];
  int print;
  mode_t maskval;
  FILE *pp;
  int preview = false;

Banner("Running shell commands");

for (ptr = VSCRIPT; ptr != NULL; ptr=ptr->next)
   {
   preview = (ptr->preview == 'y');
   
   if (IsExcluded(ptr->classes))
      {
      continue;
      }

   ResetOutputRoute(ptr->log,ptr->inform);
   
   if (!GetLock(ASUniqueName("shellcommand"),CanonifyName(ptr->name),VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
      {
      continue;
      }

   bzero(execstr,bufsize);
   ExpandVarstring(ptr->name,execstr,NULL);
   
   sprintf(OUTPUT,"Executing script %s...(timeout=%d,uid=%d,gid=%d)\n",execstr,ptr->timeout,ptr->uid,ptr->gid);
   CfLog((preview ? cfverbose : cfinform),OUTPUT,"");

   
   if (DONTDO && !preview)
      {
      printf("%s: execute script %s\n",VPREFIX,execstr);
      }
   else
      {
      for (sp = execstr; *sp != ' ' && *sp != '\0'; sp++)
	 {
	 }

      if (sp - 10 >= execstr)
	 {
	 sp -= 10;   /* copy 15 most relevant characters of command */
	 }
      else
	 {
	 sp = execstr;
	 }

      bzero(comm,20);
      strncpy(comm,sp,15);

      if (ptr->timeout != 0)
	 {
         signal(SIGALRM,(void *)TimeOut);
         alarm(ptr->timeout);
         }

      Verbose ("(Setting umask to %o)\n",ptr->umask);
      maskval = umask(ptr->umask);

      if (ptr->umask == 0)
	 {
	 sprintf(OUTPUT,"Programming %s running with umask 0! Use umask= to set\n",execstr);
	 CfLog(cfsilent,OUTPUT,"");
	 }
      
      switch (ptr->useshell)
	 {
	 case 'y':  pp = cfpopen_shsetuid(execstr,"r",ptr->uid,ptr->gid,ptr->chdir,ptr->chroot);
	            break;
	 default:   pp = cfpopensetuid(execstr,"r",ptr->uid,ptr->gid,ptr->chdir,ptr->chroot);
	            break;	     
	 }

      if (pp == NULL)
	 {
	 sprintf(OUTPUT,"Couldn't open pipe to command %s\n",execstr);
	 CfLog(cferror,OUTPUT,"popen");
	 ResetOutputRoute('d','d');
	 ReleaseCurrentLock();
	 continue;
	 } 
      
      while (!feof(pp))
	 {
	 if (ferror(pp))  /* abortable */
	    {
	    sprintf(OUTPUT,"Shell command pipe %s\n",execstr);
	    CfLog(cferror,OUTPUT,"ferror");
	    break;
	    }
	 
	 ReadLine(line,bufsize,pp);

	 if (ferror(pp))  /* abortable */
	    {
	    sprintf(OUTPUT,"Shell command pipe %s\n",execstr);
	    CfLog(cferror,OUTPUT,"ferror");
	    break;
	    }

	 if (preview)
	    {
	    /*
	     * Preview script - try to parse line as log message. If line does
	     * not parse, then log as error.
	     */

	    int i;
	    int level = cferror;
	    char *message = line;

	    /*
	     * Table matching cfoutputlevel enums to log prefixes.
	     */

	    char *prefixes[] =
	       {
	       ":silent:",
	       ":inform:",
	       ":verbose:",
	       ":editverbose:",
	       ":error:",
	       ":logonly:",
	       };
	    int precount = sizeof(prefixes)/sizeof(char *);

	    if (line[0] == ':')
	       {
	       /*
	        * Line begins with colon - see if it matches a log prefix.
		*/

	       for (i=0; i<precount; i++)
		  {
		  int prelen = 0;
		  prelen = strlen(prefixes[i]);
		  if (strncmp(line, prefixes[i], prelen) == 0)
		     {
		     /*
		      * Found log prefix - set logging level, and remove the
		      * prefix from the log message.
		      */
		     level = i;
		     message += prelen;
		     break;
		     }
		  }
	       }
	    CfLog(level, message, "");
	    }
	 else 
	    {
	    /*
	     * Dumb script - echo non-empty lines to standard output.
	     */

	    print = false;
	 
	    for (sp = line; *sp != '\0'; sp++)
	       {
	       if (! isspace(*sp))
		  {
		  print = true;
		  break;
		  }
	       }
	    
	    if (print)
	       {
	       printf("%s:%s: %s\n",VPREFIX,comm,line);
	       }
	    }
	 }
      
      cfpclose_def(pp,ptr->defines,ptr->elsedef);
      }

   if (ptr->timeout != 0)
      {
      alarm(0);
      signal(SIGALRM,SIG_DFL);
      }

   umask(maskval);
   
   sprintf(OUTPUT,"Finished script %s\n",execstr);
   CfLog((preview ? cfverbose : cfinform),OUTPUT,"");

   ResetOutputRoute('d','d');
   ReleaseCurrentLock();
   }
}

/*******************************************************************/

void GetSetuidLog()

{ struct Item *filetop = NULL;
  struct Item *ip;
  FILE *fp;
  char *sp;

if (!IsPrivileged())                     /* Ignore this if not root */
   {
   return;
   }

if ((fp = fopen(VSETUIDLOG,"r")) == NULL)
   {
   }
else
   {
   while (!feof(fp))
      {
      ReadLine(VBUFF,bufsize,fp);

      if (strlen(VBUFF) == 0)
         {
         continue;
         }

      if ((ip = (struct Item *)malloc (sizeof(struct Item))) == NULL)
         {
         perror("malloc");
         FatalError("GetSetuidList() couldn't allocate memory #1");
         }

      if ((sp = malloc (strlen(VBUFF)+2)) == NULL)
         {
         perror("malloc");
         FatalError("GetSetuidList() couldn't allocate memory #2");
         }

      if (filetop == NULL)
         {
         VSETUIDLIST = filetop = ip;
         }
      else
         {
         filetop->next = ip;
         }

      Debug2("SETUID-LOG: %s\n",VBUFF);

      strcpy(sp,VBUFF);
      ip->name = sp;
      ip->next = NULL;
      filetop = ip;
      }

   fclose(fp);
   }

}

/*******************************************************************/

void CheckFiles()                         /* Check through file systems */

{ struct File *ptr;
  char buffer[bufsize];
  short savetravlinks = TRAVLINKS;
  short savekilloldlinks = KILLOLDLINKS;

Banner("Checking files");

if (TRAVLINKS && (VERBOSE || DEBUG || D2))
   {
   printf("(Default in switched to purge stale links...)\n");
   }

for (ptr = VFILE; ptr != NULL; ptr=ptr->next)
   {
   if (IsExcluded(ptr->classes))
      {
      continue;
      }

   TRAVLINKS = savetravlinks;

   if (ptr->travlinks == 'T')
      {
      TRAVLINKS = true;
      }
   else if (ptr->travlinks == 'F')
      {
      TRAVLINKS = false;
      }
   else if (ptr->travlinks == 'K')
      {
      KILLOLDLINKS = true;
      }

   ResetOutputRoute(ptr->log,ptr->inform);

   if (strncmp(ptr->path,"home",4) == 0)
      {
      CheckHome(ptr);
      continue;
      }

   Verbose("Checking file(s) in %s\n",ptr->path);

   buffer[0] = '\0';
   ExpandWildCardsAndDo(ptr->path,buffer,CheckFileWrapper,ptr);

   ResetOutputRoute('d','d');
   TRAVLINKS = savetravlinks;
   KILLOLDLINKS = savekilloldlinks;
   }
}

/*******************************************************************/

void SaveSetuidLog()

{ FILE *fp;
  struct Item *ip;


if (!IsPrivileged())                     /* Ignore this if not root */
   {
   return;
   }

if (! DONTDO)
   {
   if ((fp = fopen(VSETUIDLOG,"w")) == NULL)
      {
      sprintf(OUTPUT,"Can't open %s for writing\n",VSETUIDLOG);
      CfLog(cferror,OUTPUT,"fopen");
      return;
      }

   Verbose("Saving the setuid log in %s\n",VSETUIDLOG);

   for (ip = VSETUIDLIST; ip != NULL; ip=ip->next)
      {
      if (!isspace(*(ip->name)) && strlen(ip->name) != 0)
         {                         
         fprintf(fp,"%s\n",ip->name);
         Debug2("SAVE-SETUID-LOG: %s\n",ip->name);
         }
      }

   fclose(fp);
   chmod(VSETUIDLOG,0600);
   }
}

/*******************************************************************/

void DisableFiles()

{ struct Disable *dp;
  struct stat statbuf;
  char workname[bufsize];
  
Banner("Disable files");

for (dp = VDISABLELIST; dp != NULL; dp=dp->next)
   {
   if (IsExcluded(dp->classes))
      {
      continue;
      }

   ExpandVarstring(dp->name,workname,NULL);
   
   ResetOutputRoute(dp->log,dp->inform);
   
   if (lstat(workname,&statbuf) == -1)
      {
      Verbose("Filetype %s, %s is not there - ok\n",dp->type,workname);
      AddMultipleClasses(dp->elsedef);
      continue;
      }

   Verbose("Disable checking %s\n",workname);

   if (S_ISDIR(statbuf.st_mode))
      {
      sprintf(OUTPUT,"Warning %s is a directory.\n",workname);
      CfLog(cferror,OUTPUT,"");
      CfLog(cferror,"I refuse to rename/delete a directory!\n\n","");
      ResetOutputRoute('d','d');
      continue;
      }

   if (S_ISLNK(statbuf.st_mode))
      {
      if (strcmp(dp->type,"file") == 0)
         {
         Verbose("%s: %s is a link, not disabling\n",VPREFIX,workname);
	 ResetOutputRoute('d','d');
         continue;
         }

      bzero(VBUFF,bufsize);
      
      if (readlink(workname,VBUFF,bufsize-1) == -1)
         {
         sprintf(OUTPUT,"DisableFiles() can't read link %s\n",workname);
	 CfLog(cferror,OUTPUT,"readlink");
	 ResetOutputRoute('d','d');
         continue;
         }

      printf("%s: Deleting link %s -> %s\n",VPREFIX,workname,VBUFF);

      if (! DONTDO)
         {
         if (unlink(workname) == -1)
            {
            sprintf(OUTPUT,"Error while unlinking %s\n",workname);
            CfLog(cferror,OUTPUT,"unlink");
	    ResetOutputRoute('d','d');
            continue;
            }
	 
	 AddMultipleClasses(dp->defines);
         }
      }
   else
      {
      if (! S_ISREG(statbuf.st_mode))
         {
         Verbose("%s: %s is not a plain file - won't disable\n",VPREFIX,workname);
	 ResetOutputRoute('d','d');
         continue;
         }

      if (strcmp(dp->type,"link") == 0)
         {
         Verbose("%s: %s is a file, not disabling\n",VPREFIX,workname);
	 ResetOutputRoute('d','d');
         continue;
         }

      if (stat(workname,&statbuf) == -1)
         {
         CfLog(cferror,"Internal; error in Disable\n","");
	 ResetOutputRoute('d','d');
	 return;
         }

      if (dp->size != cfnosize)
	 {
         switch (dp->comp)
	    {
            case '<':  if (statbuf.st_size < dp->size)
	                  {
		          Verbose("cfengine %s is smaller than %d bytes\n",workname,dp->size);
		          break;
	                  }
	               Verbose("Size is okay\n");
		       ResetOutputRoute('d','d');
	               continue;
		    
   	    case '=':  if (statbuf.st_size == dp->size)
	                  {
		          Verbose("cfengine %s is equal to %d bytes\n",workname,dp->size);
		          break;
	                  }
	                Verbose("Size is okay\n");
			ResetOutputRoute('d','d');
			continue;
		       
	    default: if (statbuf.st_size > dp->size)
	                  {
		          Verbose("cfengine %s is larger than %d bytes\n",workname,dp->size);
		          break;
	                  }
	               Verbose("Size is okay\n");
		       ResetOutputRoute('d','d');
	               continue;
	    }
	 }

      if (dp->rotate == 0)
         {
         strcpy(CURRENTPATH,workname);
         strcat(CURRENTPATH,".cfdisabled");

         sprintf(OUTPUT,"Disabling file %s\n",workname);
         CfLog(cfinform,OUTPUT,"");
	 
         if (! DONTDO)
            {
	    chmod(workname, (mode_t)0600);

	    if (! IsItemIn(VREPOSLIST,CURRENTPATH))
	       {
               if (rename(workname,CURRENTPATH) == -1)
                  {
                  sprintf(OUTPUT,"Error occurred while renaming %s\n",workname);
                  CfLog(cferror,OUTPUT,"rename");
		  ResetOutputRoute('d','d');
                  continue;
	          }

	       if (Repository(CURRENTPATH,dp->repository))
	          {
	          unlink(CURRENTPATH);
	          }
	       
	       AddMultipleClasses(dp->defines);
	       }
            }
         }
      else if (dp->rotate == CF_TRUNCATE)
	 {
         sprintf(OUTPUT,"Truncating (emptying) %s\n",workname);
	 CfLog(cfinform,OUTPUT,"");
	 
	 if (! DONTDO)
	    {
            TruncateFile(workname);
	    AddMultipleClasses(dp->defines);
	    }
	 }
      else
	 {
	 sprintf(OUTPUT,"Rotating files %s by %d\n",workname,dp->rotate);
	 CfLog(cfinform,OUTPUT,"");

	 if (!DONTDO)
	    {
	    RotateFiles(workname,dp->rotate);
	    AddMultipleClasses(dp->defines);
	    }
	 }
      }
   ResetOutputRoute('d','d');
   }
}

/*******************************************************************/

void MountHomeBinServers()

{ struct Item *mp;
  char host[maxvarsize];
  char mountdir[bufsize];
  char maketo[bufsize];

if (! GOTMOUNTINFO)
   {
   CfLog(cfinform,"Incomplete mount info due to RPC failure.\n","");
   sprintf(OUTPUT,"%s will not be modified on this pass!\n\n",VFSTAB[VSYSTEMHARDCLASS]);
   CfLog(cfinform,OUTPUT,"");
   return;
   }

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can mount filesystems.\n","");
   return;
   }

Banner("Checking home and binservers");

for (mp = VMOUNTABLES; mp != NULL; mp=mp->next)
   {
   sscanf(mp->name,"%[^:]:%s",host,mountdir);

   Debug("Mount: checking %s\n",mp->name);

   strcpy(maketo,mountdir);

   if (maketo[strlen(maketo)-1] == '/')
      {
      strcat(maketo,".");
      }
   else
      {
      strcat(maketo,"/.");
      }

   if (strcmp(host,VDEFAULTBINSERVER.name) == 0) /* A host never mounts itself nfs */
      {
      Debug("Skipping host %s\n",host);
      continue;
      }

   if (IsHomeDir(mountdir))
      {
      if (!IsItemIn(VMOUNTED,mp->name) && IsItemIn(VHOMESERVERS,host))
         {
         MakeDirectoriesFor(maketo);
         AddToFstab(host,mountdir,mountdir,"rw",false);
         }
      else if (IsItemIn(VHOMESERVERS,host))
	 {
	 AddToFstab(host,mountdir,mountdir,"rw",true);
	 }
      }
   else
      {
      if (!IsItemIn(VMOUNTED,mp->name) && IsItemIn(VBINSERVERS,host))
         {
         MakeDirectoriesFor(maketo);
         AddToFstab(host,mountdir,mountdir,"rw",false);
         }
      else if (IsItemIn(VBINSERVERS,host))
         {
	 AddToFstab(host,mountdir,mountdir,"rw",true);
         }
      }
   }
}


/*********************************************************************/

void MountMisc()

{ struct MiscMount *mp;
  char host[maxvarsize];
  char mountdir[bufsize];
  char maketo[bufsize];
  char mtpt[bufsize];

if (! GOTMOUNTINFO)
   {
   CfLog(cfinform,"Incomplete mount info due to RPC failure.\n","");
   sprintf(OUTPUT,"%s will not be modified on this pass!\n\n",VFSTAB[VSYSTEMHARDCLASS]);
   CfLog(cfinform,OUTPUT,"");
   return;
   }
 
if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can mount filesystems.\n","");
   return;
   }

Banner("Checking miscellaneous mountables:");

for (mp = VMISCMOUNT; mp != NULL; mp=mp->next)
   {
   sscanf(mp->from,"%[^:]:%s",host,mountdir);

   strcpy(maketo,mp->onto);

   if (maketo[strlen(maketo)-1] == '/')
      {
      strcat(maketo,".");
      }
   else
      {
      strcat(maketo,"/.");
      }

   if (strcmp(host,VDEFAULTBINSERVER.name) == 0) /* A host never mounts itself nfs */
      {
      continue;
      }

   sprintf(mtpt,"%s:%s",host,mp->onto);
   
   if (!IsItemIn(VMOUNTED,mtpt))
      {
      MakeDirectoriesFor(maketo);
      AddToFstab(host,mountdir,mp->onto,mp->options,false);
      }
   else
      {
      AddToFstab(host,mountdir,mp->onto,mp->options,true);
      }
   }
}

/*********************************************************************/

void Unmount()

{ struct UnMount *ptr;
  char comm[bufsize];
  char fs[bufsize];
  struct Item *filelist, *item, *search;
  struct stat statbuf;
  FILE *pp;

Banner("Checking unmounts\n");

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can unmount filesystems.\n","");
   return;
   }

filelist = NULL;

if (! LoadItemList(&filelist,VFSTAB[VSYSTEMHARDCLASS]))
   {
   sprintf(OUTPUT,"Couldn't open %s!\n",VFSTAB[VSYSTEMHARDCLASS]);
   CfLog(cferror,OUTPUT,"");
   return;
   }

NUMBEROFEDITS = 0;

for (ptr=VUNMOUNT; ptr != NULL; ptr=ptr->next)
   {
   if (IsExcluded(ptr->classes))
      {
      continue;
      }

   fs[0] = '\0';

   sscanf(ptr->name,"%*[^:]:%s",fs);

   if (strlen(fs) == 0)
      {
      continue;
      }
   
   sprintf(OUTPUT,"Unmount filesystem %s on %s\n",fs,ptr->name);
   CfLog(cfverbose,OUTPUT,"");

   if (strcmp(fs,"/") == 0 || strcmp(fs,"/usr") == 0)
      {
      CfLog(cfinform,"Request to unmount / or /usr is refused!\n","");
      continue;
      }

   if (IsItemIn(VMOUNTED,ptr->name) && (! DONTDO))
      {
      sprintf(comm,"%s %s",VUNMOUNTCOMM[VSYSTEMHARDCLASS],fs);

      if ((pp = cfpopen(comm,"r")) == NULL)
         {
         sprintf(OUTPUT,"Failed to open pipe from %s\n",VUNMOUNTCOMM[VSYSTEMHARDCLASS]);
	 CfLog(cferror,OUTPUT,"");
         return;
         }

      ReadLine(VBUFF,bufsize,pp);

      if (strstr(VBUFF,"busy") || strstr(VBUFF,"Busy"))
         {
         sprintf(OUTPUT,"umount warned that the device under %s\n",ptr->name);
	 CfLog(cfinform,OUTPUT,"");
         CfLog(cfinform,"was busy. Cannot unmount that device.\n","");
	 /* don't delete the mount dir when unmount's failed */
	 ptr->deletedir = ptr->deletefstab = 'f';
	 }
      else
	 {
	 sprintf(OUTPUT,"Unmounting %s\n",ptr->name);
	 CfLog(cfinform,OUTPUT,"");
	 DeleteItemStarting(&VMOUNTED,ptr->name);  /* update mount record */
         }

      cfpclose(pp);
      }

   if (ptr->deletedir == 't')
      {
      if (stat(fs,&statbuf) != -1)
	 {
	 if ( ! S_ISDIR(statbuf.st_mode))
	    {
	    sprintf(OUTPUT,"Warning! %s was not a directory.\n",fs);
	    CfLog(cfinform,OUTPUT,"");
	    CfLog(cfinform,"(Unmount) will not delete this!\n","");
	    KillOldLink(fs);
	    }
	 else if (! DONTDO)
	    {
	    if (rmdir(fs) == -1)
	       {
	       sprintf(OUTPUT,"Unable to remove the directory %s\n",fs);
	       CfLog(cferror,OUTPUT,"rmdir");
	       } 
	    else
	       {
	       sprintf(OUTPUT,"Removing directory %s\n",ptr->name);
	       CfLog(cfinform,OUTPUT,"");
	       }
	    }
	 }
      }
      
   if (ptr->deletefstab == 't')
      {
      if (VSYSTEMHARDCLASS == aix)
	 {
	 strcpy (VBUFF,fs);
	 strcat (VBUFF,":");
	 
	 item = LocateNextItemContaining(filelist,VBUFF);
	 
	 if (item->next == NULL)
	    {
	    sprintf(OUTPUT,"Bad format in %s\n",VFSTAB[aix]);
	    CfLog(cferror,OUTPUT,"");
	    }
	 
	 DeleteItem(&filelist,item->next);
	 
	 while (strstr(item->next->name,"="))
	    {
	    DeleteItem(&filelist,item->next);    /* DeleteItem(NULL) is harmless */
	    }
	 }
      else
	 {
	 Debug("Trying to delete filesystem %s from list\n",ptr->name);
	 	 
	 if (VSYSTEMHARDCLASS == ultrx)   /* ensure name is not just a substring */
	    {
	    strcpy (VBUFF,ptr->name);
	    strcat (VBUFF,":");
	    DeleteItemContaining(&filelist,VBUFF);
	    }
	 else
	    {
         switch (VSYSTEMHARDCLASS)
           {
           case unix_sv:
           case solarisx86:
           case solaris:
             /* find fs in proper context ("<host>:<remotepath> <-> <fs> ") */
             sprintf(VBUFF,"[^:]+:[^ \t]+[ \t]+[^ \t]+[ \t]+%s[ \t]",fs);
             break;
           default:
             /* find fs in proper context ("<host>:<remotepath> <fs> ") */
             sprintf(VBUFF,"[^:]+:[^ \t]+[ \t]+%s[ \t]",fs);
             break;
           }
	    item = LocateItemContainingRegExp(filelist,VBUFF);
	    DeleteItem(&filelist,item);
	    }
	 }
      }
   }

if ((! DONTDO) && (NUMBEROFEDITS > 0))
   {
   SaveItemList(filelist,VFSTAB[VSYSTEMHARDCLASS],VREPOSITORY);
   }

DeleteItemList(filelist);
}

/*********************************************************************/

void EditFiles()

{ struct Edit *ptr;
  struct stat statbuf;
 
Banner("Editing files");

for (ptr=VEDITLIST; ptr!=NULL; ptr=ptr->next)
   {
   if (strncmp(ptr->fname,"home",4) == 0)
      {
      DoEditHomeFiles(ptr);
      }
   else
      {
      if (lstat(ptr->fname,&statbuf) != -1)
	 {
	 if (S_ISDIR(statbuf.st_mode))
	    {
	    DoRecursiveEditFiles(ptr->fname,ptr->recurse,ptr,&statbuf);
	    }
	 else
	    {
	    WrapDoEditFile(ptr,ptr->fname);
	    }
	 }
      else
	 {
	 DoEditFile(ptr,ptr->fname);	 
	 }
      }
   }

EDITVERBOSE = false;
}

/*******************************************************************/

void CheckResolv()

{ struct Item *filebase = NULL, *referencefile = NULL;
  struct Item *ip, *ip2;
  FILE *fp;
  char ch;
  int fd, existed = true;

Verbose("Checking config in %s\n",VRESOLVCONF[VSYSTEMHARDCLASS]);
  
if (VDOMAIN == NULL)
   {
   CfLog(cferror,"Domain name not specified. Can't configure resolver\n","");
   return;
   }

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can configure the resolver.\n","");
   return;
   }

if (! LoadItemList(&referencefile,VRESOLVCONF[VSYSTEMHARDCLASS]))
   {
   sprintf(OUTPUT,"Trying to create %s\n",VRESOLVCONF[VSYSTEMHARDCLASS]);
   CfLog(cfinform,OUTPUT,"");
   existed = false;
   
   if ((fd = creat(VRESOLVCONF[VSYSTEMHARDCLASS],0644)) == -1)
      {
      sprintf(OUTPUT,"Unable to create file %s\n",VRESOLVCONF[VSYSTEMHARDCLASS]);
      CfLog(cferror,OUTPUT,"creat");
      return;
      }
   else
      {
      close(fd);
      }
   }

if (existed)
   {
   LoadItemList(&filebase,VRESOLVCONF[VSYSTEMHARDCLASS]);
   }

for (ip = filebase; ip != NULL; ip=ip->next)
   {
   if (strlen(ip->name) == 0)
      {
      continue;
      }

   ch = *(ip->name+strlen(ip->name)-2);

   if (isspace(ch))
      {
      sprintf(OUTPUT,"Deleted line %s ended with a space in %s.\n",ip->name,VRESOLVCONF[VSYSTEMHARDCLASS]);
      CfLog(cfinform,OUTPUT,"");
      CfLog(cfinform,"The resolver doesn't understand this.\n","");
      DeleteItem(&filebase,ip);
      }
   else if (isspace(*(ip->name)))
      {
      sprintf(OUTPUT,"Deleted line %s started with a space in %s.\n",ip->name,VRESOLVCONF[VSYSTEMHARDCLASS]);
      CfLog(cfinform,OUTPUT,"");
      CfLog(cfinform,"The resolver doesn't understand this.\n","");
      DeleteItem(&filebase,ip);
      }
   }

DeleteItemStarting(&filebase,"domain");
EditItemsInResolvConf(VRESOLVE,&filebase);
sprintf(VBUFF,"domain %s",ToLowerStr(VDOMAIN));
PrependItem(&filebase,VBUFF,NULL);

if (DONTDO)
   {
   printf("Check %s for editing\n",VRESOLVCONF[VSYSTEMHARDCLASS]);
   }
else if (!ItemListsEqual(filebase,referencefile))
   {
   SaveItemList(filebase,VRESOLVCONF[VSYSTEMHARDCLASS],VREPOSITORY);
   chmod(VRESOLVCONF[VSYSTEMHARDCLASS],DEFAULTSYSTEMMODE);
   }
else
   {
   Verbose("cfengine: %s is okay\n",VRESOLVCONF[VSYSTEMHARDCLASS]);
   }

DeleteItemList(filebase);
DeleteItemList(referencefile);
}


/*******************************************************************/

void MakeImages()

{ struct Image *ip;
  struct Item *svp;
  struct stat statbuf;
  struct servent *serverent;
  int savesilent;
  char path[bufsize];
  char destination[bufsize];
  char server[bufsize];

Banner("Checking file images:");

if ((serverent = getservbyname(CFENGINE_SERVICE,"tcp")) == NULL)
   {
   CfLog(cfverbose,"Remember to register cfengine in /etc/services: cfengine 5308/tcp\n","getservbyname");
   PORTNUMBER = htons((unsigned short)5308);
   }
else
   {
   PORTNUMBER = (unsigned short)(serverent->s_port); /* already in network order */
   }
 
for (svp = VSERVERLIST; svp != NULL; svp=svp->next) /* order servers */
   {
   Debug("New server....\n");
   for (ip = VIMAGE; ip != NULL; ip=ip->next)
      {
      if (strcmp(svp->name,ip->server) != 0)  /* group together similar hosts so */
	 {                                    /* can can do multiple transactions */
	 continue;                            /* on one connection */
	 }
      
      if (IsExcluded(ip->classes))
	 {
	 continue;
	 }

      if (!OpenServerConnection(svp,ip))
	 {
	 sprintf(OUTPUT,"Unable to establish connection with %s\n",svp->name);
	 CfLog(cferror,OUTPUT,"");
	 continue;
	 }

      if (AUTHENTICATED)
	 {
	 Debug("Authentic connection verified\n");
	 }
   
      IMAGEBACKUP = true;
      
	  /* Convert any variables to their values */
	  if (IsVarString(ip->server))
		ExpandVarstring(ip->server,server,"");
	  else
		strncpy (server, ip->server, sizeof(server));
	  if (IsVarString(ip->path))
		ExpandVarstring(ip->path,path,"");
	  else
		strncpy (path, ip->path, sizeof(path));
	  if (IsVarString(ip->destination))
		ExpandVarstring(ip->destination,destination,"");
	  else
		strncpy (destination, ip->destination, sizeof(destination));		

      Verbose("Checking copy from %s:%s to %s\n",server,path,destination);
      
      savesilent = SILENT;
      
      if (strcmp(ip->action,"silent") == 0)
	 {
	 SILENT = true;
	 }
      
      ResetOutputRoute(ip->log,ip->inform);
      
      snprintf(VBUFF,bufsize,"%.50s.%.50s",path,destination); /* Unique ID for copy locking */

      if (!GetLock(ASUniqueName("copy"),CanonifyName(VBUFF),VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
	 {
	 SILENT = savesilent;
	 ResetOutputRoute('d','d');
	 continue;
	 }
      
      IMAGEBACKUP = ip->backup;
      
      if (cfstat(path,&statbuf,ip) == -1)
	 {
	 snprintf(OUTPUT,bufsize*2,"Can't stat %s in copy\n",path);
	 CfLog(cfinform,OUTPUT,"");
	 ReleaseCurrentLock();
	 SILENT = savesilent;
	 ResetOutputRoute('d','d');
	 continue;
	 }
      
      if (strncmp(destination,"home",4) == 0)
	 {
	 HOMECOPY = true;          /* Don't send home backups to repository */
	 CheckHomeImages(ip);
	 HOMECOPY = false;
	 }
      else
	 {
	 if (S_ISDIR(statbuf.st_mode))
	    {
	    if (ip->purge == 'y')
	       {
	       Verbose("%s: (Destination purging enabled)\n",VPREFIX);
	       }
	    RecursiveImage(ip,path,destination,ip->recurse);
	    }
	 else
	    {
	    if (! MakeDirectoriesFor(destination))
	       {
	       ReleaseCurrentLock();
	       SILENT = savesilent;
	       ResetOutputRoute('d','d');
	       continue;
	       }
	    
	    CheckImage(path,destination,ip);
	    }
	 }
      
      ReleaseCurrentLock();
      SILENT = savesilent;
      ResetOutputRoute('d','d');
      }

   CloseServerConnection();
   }
}

/*******************************************************************/

void ConfigureInterfaces()

{ struct Interface *ifp;

Banner("Checking network interfaces"); 

if (GetLock("netconfig",VIFDEV[VSYSTEMHARDCLASS],VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
   {
   if (strlen(VNETMASK) != 0)
      {
      IfConf(VIFDEV[VSYSTEMHARDCLASS],VNETMASK,VBROADCAST);
      }
   
   SetDefaultRoute();
   ReleaseCurrentLock();   
   }
    
for (ifp = VIFLIST; ifp != NULL; ifp=ifp->next)
   {
   if (!GetLock("netconfig",ifp->ifdev,VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
      {
      continue;
      }
   
   IfConf(ifp->ifdev,ifp->netmask,ifp->broadcast);
   SetDefaultRoute();
   ReleaseCurrentLock();
   }
}

/*******************************************************************/

void CheckTimeZone()

{ time_t tloc;
  struct stat statbuf;
  struct Item *ip;
  char tz[maxvarsize];
  
if (VTIMEZONE == NULL)
   {
   CfLog(cferror,"Program does not define a timezone","");
   return;
   }

for (ip = VTIMEZONE; ip != NULL; ip=ip->next)
   {
#ifdef NT
   
   tzset();
   strcpy(tz,timezone());
   
#else
#ifndef AOS
#ifndef SUN4

   tzset();
   strcpy(tz,tzname[0]);

#else

   if ((tloc = time((time_t *)NULL)) == -1)
      {
      printf("Couldn't read system clock\n\n");
      }
   strcpy(tz,localtime(&tloc)->tm_zone);
      
#endif /* SUN4 */
#endif /* AOS  */
#endif /* NT */

   if (TZCheck(tz,ip->name))
      {
      return;
      }
   }

sprintf(OUTPUT,"Time zone was %s which is not in the list of acceptable values");
CfLog(cferror,OUTPUT,""); 
}

/*******************************************************************/

void CheckProcesses()

{ struct Process *pp;
  struct Item *procdata = NULL;
  char *psopts = VPSOPTS[VSYSTEMHARDCLASS];

Banner("Checking Processes:");

if (!LoadProcessTable(&procdata,psopts))
   {
   CfLog(cferror,"Unable to read the process table\n","");
   return;
   }

for (pp = VPROCLIST; pp != NULL; pp=pp->next)
   {
   if (IsExcluded(pp->classes))
      {
      continue;
      }
   
   ResetOutputRoute(pp->log,pp->inform);
   
   if (strcmp(pp->expr,"SetOptionString") == 0)
      {
      psopts = pp->restart;
      DeleteItemList(procdata);
      procdata = NULL;
      if (!LoadProcessTable(&procdata,psopts))
	 {
	 CfLog(cferror,"Unable to read the process table\n","");
	 }
      }
   else
      {
      DoProcessCheck(pp,procdata);
      }
   
   ResetOutputRoute('d','d');
   }
}

/*******************************************************************/

int RequiredFileSystemOkay (name)

char *name;

{ struct stat statbuf, localstat;
  DIR *dirh;
  struct dirent *dirp;
  long sizeinbytes = 0, filecount = 0;
  char buff[bufsize];

Debug2("Checking required filesystem %s\n",name);

if (stat(name,&statbuf) == -1)
   {
   return(false);
   }

if (S_ISLNK(statbuf.st_mode))
   {
   KillOldLink(name);
   return(true);
   }

if (S_ISDIR(statbuf.st_mode))
   {
   if ((dirh = opendir(name)) == NULL)
      {
      sprintf(OUTPUT,"Can't open directory %s which checking required/disk\n",name);
      CfLog(cferror,OUTPUT,"opendir");
      return false;
      }

   for (dirp = readdir(dirh); dirp != NULL; dirp = readdir(dirh))
      {
      if (!SensibleFile(dirp->d_name,name,NULL))
         {
         continue;
         }

      filecount++;

      strcpy(buff,name);

      if (buff[strlen(buff)] != '/')
         {
         strcat(buff,"/");
         }

      strcat(buff,dirp->d_name);

      if (lstat(buff,&localstat) == -1)
         {
         if (S_ISLNK(localstat.st_mode))
            {
            KillOldLink(buff);
            continue;
            }

         sprintf(OUTPUT,"Can't stat %s in required/disk\n",buff);
	 CfLog(cferror,OUTPUT,"lstat");
         continue;
         }

      sizeinbytes += localstat.st_size;
      }

   closedir(dirh);

   if (sizeinbytes < 0)
      {
      Verbose("Internal error: count of byte size was less than zero!\n");
      return true;
      }

   if (sizeinbytes < SENSIBLEFSSIZE)
      {
      sprintf(OUTPUT,"File system %s is suspiciously small! (%d bytes)\n",name,sizeinbytes);
      CfLog(cferror,OUTPUT,"");
      return(false);
      }

   if (filecount < SENSIBLEFILECOUNT)
      {
      sprintf(OUTPUT,"Filesystem %s has only %d files/directories.\n",name,filecount);
      CfLog(cferror,OUTPUT,"");
      return(false);
      }
   }

return(true);
}


/*******************************************************************/

void InstallMountedItem(host,mountdir)

char *host, *mountdir;

{
strcpy (VBUFF,host);
strcat (VBUFF,":");
strcat (VBUFF,mountdir);

if (IsItemIn(VMOUNTED,VBUFF))
   {
   if (! SILENT || !WARNINGS)
      {
      sprintf(OUTPUT,"WARNING mount item %s\n",VBUFF);
      CfLog(cferror,OUTPUT,"");
      CfLog(cferror,"is mounted multiple times!\n","");
      }
   }

AppendItem(&VMOUNTED,VBUFF,NULL);
}

/*******************************************************************/


void AddToFstab(host,rmountpt,mountpt,mode,ismounted)

char *host, *mountpt, *rmountpt, *mode;
int ismounted;

{ char fstab[bufsize];
  char *opts;
  FILE *fp;

Debug("AddToFstab(%s)\n",mountpt);

opts = VMOUNTOPTS[VSYSTEMHARDCLASS];

switch (VSYSTEMHARDCLASS)
   {
   case osf:
   case bsd4_3:
   case irix:
   case irix4:
   case irix64:
   case sun3:
   case aos:
   case nextstep:
   case newsos:
   case sun4:    sprintf(fstab,"%s:%s \t %s %s\t%s,%s 0 0",host,rmountpt,mountpt,VNFSTYPE,mode,opts);
                 break;

   case crayos:
                 sprintf(fstab,"%s:%s \t %s %s\t%s,%s",host,rmountpt,mountpt,ToUpperStr(VNFSTYPE),mode,opts);
                 break;
   case ultrx:   sprintf(fstab,"%s@%s:%s:%s:0:0:%s:%s",rmountpt,host,mountpt,mode,VNFSTYPE,opts);
                 break;
   case hp10:
   case hp:      sprintf(fstab,"%s:%s %s \t %s \t %s,%s 0 0",host,rmountpt,mountpt,VNFSTYPE,mode,opts);
                 break;
   case aix:     sprintf(fstab,"%s:\n\tdev\t= %s\n\ttype\t= %s\n\tvfs\t= %s\n\tnodename\t= %s\n\tmount\t= true\n\toptions\t= %s,%s\n\taccount\t= false\n\n",mountpt,rmountpt,VNFSTYPE,VNFSTYPE,host,mode,opts);
                 break;
   case GnU:
   case linuxx:  sprintf(fstab,"%s:%s \t %s \t %s \t %s,%s",host,rmountpt,mountpt,VNFSTYPE,mode,opts);
                 break;

   case netbsd:
   case openbsd:
   case bsd_i:
   case freebsd: sprintf(fstab,"%s:%s \t %s \t %s \t %s,%s 0 0",host,rmountpt,mountpt,VNFSTYPE,mode,opts);
                 break;

   case unix_sv:
   case solarisx86:
   case solaris: sprintf(fstab,"%s:%s - %s %s - yes %s,%s",host,rmountpt,mountpt,VNFSTYPE,mode,opts);
                 break;

   case cfnt:    sprintf(fstab,"/bin/mount %s:%s %s",host,rmountpt,mountpt);
                 break;
   case cfsco:   CfLog(cferror,"Don't understand filesystem format on SCO, no data","");
                 break;
   case unused1:
   case unused2:
   case unused3:
   default:      FatalError("AddToFstab(): unknown hard class detected!\n");
                 break;
   }

if (MatchStringInFstab(mountpt))
   {
   /* if the fstab entry has changed, remove the old entry and update */
   if (!MatchStringInFstab(fstab))
      { struct UnMount *saved_VUNMOUNT = VUNMOUNT;
	char mountspec[MAXPATHLEN];
        struct Item *mntentry = NULL;
	struct UnMount cleaner;

      sprintf(OUTPUT,"Removing \"%s\" entry from %s to allow update:\n",
              mountpt,VFSTAB[VSYSTEMHARDCLASS]);
      CfLog(cfinform,OUTPUT,"");
      CfLog(cfinform,"---------------------------------------------------","");

      /* delete current fstab entry and unmount if necessary */
      sprintf(mountspec,".+:%s",mountpt);
      mntentry = LocateItemContainingRegExp(VMOUNTED,mountspec);
      if (mntentry)
	 {
	 sscanf(mntentry->name,"%[^:]:",mountspec);  /* extract current host */
	 strcat(mountspec,":");
	 strcat(mountspec,mountpt);
	 }
      else  /* mountpt isn't mounted, so Unmount can use dummy host name */
	 sprintf(mountspec,"host:%s",mountpt);

      /* delete current fstab entry and unmount if necessary (don't rmdir) */
      cleaner.name        = mountspec;
      cleaner.classes     = NULL;
      cleaner.deletedir   = 'f';
      cleaner.deletefstab = 't';
      cleaner.force       = 'f';
      cleaner.next        = NULL;

      VUNMOUNT = &cleaner;
      Unmount();
      VUNMOUNT = saved_VUNMOUNT;
      CfLog(cfinform,"---------------------------------------------------","");
      }
   else /* no need to update fstab - this mount entry is already there */
      {
      /* warn if entry's already in the fstab but hasn't been mounted */
      if (!ismounted && !SILENT && !strstr(mountpt,"cdrom"))
	 {
	 sprintf(OUTPUT,"Warning the file system %s seems to be in %s\n",mountpt,VFSTAB[VSYSTEMHARDCLASS]);
	 CfLog(cfinform,OUTPUT,"");
	 sprintf(OUTPUT,"already, but I was not able to mount it.\n");
	 CfLog(cfinform,OUTPUT,"");
	 sprintf(OUTPUT,"Check the exports file on host %s? Check for file with same name as dir?\n",host);
	 CfLog(cfinform,OUTPUT,"");
	 }
      
      return;
      }
   }

if (DONTDO)
   {
   printf("%s: add filesystem to %s\n",VPREFIX,VFSTAB[VSYSTEMHARDCLASS]);
   printf("%s: %s\n",VPREFIX,fstab);
   }
else
   {
   if ((fp = fopen(VFSTAB[VSYSTEMHARDCLASS],"a")) == NULL)
      {
      sprintf(OUTPUT,"Can't open %s for appending\n",VFSTAB[VSYSTEMHARDCLASS]);
      CfLog(cferror,OUTPUT,"fopen");
      ReleaseCurrentLock();
      return;
      }

   sprintf(OUTPUT,"Adding filesystem to %s\n",VFSTAB[VSYSTEMHARDCLASS]);
   CfLog(cfinform,OUTPUT,"");
   sprintf(OUTPUT,"%s\n",fstab);
   CfLog(cfinform,OUTPUT,"");
   fprintf(fp,"%s\n",fstab);
   fclose(fp);
   
   chmod(VFSTAB[VSYSTEMHARDCLASS],DEFAULTSYSTEMMODE);
   }
}


/*******************************************************************/

int CheckFreeSpace (file,kilobytes)

char *file;
int kilobytes;

{ struct stat statbuf;
  int free;

if (stat(file,&statbuf) == -1)
   {
   sprintf(OUTPUT,"Couldn't stat %s checking diskspace\n",file);
   CfLog(cferror,OUTPUT,"");
   return true;
   }

if (IsMountedFileSystem(&statbuf,file,1)) /* only do this on server */
   {
   return true;
   }

if (kilobytes < 0)  /* percentage */
   {
   free = GetDiskUsage(file,cfpercent);
   kilobytes = -1 * kilobytes;
   if (free < kilobytes)
      {
      sprintf(OUTPUT,"Free disk space is under %d%% for partition\n",kilobytes);
      CfLog(cfinform,OUTPUT,"");
      sprintf(OUTPUT,"containing %s (%d%% free)\n",file,free);
      CfLog(cfinform,OUTPUT,"");
      return false;
      }
   }
else
   {
   free = GetDiskUsage(file, cfabs);

   if (free < kilobytes)
      {
      sprintf(OUTPUT,"Disk space under %d kB for partition\n",kilobytes);
      CfLog(cfinform,OUTPUT,"");
      sprintf(OUTPUT,"containing %s (%d kB free)\n",file,free);
      CfLog(cfinform,OUTPUT,"");
      return false;
      }
   }

return true;
}

/*******************************************************************/

void CheckHome(ptr)                      /* iterate check over homedirs */

struct File *ptr;

{ struct Item *ip1, *ip2;
  char basename[bufsize],pathbuff[bufsize];

Debug("CheckHome(%s)\n",ptr->path);

if (!IsPrivileged())                            
   {
   CfLog(cferror,"Only root can check others' files.\n","");
   return;
   }

if (!MountPathDefined())
   {
   return;
   }

for (ip1 = VHOMEPATLIST; ip1 != NULL; ip1=ip1->next)
   {
   for (ip2 = VMOUNTLIST; ip2 != NULL; ip2=ip2->next)
      {
      if (IsExcluded(ip2->classes))
	 {
	 continue;
	 }
      pathbuff[0]='\0';
      basename[0]='\0';
      strcpy(pathbuff,ip2->name);
      AddSlash(pathbuff);
      strcat(pathbuff,ip1->name);
      AddSlash(pathbuff);

      if (strncmp(ptr->path,"home/",5) == 0) /* home/subdir */
	 {
	 strcat(pathbuff,"*");
	 AddSlash(pathbuff);

         if (*(ptr->path+4) != '/')
            {
            sprintf(OUTPUT,"Illegal use of home in files: %s\n",ptr->path);
	    CfLog(cferror,OUTPUT,"");
            return;
            }
         else
            {
            strcat(pathbuff,ptr->path+5);
            }

	 ExpandWildCardsAndDo(pathbuff,basename,RecFileCheck,ptr);
	 }
      else
	 {
	 ExpandWildCardsAndDo(pathbuff,basename,RecFileCheck,ptr);
	 }
      }
   }
}

/*******************************************************************/

void EditItemsInResolvConf(from,list)

struct Item *from, **list;

{ struct Item *ip;
  char buf[maxvarsize];

if (from == NULL || !IsDefinedClass(from->classes))
   {
   return;
   }
  
if (from == NULL)
   {
   return;
   }
else
   {
   EditItemsInResolvConf(from->next,list);
   if (isdigit(*(from->name)))
      {
      sprintf(buf,"nameserver %s",from->name);
      }
   else
      {
      strcpy(buf,from->name);
      }
   
   DeleteItemMatching(list,buf); /* del+prep = move to head of list */
   PrependItem(list,buf,NULL);
   return;
   }
}


/*******************************************************************/

int TZCheck(tzsys,tzlist)

char *tzsys,*tzlist;

{
if ((strncmp(tzsys,"GMT+",4) == 0) || (strncmp(tzsys,"GMT-",4) == 0))
   {
   return (strncmp(tzsys,tzlist,5) == 0); /* e.g. GMT+1 */
   }
else
   {
   return (strncmp(tzsys,tzlist,3) == 0); /* e.g. MET or CET */
   }
}

/*******************************************************************/

void ExpandWildCardsAndDo(wildpath,buffer,function,argptr)
 
char *wildpath, *buffer;
void (*function) ARGLIST((char *path, void *ptr));
void *argptr;

 /* This function recursively expands a path containing wildcards */
 /* and executes the function pointed to by function for each     */
 /* matching file or directory                                    */

 
{ char *rest, extract[bufsize], construct[bufsize],varstring[bufsize], *work;
  struct stat statbuf;
  DIR *dirh;
  struct dirent *dp;
  int count, isdir = false;

varstring[0] = '\0';

ExpandVarstring(wildpath,varstring,NULL);
work = varstring;

Debug2("ExpandWildCardsAndDo(%s=%s)\n",wildpath,work);
 
extract[0] = '\0';

if (*work == '/')
   {
   work++;
   isdir = true;
   }

sscanf(work,"%[^/]",extract);
rest = work + strlen(extract);
 
if (strlen(extract) == 0)
   {
   if (isdir)
      {
      strcat(buffer,"/");
      }
   (*function)(buffer,argptr);
   return;
   }
 
if (! IsWildCard(extract))
   {
   strcat(buffer,"/");
   if (BufferOverflow(buffer,extract))
       {
       sprintf(OUTPUT,"Culprit %s\n",extract);
       CfLog(cferror,OUTPUT,"");
       exit(0);
       }
   strcat(buffer,extract);
   ExpandWildCardsAndDo(rest,buffer,function,argptr);
   return;
   }
else
   { 
   strcat(buffer,"/");
   
   if ((dirh=opendir(buffer)) == NULL)
      {
      sprintf(OUTPUT,"Can't open dir: %s\n",buffer);
      CfLog(cferror,OUTPUT,"opendir");
      return;
      }

   count = 0;
   strcpy(construct,buffer);                 /* save relative path */
 
   for (dp = readdir(dirh); dp != 0; dp = readdir(dirh))
      {
      if (!SensibleFile(dp->d_name,buffer,NULL))
         {
         continue;
         }

      count++;
      strcpy(buffer,construct);
      strcat(buffer,dp->d_name);

      if (stat(buffer,&statbuf) == -1)
         {
         sprintf(OUTPUT,"Can't stat %s\n\n",buffer);
         CfLog(cferror,OUTPUT,"stat");
         continue;
         }
 
      if (S_ISDIR(statbuf.st_mode) && WildMatch(extract,dp->d_name))
         {
         ExpandWildCardsAndDo(rest,buffer,function,argptr);
         }
 
      }
 
   if (count == 0)
      {
      sprintf(OUTPUT,"No directories matching %s in %s\n",extract,buffer);
      CfLog(cfinform,OUTPUT,"");
      return;
      }
   closedir(dirh);
   }
}
 

/*******************************************************************/

int TouchDirectory(ptr)                     /* True if file path in /. */

struct File *ptr;

{ char *sp;

if (ptr->action == touch)
   {
   sp = ptr->path+strlen(ptr->path)-2;

   if (strcmp(sp,"/.") == 0)
      {
      return(true);
      }
   else
      {
      return false;
      }
   }
else
   {
   return false;
   }
}


/*******************************************************************/

void RecFileCheck(startpath,vp)

char *startpath;
void *vp;

{ struct File *ptr;
  struct stat sb; 

ptr = (struct File *)vp;
Verbose("%s: Checking files in %s...\n",VPREFIX,startpath);

if (stat(startpath,&sb) == -1)
   {
   CfLog(cferror,"Can't stat in recursive file check","stat");
   }
 
RecursiveCheck(startpath,ptr->plus,ptr->minus,ptr->action,ptr->uid,ptr->gid,ptr->recurse,0,ptr,&sb);
}

/*******************************************************************/
/* Toolkit fstab                                                   */
/*******************************************************************/

int MatchStringInFstab(str)

char *str;

{ FILE *fp;

if ((fp = fopen(VFSTAB[VSYSTEMHARDCLASS],"r")) == NULL)
   {
   sprintf(OUTPUT,"Can't open %s for reading\n",VPREFIX,VFSTAB[VSYSTEMHARDCLASS]);
   CfLog(cferror,OUTPUT,"fopen");
   return true; /* write nothing */
   }

while (!feof(fp))
   {
   ReadLine(VBUFF,bufsize,fp);

   if (VBUFF[0] == '#')
      {
      continue;
      }

   if (strstr(VBUFF,str))
      {
      fclose(fp);
      return true;
      }
   }

fclose(fp);
return(false);
}


