/* 

        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

*/

/*******************************************************************/
/*                                                                 */
/*  Cfengine : a site configuration langugae                       */
/*                                                                 */
/*  Module: (main) cfengine.c                                      */
/*                                                                 */
/*  Mark Burgess 1994/96                                           */
/*                                                                 */
/*******************************************************************/

#define INET

#include "cf.defs.h"
#include "cf.extern.h"
#include "../pub/getopt.h"

/*******************************************************************/
/* Functions internal to cfengine.c                                */
/*******************************************************************/

int main ARGLIST((int argc,char *argv[]));
void Initialize ARGLIST((int argc, char **argv));
void PreNetConfig ARGLIST((void));
void ReadRCFile ARGLIST((void));
void EchoValues ARGLIST((void));
void CheckSystemVariables ARGLIST((void));
void SetReferenceTime ARGLIST((int setclasses));
enum aseq EvaluateAction ARGLIST((char *action, struct Item **classlist));
void CheckOpts ARGLIST((int argc, char **argv));
int GetResource ARGLIST((char *var));
void BuildClassEnvironment ARGLIST((void));
void CheckForModule ARGLIST((char *actiontxt, char *args));
void Syntax ARGLIST((void));
int linux_redhat_version ARGLIST((void));

/*******************************************************************/
/* Level 0 : Main                                                  */
/*******************************************************************/

int main(argc,argv)

char *argv[];
int argc;

{ void HandleSignal();
  struct Item *action;

signal (SIGTERM,HandleSignal);                   /* Signal Handler */
signal (SIGHUP,HandleSignal);
signal (SIGINT,HandleSignal);
signal (SIGPIPE,HandleSignal);

Initialize(argc,argv);

SetReferenceTime(true);
 
if (! NOHARDCLASSES)
   {
   GetNameInfo();
   GetInterfaceInfo();
   }

PreNetConfig();

ReadRCFile(); /* Should come before parsing so that it can be overridden */

ParseInputFiles();
CheckFilters();
 
EchoValues();
 
if (PARSEONLY)                            /* Establish lock for root */
   {
   exit(0);
   }
 
if (PRSYSADM)                                           /* -a option */
   {
   printf("%s\n",VSYSADM);
   exit (0);
   }

openlog(VPREFIX,LOG_PID|LOG_NOWAIT|LOG_ODELAY,LOG_USER);

CheckSystemVariables();

SetReferenceTime(false); /* Reset */
   
for (action = VACTIONSEQ; action !=NULL; action=action->next)
   {
   if (IsExcluded(action->classes))
      {
      continue;
      }
   
   switch(EvaluateAction(action->name,&VADDCLASSES))
      {
      case mountinfo:
                        GetHomeInfo();
                        GetMountInfo();
                        break;
      case mkpaths:
                        if (! NOFILECHECK)
                           {
			   GetSetuidLog();
			   MakePaths();
			   SaveSetuidLog();
			   }
                        break;
      case lnks:
	  	        if (!GetLock(ASUniqueName("link"),"all",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
			
                        MakeChildLinks();
                        MakeLinks();
			ReleaseCurrentLock();
                        break;
      case simplelnks:

	  	        if (!GetLock(ASUniqueName("link"),"simple",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
	  
                        MakeLinks();
			ReleaseCurrentLock();
                        break;
      case childlnks:

	  	        if (!GetLock(ASUniqueName("link"),"child",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
			
                        MakeChildLinks();
			ReleaseCurrentLock();
                        break;
      case chkmail:
			if (!GetLock("Mount",CanonifyName(VFSTAB[VSYSTEMHARDCLASS]),0,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
			   {
			   exit(0); /* Note IFELAPSED must be zero to avoid conflict with mountresc */
			   }
	  
                        MailCheck();

			ReleaseCurrentLock();
                        break;
      case mountall:
                        if (! NOMOUNTS )
                           {
                           MountFileSystems();
                           }
                        break;
      case requir:
	                if (!GetLock(ASUniqueName("disks"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
	                   {
			   continue;
			   }
			CheckRequired();
			ReleaseCurrentLock();
			break;
      case tidyf:
                        if (! NOTIDY) 
                           {
			   if (!GetLock(ASUniqueName("tidy"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
			      {
			      continue;
			      }
			   
                           TidyFiles();
			   ReleaseCurrentLock();
                           }
                        break;
      case shellcom:
                        if (! NOSCRIPTS)
                           {
                           Scripts();
                           }
                        break;
      case chkfiles:
                        if (! NOFILECHECK)
                           {
			   if (!GetLock(ASUniqueName("files"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
			      {
			      continue;
			      }
			   
                           GetSetuidLog();
                           CheckFiles();
                           SaveSetuidLog();
			   ReleaseCurrentLock();
                           }
                        break;
      case disabl:
	  	        if (!GetLock(ASUniqueName("disable"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
			
                        DisableFiles();
			ReleaseCurrentLock();
                        break;
      case mountresc:
                        if (! NOMOUNTS)
                           {
			   if (!GetLock("Mountres",CanonifyName(VFSTAB[VSYSTEMHARDCLASS]),0,VEXPIREAFTER,VUQNAME,0))
			      {
			      exit(0);    /* Note IFELAPSED must be zero to avoid conflict with mountresc */
			      }

			   Banner("Mount file system resources");
                           MountHomeBinServers();
                           MountMisc();
			   ReleaseCurrentLock();
                           }
                        break;
      case umnt:
	  	        if (!GetLock(ASUniqueName("unmount"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
			
                        Unmount();
			ReleaseCurrentLock();
                        break;

      case edfil:
                        if (! NOEDITS)
                           {
                           EditFiles();
                           }
                        break;


      case resolv:

	                if (!GetLock(ASUniqueName("resolve"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
                           {
                           continue;
                           }
			
                        CheckResolv();
			ReleaseCurrentLock();
                        break;
      case imag:
                        if (! NOCOPY)
                           {
			   GetSetuidLog();
                           MakeImages();
			   SaveSetuidLog();
                           }
                        break;
      case netconfig:
	  
			if (IFCONF)
			   {
			   ConfigureInterfaces();
			   }
			
                        break;

      case tzone:
                        CheckTimeZone();
                        break;

      case procs:
                        if (! NOPROCS)
			   {
			   if (!GetLock(ASUniqueName("processes"),"",VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
			      {
			      continue;
			      }
			   
			   CheckProcesses();
			   ReleaseCurrentLock();
			   }
                        break;

      case plugin:      break;

      default:  
                        sprintf(VBUFF,"Undefined action %s in sequence\n",action->name);
                        FatalError(VBUFF);
                        break;
      }

   DeleteItemList(VADDCLASSES);
   VADDCLASSES = NULL;
   }

closelog();
exit(0);
return 0;
}

/*******************************************************************/
/* Level 1                                                         */
/*******************************************************************/

void Initialize(argc,argv)

char *argv[];
int argc;

{ char *sp, *cfargv[maxargs];
  int i, cfargc;
  struct stat statbuf;
  
strcpy(VDOMAIN,CF_START_DOMAIN);

ISCFENGINE = true;

VFACULTY[0] = '\0';
VSYSADM[0] = '\0';
VNETMASK[0]= '\0';
VBROADCAST[0] = '\0';
VMAILSERVER[0] = '\0';
VDEFAULTROUTE[0] = '\0';

ALLCLASSBUFFER[0] = '\0';
CURRENTITEM[0] = '\0';
GROUPBUFF[0] = '\0';
ACTIONBUFF[0] = '\0';
CURRENTPATH[0] = '\0';
CLASSBUFF[0] = '\0';
LINKFROM[0] = '\0';
VREPOSITORY = strdup("\0");
 
#ifndef HAVE_REGCOMP
re_syntax_options |= RE_INTERVALS;
#endif
 
strcpy(VINPUTFILE,"cfengine.conf");
strcpy(VNFSTYPE,"nfs");

InitHashTable();

AddClassToHeap("any");      /* This is a reserved word / wildcard */

if (stat("/etc/redhat-release",&statbuf) != -1)
   {
   Verbose("This appears to be a redhat system.\n");
   AddClassToHeap("redhat");
   linux_redhat_version();
   }

if (stat("/etc/SuSE-release",&statbuf) != -1)
   {
   Verbose("\nThis appears to be a SuSE system.\n");
   AddClassToHeap("SuSE");
   }

if (stat("/etc/slackware-release",&statbuf) != -1)
   {
   Verbose("\nThis appears to be a slackware system.\n");
   AddClassToHeap("slackware");
   }
 
if (stat("/etc/debian_version",&statbuf) != -1)
   {
   Verbose("\nThis appears to be a debian system.\n");
   AddClassToHeap("debian");
   }
 
/* Note we need to fix the options since the argv mechanism doesn't */
 /* work when the shell #!/bla/cfengine -v -f notation is used.      */
 /* Everything ends up inside a single argument! Here's the fix      */

cfargc = 1;
cfargv[0]="cfengine";

for (i = 1; i < argc; i++)
   {
   sp = argv[i];

   while (*sp != '\0')
      {
      while (*sp == ' ' && *sp != '\0') /* Skip to arg */
         {
	 if (*sp == ' ')
	    {
	    *sp = '\0'; /* Break argv string */
	    }
	 sp++;
         }

      cfargv[cfargc++] = sp;
      
      while (*sp != ' ' && *sp != '\0') /* Skip to white space */
         {
	 sp++;
         }
      }
   }

VDEFAULTBINSERVER.name = "";

VEXPIREAFTER = VDEFAULTEXPIREAFTER;
VIFELAPSED = VDEFAULTIFELAPSED;

CheckOpts(cfargc,cfargv);
}

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

void PreNetConfig()                           /* Execute a shell script */

{ struct stat buf;
  char comm[bufsize];
  char *sp;
  FILE *pp;

if (NOPRECONFIG)
   {
   CfLog(cfverbose,"Ignoring the cf.preconf file: option set","");
   return;
   }

if ((sp=getenv(CFINPUTSVAR)) != NULL)
   {
   sprintf(comm,"%s/%s",sp,VPRECONFIG);

   if (stat(comm,&buf) == -1)
       {
       CfLog(cfverbose,"No preconfiguration file","");
       return;
       }
   
   sprintf(comm,"%s/%s %s 2>&1",sp,VPRECONFIG,CLASSTEXT[VSYSTEMHARDCLASS]);
   }
else
   {
   sprintf(comm,"./%s",VPRECONFIG);

   if (stat(comm,&buf) == -1)
       {
       CfLog(cfverbose,"No preconfiguration file\n","");
       return;
       }
      
   sprintf(comm,"./%s %s",VPRECONFIG,CLASSTEXT[VSYSTEMHARDCLASS]);
   }

if (S_ISDIR(buf.st_mode) || S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode))
   {
   sprintf(OUTPUT,"Error: %s was not a regular file\n",VPRECONFIG);
   CfLog(cferror,OUTPUT,"");
   FatalError("Aborting.");
   }

Verbose("\n\nExecuting Net Preconfiguration script...%s\n\n",VPRECONFIG);
 
if ((pp = cfpopen(comm,"r")) == NULL)
   {
   sprintf(OUTPUT,"Failed to open pipe to %s\n",comm);
   CfLog(cferror,OUTPUT,"");
   return;
   }

while (!feof(pp))
   {
   if (ferror(pp))  /* abortable */
      {
      CfLog(cferror,"Error running preconfig\n","ferror");
      break;
      }

   ReadLine(VBUFF,bufsize,pp);

   if (feof(pp))
      {
      break;
      }
   
   if (ferror(pp))  /* abortable */
      {
      CfLog(cferror,"Error running preconfig\n","ferror");
      break;
      }

   CfLog(cfinform,VBUFF,"");
   }

cfpclose(pp);
}

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

void ReadRCFile()

{ char filename[bufsize], buffer[bufsize], *sp, *mp;
  char class[maxvarsize], variable[maxvarsize], value[maxvarsize];
  int c;
  FILE *fp;

filename[0] = buffer[0] = class[0] = variable[0] = value[0] = '\0';
LINENUMBER = 0;

if ((sp=getenv(CFINPUTSVAR)) != NULL)
   {
   strcpy(filename,sp);
   if (filename[strlen(filename)-1] != '/')
      {
      strcat(filename,"/");
      }
   }

strcat(filename,VRCFILE);

if ((fp = fopen(filename,"r")) == NULL)      /* Open root file */
   {
   return;
   }

while (!feof(fp))
   {
   ReadLine(buffer,bufsize,fp);
   LINENUMBER++;
   class[0]='\0';
   variable[0]='\0';
   value[0]='\0';

   if (strlen(buffer) == 0 || buffer[0] == '#')
      {
      continue;
      }

   if (strstr(buffer,":") == 0)
      {
      sprintf(OUTPUT,"Malformed line (missing :) in resource file %s - skipping\n",VRCFILE);
      CfLog(cferror,OUTPUT,"");
      continue;
      }

   sscanf(buffer,"%[^.].%[^:]:%[^\n]",class,variable,value);

   if (class[0] == '\0' || variable[0] == '\0' || value[0] == '\0')
      {
      sprintf(OUTPUT,"%s:%s - Bad resource\n",VRCFILE,buffer);
      CfLog(cferror,OUTPUT,"");
      sprintf(OUTPUT,"class=%s,variable=%s,value=%s\n",class,variable,value);
      CfLog(cferror,OUTPUT,"");
      FatalError("Bad resource");
      }

   if (strcmp(CLASSTEXT[VSYSTEMHARDCLASS],class) != 0) 
      {
      continue;  /* No point if not equal*/
      }

   if ((mp = strdup(value)) == NULL)
      {
      perror("malloc");
      FatalError("malloc in ReadRCFile");
      }

   sprintf(OUTPUT,"Redefining resource %s as %s (%s)\n",variable,value,class);
   CfLog(cfverbose,OUTPUT,"");

   c = VSYSTEMHARDCLASS;

   switch (GetResource(variable))
      {
      case rmountcom:
                        VMOUNTCOMM[c] = mp;
                        break;

      case runmountcom:
                        VUNMOUNTCOMM[c] = mp;
                        break;
      case rethernet:
                        VIFDEV[c] = mp;
                        break;
      case rmountopts:
                        VMOUNTOPTS[c] = mp;
                        break;
      case rfstab:
                        VFSTAB[c] = mp;
                        break;
      case rmaildir:
                        VMAILDIR[c] = mp;
                        break;
      case rnetstat:
                        VNETSTAT[c] = mp;
                        break;
      case rpscomm:
	                VPSCOMM[c] = mp;
	                break;
      case rpsopts:
	                VPSOPTS[c] = mp;
	                break;
      default:
                        sprintf(VBUFF,"Bad resource %s in %s\n",variable,VRCFILE);
                        FatalError(VBUFF);
                        break;
      }
   }

fclose(fp);
}

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

void EchoValues()

{ struct Item *ip;
  int n = 0;
  
if (strstr(VSYSNAME.nodename,ToLowerStr(VDOMAIN)))
   {
   strcpy(VFQNAME,VSYSNAME.nodename);
   
   while(VSYSNAME.nodename[n++] != '.')
      {
      }
   
   strncpy(VUQNAME,VSYSNAME.nodename,n-1);
   }
else
   {
   sprintf(VFQNAME,"%s.%s",VSYSNAME.nodename,ToLowerStr(VDOMAIN));
   strcpy(VUQNAME,VSYSNAME.nodename);
   }

Verbose("Accepted domain name: %s\n\n",VDOMAIN); 
bzero(VBUFF,bufsize);

if (GetMacroValue("OutputPrefix"))
   {
   ExpandVarstring("$(OutputPrefix)",VBUFF,NULL);
   }

if (strlen(VBUFF) != 0)
   {
   strncpy(VPREFIX,VBUFF,40);  /* No more than 40 char prefix (!) */
   }
else
   {
   strcpy(VPREFIX,"cfengine:");
   strcat(VPREFIX,VUQNAME);
   }


if (VERBOSE || DEBUG || D2 || D3)
   {
   struct Item *ip;
   
   ListDefinedClasses();

   printf("\nGlobal expiry time for locks: %d minutes\n",VEXPIREAFTER);
   printf("\nGlobal anti-spam elapse time: %d minutes\n\n",VIFELAPSED);

   printf("Extensions which should not be directories = ( ");
   for (ip = EXTENSIONLIST; ip != NULL; ip=ip->next)
      {
      printf("%s ",ip->name);
      }
   printf(")\n");

   printf("Suspicious filenames to be warned about = ( ");
   for (ip = SUSPICIOUSLIST; ip != NULL; ip=ip->next)
      {
      printf("%s ",ip->name);
      }
   printf(")\n");
   
   }

if (DEBUG || D2 || D3)
   {
   printf("\nFully qualified hostname is: %s\n",VFQNAME);
   printf("Unqualified hostname is: %s\n",VUQNAME);
   printf("\nSystem administrator mail address is: %s\n",VSYSADM);
   printf("Sensible size = %d\n",SENSIBLEFSSIZE);
   printf("Sensible count = %d\n",SENSIBLEFILECOUNT);
   printf("Edit File (Max) Size = %d\n\n",EDITFILESIZE);
   printf("------------------------------------------------------------\n");
   ListDefinedInterfaces();
   printf("------------------------------------------------------------\n");
   ListDefinedBinservers();
   printf("------------------------------------------------------------\n");
   ListDefinedHomeservers();
   printf("------------------------------------------------------------\n");
   ListDefinedHomePatterns();
   printf("------------------------------------------------------------\n");
   ListActionSequence();

   printf("\nWill need to copy from the following trusted sources = ( ");

   for (ip = VSERVERLIST; ip !=NULL; ip=ip->next)
      {
      printf("%s ",ip->name);
      }
   printf(")\n");
   printf("\nUsing mailserver %s\n",VMAILSERVER);
   printf("\nLocal mountpoints: ");
   for (ip = VMOUNTLIST; ip != NULL; ip=ip->next)
      {
      printf ("%s ",ip->name);
      }
   printf("\n");
   printf("\nDefault route for packets %s\n\n",VDEFAULTROUTE);
   printf("\nFile repository = %s\n\n",VREPOSITORY);
   printf("\nNet interface name = %s\n",VIFDEV[VSYSTEMHARDCLASS]);
   printf("------------------------------------------------------------\n");
   ListDefinedResolvers();
   printf("------------------------------------------------------------\n");
   ListDefinedRequired();
   printf("------------------------------------------------------------\n");
   ListDefinedMountables();
   printf("------------------------------------------------------------\n");
   ListMiscMounts();
   printf("------------------------------------------------------------\n");
   ListUnmounts();
   printf("------------------------------------------------------------\n");
   ListDefinedMakePaths();
   printf("------------------------------------------------------------\n");
   ListDefinedImports();
   printf("------------------------------------------------------------\n");
   ListFiles();
   printf("------------------------------------------------------------\n");
   ListACLs();
   printf("------------------------------------------------------------\n");
   ListFilters();
   printf("------------------------------------------------------------\n");   
   ListDefinedIgnore();
   printf("------------------------------------------------------------\n");
   ListFileEdits();
   printf("------------------------------------------------------------\n");
   ListProcesses();
   printf("------------------------------------------------------------\n");
   ListDefinedImages();
   printf("------------------------------------------------------------\n");
   ListDefinedTidy();
   printf("------------------------------------------------------------\n");
   ListDefinedDisable();
   printf("------------------------------------------------------------\n");
   ListDefinedLinks();
   printf("------------------------------------------------------------\n");
   ListDefinedLinkchs();
   printf("------------------------------------------------------------\n");
   ListDefinedScripts();
   printf("------------------------------------------------------------\n");

   
   if (IGNORELOCK)
      {
      printf("\nIgnoring locks...\n");
      }
   }
}

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

void CheckSystemVariables()

{ char id[maxvarsize];
  struct stat statbuf; 
  int time, hash, activecfs, locks;
  char *sp;

Debug2("\n\n");

if (VACTIONSEQ == NULL)
   {
   Warning("actionsequence is empty ");
   Warning("perhaps cfengine.conf has not yet been set up?");
   }

sprintf(id,"%d",geteuid());   /* get effective user id */

if (VACCESSLIST != NULL && !IsItemIn(VACCESSLIST,id))
   {
   FatalError("Access denied");
   }

Debug2("cfengine -d : Debugging output enabled.\n");

if (DONTDO && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -n: Running in ``All talk and no action'' mode\n");
   }

if (TRAVLINKS && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -l : will traverse symbolic links\n");
   }

if ( ! IFCONF && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -i : suppressing interface configuration\n");
   }

if ( NOFILECHECK && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -c : suppressing file checks\n");
   }

if ( NOSCRIPTS && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -s : suppressing script execution\n");
   }

if ( NOTIDY && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -t : suppressing tidy function\n");
   }

if ( NOMOUNTS && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -m : suppressing mount operations\n");
   }

if ( MOUNTCHECK && (VERBOSE || DEBUG || D2))
   {
   printf("cfengine -C : check mount points\n");
   }


if (ERRORCOUNT > 0)
   {
   FatalError("Execution terminated after parsing due to errors in program");
   }
 
VCANONICALFILE = strdup(CanonifyName(VINPUTFILE));

strcpy(VLOGDIR,LOGFILEDIR);
strcpy(VLOCKDIR,LOCKFILEDIR);
 
if (GetMacroValue("LockDirectory"))
   {
   strcpy(VLOCKDIR,GetMacroValue("LockDirectory"));
   }

if (GetMacroValue("LogDirectory"))
   {
   strcpy(VLOGDIR,GetMacroValue("LogDirectory"));
   }

sprintf(LOGFILE,"%s/cfengine.%s.log",VLOGDIR,VUQNAME);
VSETUIDLOG = strdup(LOGFILE); 
 
if (!IsPrivileged())
   {
   Verbose("\n(Non privileged user...)\n\n");
   
   if ((sp = getenv("HOME")) == NULL)
      {
      FatalError("You do not have a HOME variable pointing to your home directory");
      }

   sprintf(VLOGDIR,"%s/.cfengine",sp);
   sprintf(VLOCKDIR,"%s/.cfengine",sp);

   sprintf(VBUFF,"%s/.cfengine/test",sp);
   MakeDirectoriesFor(VBUFF);
   }
else
   {
   sprintf(VBUFF,"%s/test",LOCKFILEDIR);
   MakeDirectoriesFor(VBUFF);
   sprintf(VBUFF,"%s/test",LOGFILEDIR);
   MakeDirectoriesFor(VBUFF);
   }

if (stat(VLOCKDIR,&statbuf) == -1)
   {
   if (statbuf.st_mode & 07)
      {
      CfLog(cferror,"Lock directory %s is not private! Mode %o\n",VLOCKDIR);
      }
   }
 
chmod(VLOCKDIR,(mode_t)0700); /* Locks must be immutable to others */

LoadSecretKeys();
 
if (GetMacroValue("MaxCfengines"))
   {
   activecfs = atoi(GetMacroValue("MaxCfengines"));
   locks = CountActiveLocks();
   
   if (locks >= activecfs)
      {
      sprintf(OUTPUT,"Too many cfengines running (%d/%d)\n",locks,activecfs);
      CfLog(cferror,OUTPUT,"");
      closelog();
      exit(1);
      }
   }

if (GetMacroValue("Verbose") && (strcmp(GetMacroValue("Verbose"),"on") == 0))
   {
   VERBOSE = true;
   }

if (GetMacroValue("Inform") && (strcmp(GetMacroValue("Inform"),"on") == 0))
   {
   INFORM = true;
   } 

INFORM_save = INFORM;
 
if (GetMacroValue("Syslog") && (strcmp(GetMacroValue("Syslog"),"on") == 0))
   {
   LOGGING = true;
   }

LOGGING_save = LOGGING;

if (GetMacroValue("DryRun") && (strcmp(GetMacroValue("DryRun"),"on") == 0))
   {
   DONTDO = true;
   AddClassToHeap("opt_dry_run");
   }

if (GetMacroValue("BinaryPaddingChar"))
   {
   strcpy(VBUFF,GetMacroValue("BinaryPaddingChar"));
   
   if (VBUFF[0] == '\\')
      {
      switch (VBUFF[1])
	 {
	 case '0': PADCHAR = '\0';
	     break;
	 case '\n': PADCHAR = '\n';

	     break;
	 case '\\': PADCHAR = '\\';
	 }
      }
   else
      {
      PADCHAR = VBUFF[0];
      }
   }

 
if (GetMacroValue("Warnings") && (strcmp(GetMacroValue("Warnings"),"on") == 0))
   {
   WARNINGS = true;
   }

if (GetMacroValue("NonAlphaNumFiles") && (strcmp(GetMacroValue("NonAlphaNumFiles"),"on") == 0))
   {
   NONALPHAFILES = true;
   }

if (GetMacroValue("SecureInput") && (strcmp(GetMacroValue("SecureInput"),"on") == 0))
   {
   CFPARANOID = true;
   }

if (GetMacroValue("ShowActions") && (strcmp(GetMacroValue("ShowActions"),"on") == 0))
   {
   SHOWACTIONS = true;
   }

 if (GetMacroValue("Umask"))
   {
   mode_t val;
   val = (mode_t)atoi(GetMacroValue("Umask"));
   if (umask(val) == (mode_t)-1)
      {
      sprintf(OUTPUT,"Can't set umask to %0\n",val);
      CfLog(cferror,OUTPUT,"umask");
      }
   }

if (GetMacroValue("DefaultCopyType"))
   {
   if (strcmp(GetMacroValue("DefaultCopyType"),"mtime") == 0)
      {
      DEFAULTCOPYTYPE = 'm';
      }
   if (strcmp(GetMacroValue("DefaultCopyType"),"checksum") == 0)
      {
      DEFAULTCOPYTYPE = 'c';
      }
   if (strcmp(GetMacroValue("DefaultCopyType"),"binary") == 0)
      {
      DEFAULTCOPYTYPE = 'b';
      }
   if (strcmp(GetMacroValue("DefaultCopyType"),"ctime") == 0)
      {
      DEFAULTCOPYTYPE = 't';
      }
   }
 
if (GetMacroValue("ChecksumDatabase"))
   {
   ExpandVarstring("$(ChecksumDatabase)",VBUFF,NULL);

   CHECKSUMDB = strdup(VBUFF);

   if (*CHECKSUMDB != '/')
      {
      FatalError("$(ChecksumDatabase) does not expand to an absolute filename\n");
      }
   }


if (GetMacroValue("CompressCommand"))
   {
   ExpandVarstring("$(CompressCommand)",VBUFF,NULL);

   COMPRESSCOMMAND = strdup(VBUFF);
   
   if (*COMPRESSCOMMAND != '/')
      {
      FatalError("$(ChecksumDatabase) does not expand to an absolute filename\n");
      }
   }

 
if (GetMacroValue("ChecksumUpdates") && (strcmp(GetMacroValue("ChecksumUpdates"),"on") == 0))
   {
   CHECKSUMUPDATES = true;
   } 
 
if (GetMacroValue("TimeOut"))
   {
   time = atoi(GetMacroValue("TimeOut"));

   if (time < 3 || time > 60)
      {
      CfLog(cfinform,"TimeOut not between 3 and 60 seconds, ignoring.\n","");
      }
   else
      {
      CF_TIMEOUT = time;
      }
   }

if (NOSPLAY)
   {
   return;
   }

time = 0;
hash = Hash(VFQNAME);
 
if (GetMacroValue("SplayTime"))
   {
   time = atoi(GetMacroValue("SplayTime"));

   if (time < 0)
      {
      CfLog(cfinform,"SplayTime with negative value, ignoring.\n","");
      return;
      }

   Verbose("Sleeping for SplayTime %d seconds\n\n",(int)(time*60*hash/hashtablesize));
   sleep((int)(time*60*hash/hashtablesize));
   }
}

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

void SetReferenceTime(setclasses)

int setclasses;

{ time_t tloc;
 
if ((tloc = time((time_t *)NULL)) == -1)
   {
   printf("Couldn't read system clock\n");
   }

CFSTARTTIME = tloc;

sprintf(VBUFF,"%s",ctime(&tloc));

Verbose("Reference time set to %s\n",ctime(&tloc));

if (setclasses)
   {
   AddTimeClass(VBUFF);
   }
}


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

enum aseq EvaluateAction(action,classlist)

char *action;
struct Item **classlist;

{ int i,j = 0;
  char *sp,cbuff[bufsize],actiontxt[bufsize],mod[bufsize],args[bufsize];
  struct Item *ip;

cbuff[0]='\0';
actiontxt[0]='\0';
mod[0] = args[0] = '\0'; 
sscanf(action,"%s %[^\n]",mod,args);
sp = mod;

while (*sp != '\0')
   {
   ++j;
   sscanf(sp,"%[^.]",cbuff);

   while ((*sp != '\0') && (*sp !='.'))
      {
      sp++;
      }
 
   if (*sp == '.')
      {
      sp++;
      }
 
   if (IsHardClass(cbuff))
      {
      sprintf(OUTPUT,"Error in action sequence: %s\n",action);
      CfLog(cferror,OUTPUT,"");
      FatalError("You cannot add a reserved class!");
      }
 
   if (j == 1)
      {
      VIFELAPSED = VDEFAULTIFELAPSED;
      VEXPIREAFTER = VDEFAULTEXPIREAFTER;
      strcpy(actiontxt,cbuff);
      continue;
      }
   else
      {
      if ((strncmp(actiontxt,"module:",7) != 0) && ! IsSpecialClass(cbuff))
	 {
         AppendItem(classlist,cbuff,NULL);
	 }
      }
   }

BuildClassEnvironment();

if ((VERBOSE || DEBUG || D2) && *classlist != NULL)
   {
   printf("\n                  New temporary class additions\n");
   printf("                  -----------------------------\n");
   for (ip = *classlist; ip != NULL; ip=ip->next)
      {
      printf("                             %s\n",ip->name);
      }
   }

for (i = 0; ACTIONSEQTEXT[i] != NULL; i++)
   {
   if (strcmp(ACTIONSEQTEXT[i],actiontxt) == 0)
      {
      return (enum aseq) i;
      }
   }

Debug("Checking if entry is a module\n");
 
if (strncmp(actiontxt,"module:",7) == 0)
   {
   CheckForModule(actiontxt,args);
   return plugin;
   }

return(non);
}


/*******************************************************************/
/* Level 2                                                         */
/*******************************************************************/

void CheckOpts(argc,argv)

char **argv;
int argc;

{ extern char *optarg;
  int optindex = 0;
  int c;

while ((c=getopt_long(argc,argv,"MAbKqkhYHd:vlniIf:pPmcCtsSaeEVD:N:LwxXuU",OPTIONS,&optindex)) != EOF)
  {
  switch ((char) c)
      {
      case 'E': printf("%s: the enforce-links option can only be used in interactive mode.\n",VPREFIX);
                printf("%s: Do you really want to blindly enforce ALL links? (y/n)\n",VPREFIX);
                if (getchar() != 'y')
                   {
                   printf("cfengine: aborting\n");
		   closelog();
                   exit(1);
                   }

                ENFORCELINKS = true;
                break;

      case 'f': strcpy(VINPUTFILE,optarg);
                MINUSF = true;
                break;

      case 'd': 
	        AddClassToHeap("opt_debug");
                switch ((optarg==NULL) ? '3' : *optarg)
                   {
                   case '1': D1 = true;
                             break;
                   case '2': D2 = true;
                             break;
		   case '3': D3 = true;
		             VERBOSE = true;
		             break;
                   default:  DEBUG = true;
                             break;
                   }
                break;

      case 'M':
		NOMODULES = true;
		break;
		
      case 'K': IGNORELOCK = true;
	        break;

      case 'A': IGNORELOCK = true;
	        break;
		
      case 'D': AddCompoundClass(optarg);
                break;

      case 'N': NegateCompoundClass(optarg,&VNEGHEAP);
                break;

      case 'b': FORCENETCOPY = true;
	        break;

      case 'e': NOEDITS = true;
                break;

      case 'i': IFCONF = false;
                break;

      case 'I': INFORM = true;
	        break;

      case 'v': VERBOSE = true;
                break;

      case 'l': TRAVLINKS = true;
                break;

      case 'n': DONTDO = true;
	        IGNORELOCK = true;
	        AddClassToHeap("opt_dry_run");
                break;

      case 'p': PARSEONLY = true;
                IGNORELOCK = true;
                break;

      case 'm': NOMOUNTS = true;
                break;

      case 'c': NOFILECHECK = true;
                break;

      case 'C': MOUNTCHECK = true;
                break;

      case 't': NOTIDY = true;
                break;

      case 's': NOSCRIPTS = true;
                break;

      case 'a': PRSYSADM = true;
                IGNORELOCK = true;
                break;

      case 'L': KILLOLDLINKS = true;
                break;

      case 'V': printf("GNU %s\n%s\n",VERSION,COPYRIGHT);
	        printf("This program is covered by the GNU Public License and may be\n");
		printf("copied free of charge.  No warranty is implied.\n\n");
                exit(0);

      case 'h': Syntax();
                exit(0);

      case 'x': NOPRECONFIG = true;
                break;

      case 'w': WARNINGS = false;
                break;

      case 'X': NOLINKS = true;
                break;

      case 'k': NOCOPY = true;
                break;

      case 'S': SILENT = true;
                break;

      case 'u': USEENVIRON = true;
                break;

      case 'U': UNDERSCORE_CLASSES = true;
	        break;

      case 'H': NOHARDCLASSES = true;
	        break;

      case 'P': NOPROCS = true;
	        break;

      case 'q': NOSPLAY = true;
	        break;

      case 'Y': CFPARANOID = true;
	        break;

      default:  Syntax();
                exit(1);

      }
   }
}

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

int GetResource(var)

char *var;

{ int i;

for (i = 0; VRESOURCES[i] != '\0'; i++)
   {
   if (strcmp(VRESOURCES[i],var)==0)
      {
      return i;
      }
   }

sprintf (VBUFF,"Unknown resource %s in %s",var,VRCFILE);

FatalError(VBUFF);

return 0; /* This to placate insight */
}

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

void BuildClassEnvironment()

{ struct Item *ip;

Debug("(BuildClassEnvironment)\n");

sprintf(ALLCLASSBUFFER,"%s=\0",CFALLCLASSESVAR);

for (ip = VHEAP; ip != NULL; ip=ip->next)
   {
   if (IsDefinedClass(ip->name))
      {
      if (BufferOverflow(ALLCLASSBUFFER,ip->name))
	 {
	 printf("%s: culprit BuildClassEnvironment()\n",VPREFIX);
	 return;
	 }

      strcat(ALLCLASSBUFFER,ip->name);
      strcat(ALLCLASSBUFFER,":");
      }
   }

for (ip = VALLADDCLASSES; ip != NULL; ip=ip->next)
   {
   if (IsDefinedClass(ip->name))
      {
      if (BufferOverflow(ALLCLASSBUFFER,ip->name))
	 {
	 printf("%s: culprit BuildClassEnvironment()\n",VPREFIX);
	 return;
	 }
      
      strcat(ALLCLASSBUFFER,ip->name);
      strcat(ALLCLASSBUFFER,":");
      }
   }

Debug2("---\nENVIRONMENT: %s\n---\n",ALLCLASSBUFFER);

if (USEENVIRON)
   {
   if (putenv(ALLCLASSBUFFER) == -1)
      {
      perror("putenv");
      }
   }
}

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

void CheckForModule(actiontxt,args)

char *actiontxt,*args;

{ struct stat statbuf;
  char line[bufsize],command[bufsize],name[maxvarsize],content[bufsize],*value,*sp;
  FILE *pp;
  int print;

if (NOMODULES)
   {
   return;
   }
  
bzero(VBUFF,bufsize);

if (GetMacroValue("moduledirectory"))
   {
   ExpandVarstring("$(moduledirectory)",VBUFF,NULL);
   }
else
   {
   sprintf(OUTPUT,"Plugin was called in ActionSequence but moduledirectory was not defined\n");
   CfLog(cfinform,OUTPUT,"");
   return;
   }

AddSlash(VBUFF);
strcat(VBUFF,actiontxt);
 
if (stat(VBUFF,&statbuf) == -1)
   {
   sprintf(OUTPUT,"(Plug-in %s not found)",VBUFF);
   Banner(OUTPUT);
   return;
   }

if ((statbuf.st_uid != 0) && (statbuf.st_uid != getuid()))
   {
   sprintf(OUTPUT,"Module %s was not owned by uid=%d executing cfengine\n",VBUFF,getuid());
   CfLog(cferror,OUTPUT,"");
   return;
   }
  
sprintf(OUTPUT,"Plug-in `%s\'",actiontxt);
Banner(OUTPUT);

strcat(VBUFF," ");

if (BufferOverflow(VBUFF,args))
   {
   sprintf(OUTPUT,"Culprit: class list for module (shouldn't happen)\n" );
   CfLog(cferror,OUTPUT,"");
   return;
   }

strcat(VBUFF,args); 
ExpandVarstring(VBUFF,command,NULL); 
 
if (!GetLock(ASUniqueName("actionseq"),CanonifyName(actiontxt),VIFELAPSED,VEXPIREAFTER,VUQNAME,CFSTARTTIME))
   {
   return;
   }

Verbose("Exec module [%s]\n",command); 
   
if ((pp = cfpopen(command,"r")) == NULL)
   {
   sprintf(OUTPUT,"Couldn't open pipe from %s\n",actiontxt);
   CfLog(cferror,OUTPUT,"cfpopen");
   return;
   }

while (!feof(pp))
   {
   if (ferror(pp))  /* abortable */
      {
      sprintf(OUTPUT,"Shell command pipe %s\n",actiontxt);
      CfLog(cferror,OUTPUT,"ferror");
      break;
      }
   
   ReadLine(line,bufsize,pp);

   if (strlen(line) > bufsize - 80)
      {
      sprintf(OUTPUT,"Line from module %s is too long to be sensible\n",actiontxt);
      CfLog(cferror,OUTPUT,"");
      break;
      }
   
   if (ferror(pp))  /* abortable */
      {
      sprintf(OUTPUT,"Shell command pipe %s\n",actiontxt);
      CfLog(cferror,OUTPUT,"ferror");
      break;
      }	 
   
   print = false;
   
   for (sp = line; *sp != '\0'; sp++)
      {
      if (! isspace(*sp))
	 {
	 print = true;
	 break;
	 }
      }
   
   switch (*line)
      {
      case '+':
	  Verbose("Activated classes: %s\n",line+1);
	  AddMultipleClasses(line+1);
	  break;
      case '-':
	  Verbose("Deactivated classes: %s\n",line+1);
	  NegateCompoundClass(line+1,&VNEGHEAP);
	  break;
      case '=':
          sscanf(line+1,"%[^=]=%[^\n]",name,content);
	  AddMacroValue(name,content);
	  break;

      default:
	  if (print)
	     {
	     printf("%s:%s: %s\n",VPREFIX,actiontxt,line);
	     }
      }
   }

cfpclose(pp);

ReleaseCurrentLock();
}

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

void Syntax()

{ int i;

printf("GNU cfengine: A system configuration engine \n%s\n%s\n",VERSION,COPYRIGHT);
printf("\n");
printf("Options:\n\n");

for (i=0; OPTIONS[i].name != NULL; i++)
   {
   printf("--%-20s    (-%c)\n",OPTIONS[i].name,(char)OPTIONS[i].val);
   }

printf("\nDebug levels: 1=parsing, 2=running, 3=summary\n");

printf("\nBug reports to bug-cfengine@gnu.org (News: gnu.cfengine.bug)\n");
printf("General help to help-cfengine@gnu.org (News: gnu.cfengine.help)\n");
printf("Info & fixes at http://www.iu.hio.no/cfengine\n");
}

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

int linux_redhat_version(void)

/* Contrib Pekka Savola */

{
#define FMT_SIZ 15
#define REDHAT_ID "Red Hat"
#define MANDRAKE_ID "Linux Mandrake"
#define RAWHIDE_ID "Raw Hide"

/* Linux Mandrake release 7.1 (helium)
 * Red Hat Linux release 6.2 (Zoot)
 * Raw Hide 20000526
 */
 
#define RH_REL_STR "%s %s"			/* "Red Hat", "Linux Mandrake" */
#define RH_REL_STR_REDHAT "%*s %*s %u.%u %*s"	/* "Linux release 6.1 (Cartman)" */
#define RH_REL_STR_MANDRAKE "%*s %u.%u %*s"	/* "release 7.0 (Whatever)" */
#define RH_REL_FILENAME "/etc/redhat-release"

FILE *fp;
 
char fmt1[maxvarsize]; 
char fmt2[maxvarsize];
char fmt[2*FMT_SIZ];
 
char *distro="";
 
int major = -1; 
int release = -1; 

 if ((fp = fopen(RH_REL_FILENAME,"r")) == NULL)
    {
    return 1;
    }

 Verbose("Looking for redhat linux info...\n");
 
/* Get necessary strings to distinguish between different distros using the same 
   file name */
 
 fscanf(fp, RH_REL_STR, fmt1, fmt2);
 
/* A few sanity checks */
 if (strlen(fmt1) >= FMT_SIZ || strlen(fmt2) >= FMT_SIZ )
    {
    Verbose("Your %s is corrupted, either one of it's first words is longer than %i chars\n", RH_REL_FILENAME, FMT_SIZ); 
    return 2;
    }
 
 sprintf(fmt, "%s %s", fmt1, fmt2);
 
 if (!strncmp(fmt, REDHAT_ID, sizeof(fmt)))
    {
    fscanf(fp, RH_REL_STR_REDHAT, &major, &release );
    Verbose("This appears to be a Redhat %u.%u system..", major, release);
    distro="redhat";
    }
 else if (!strncmp(fmt, MANDRAKE_ID, sizeof(fmt)))
    {
    fscanf(fp, RH_REL_STR_MANDRAKE, &major, &release );
    Verbose("This appears to be a Mandrake %u.%u system..", major, release);
    distro="mandrake";
    }
 
 fclose(fp);

 if ( major != -1 && release != -1 && distro != "" )
    {
    sprintf(fmt1, "%s_%u", distro, major);
    sprintf(fmt2, "%s_%u_%u", distro, major, release);
    
    AddClassToHeap(fmt1);
    AddClassToHeap(fmt2);
    }
 return 0;
}


/* EOF */
