diff -Naur cfengine-2.1.13.orig/doc/cfengine-Reference.texinfo cfengine-2.1.13/doc/cfengine-Reference.texinfo --- cfengine-2.1.13.orig/doc/cfengine-Reference.texinfo 2005-01-23 03:08:01.000000000 -0500 +++ cfengine-2.1.13/doc/cfengine-Reference.texinfo 2005-02-24 22:45:07.000000000 -0500 @@ -2397,6 +2397,7 @@ * deletenonownermail:: * domain:: * dryrun:: +* dpkginstallcommand:: * editbinaryfilesize:: * editfilesize:: * emptyresolvconf:: @@ -2423,6 +2424,7 @@ * repchar:: * repository:: * RPMcommand:: +* rpminstallcommand:: * schedule:: * secureinput:: * sensiblecount:: @@ -2435,6 +2437,7 @@ * SplayTime:: * split:: * spooldirectories:: +* suninstallcommand:: * suspiciousnames:: * sysadm:: * Syslog:: @@ -2654,8 +2657,10 @@ @item packages executes commands defined under the @code{packages} section of the program. This will query the system's package database -for the specified packages, at the specified versions, and set -classes based on whether or not those packages exist. +for the specified packages, at the specified versions, set +classes based on whether or not those packages exist, and +optionally install those packages using a pre-defined package +manager command. @item shellcommands executes all the commands defined under the @code{shellcommands} @@ -3163,7 +3168,7 @@ @c SUBSECTION @c ..................................................... -@node domain, dryrun, deletenonownermail, control +@node domain, dpkginstallcommand, deletenonownermail, control @subsection domain @cindex domain @vindex domain @@ -3187,7 +3192,26 @@ @c SUBSECTION @c ..................................................... -@node dryrun, editbinaryfilesize, domain, control +@node dpkginstallcommand, dryrun, domain, control +@subsection DPKGInstallCommand + +Sets the command used to install packages that need to be installed +under the DPKG package manager. + +@smallexample +DPKGInstallCommand = ( "/usr/bin/pkgmgr %s" ) +@end smallexample + +By default, this variable is not set, meaning that any packages with +action=install will NOT be installed if installation is required. Note +the "'s around the string, and the %s is replaced with the list of +packages to be installed, each separated by a ' ' (space). + +@c ..................................................... +@c SUBSECTION +@c ..................................................... + +@node dryrun, editbinaryfilesize, dpkginstallcommand, control @subsection DryRun @cindex DryRun @vindex DryRun @@ -3963,7 +3987,7 @@ @c SUBSECTION @c ..................................................... -@node RPMcommand, schedule, repository, control +@node RPMcommand, rpminstallcommand, repository, control @subsection RPMcommand The default value of the Red Hat Package manager command @file{/bin/rpm} can be altered @@ -3976,7 +4000,26 @@ @c SUBSECTION @c ..................................................... -@node schedule, secureinput, RPMcommand, control +@node rpminstallcommand, schedule, RPMcommand, control +@subsection RPMInstallCommand + +Sets the command used to install packages that need to be installed +under the RPM package manager. + +@smallexample +RPMInstallCommand = ( "/usr/bin/pkgmgr %s" ) +@end smallexample + +By default, this variable is not set, meaning that any packages with +action=install will NOT be installed if installation is required. Note +the "'s around the string, and the %s is replaced with the list of +packages to be installed, each separated by a ' ' (space). + +@c ..................................................... +@c SUBSECTION +@c ..................................................... + +@node schedule, secureinput, rpminstallcommand, control @subsection Schedule @cindex scheduling @vindex scheduling @@ -4238,7 +4281,7 @@ @c SUBSECTION @c ..................................................... -@node spooldirectories, suspiciousnames, split, control +@node spooldirectories, suninstallcommand, split, control @subsection SpoolDirectories @cindex SpoolDirectories @vindex SpoolDirectories @@ -4253,12 +4296,30 @@ SpoolDirectories = ( /var/spool/cron/crontabs /var/spool/cron/atjobs ) @end example +@c ..................................................... +@c SUBSECTION +@c ..................................................... + +@node suninstallcommand, suspiciousnames, spooldirectories, control +@subsection SUNInstallCommand + +Sets the command used to install packages that need to be installed +under the SUN package manager. + +@smallexample +SUNInstallCommand = ( "/usr/bin/pkgmgr %s" ) +@end smallexample + +By default, this variable is not set, meaning that any packages with +action=install will NOT be installed if installation is required. Note +the "'s around the string, and the %s is replaced with the list of +packages to be installed, each separated by a ' ' (space). @c ..................................................... @c SUBSECTION @c ..................................................... -@node suspiciousnames, sysadm, spooldirectories, control +@node suspiciousnames, sysadm, suninstallcommand, control @subsection suspiciousnames @cindex suspiciousnames @vindex SuspiciousNames @@ -8766,7 +8827,10 @@ @cindex Checking for installed packages The packages action allows you to check for the existance of packages -on the system, as determined by the package database you select. +on the system, as determined by the package database you select. Optionally, +if a package install command was specified, the package can be installed +if it is not there. + This operation is set up such that it tries not to make assumptions about the package manager in use. For example, it should be possible to use RPM on a Solaris box. @@ -8781,20 +8845,18 @@ @var{class}:: @var{package-name} - pkgmgr=@b{none}/@var{rpm} + pkgmgr=@b{none}/@var{rpm/dpkg/sun} cmp=@b{eq}/@var{lt/gt/ge/le/ne} version=@var{version-string} define=@var{class-list}(,:.) elsedefine=@var{class-list}(,:.) + action=@b{none}/@var{install/remove} ifelapsed=@var{mins} expireafter=@var{mins} @end smallexample @end cartouche - -@noindent -Associated variables in control are @code{DefaultPkgMgr} and @code{RPMcommand}. @table @code @item cmp @@ -8879,6 +8941,14 @@ the package you really want, and specify it. It may take a few extra extra seconds to check, but it will save you lots of headaches later. +@item dpkg +@cindex Debian Package Database Queries +Please document me! + +@item sun +@cindex Sun Package Database Queries +Please document me! + @end table @item define @@ -8889,17 +8959,64 @@ Specifies the list of classes to define if the specified package is not installed. +@item action +Specifies whether the packages should actually do anything about the +situation it finds. The default for this is to do nothing. Of course, +the classes in @code{define} and @code{elsedefine} will alwass be defined, +as applicable, regardless of the action specified. + +@table @code +@item install +Installs the package using the command associated with the selected +package manager, if it is not currently on the system at the requested +version, as follows: + +@itemize @bullet +@item +RPM - +RPMInstallCommand +@item +DPKG - +DPKGInstallCommand +@item +SUN - +SUNInstallCommand +@end itemize + +Each variable is of the format: + +@smallexample +FOOInstallCommand = ( "/usr/bin/foo --args %s --more-args" ) +@end smallexample + +The ---args are of course optional. The %s is replaced with a space-separated +list of the package names that were checked, and found to not be installed. + +@item remove +CURRENTLY PARSED BUT NOT IMPLEMENTED +@end table @end table -Example: +@noindent +NOTE: classes are defined according to the result of the check, not +any action performed as a result of that check. In otherwords, if for +example you have a situation where a package is not installed, +and the action= is set to install, the classes in @code{elsedefine} will +be defined @b{regardless} of whether or not the install was successful. +Assuming the package installed, the next run of cfagent will pick up that +fact. This has to be done since the package installs are batched, so there +is no reliable way to know if a given package was installed. + +@noindent +Examples: @smallexample packages: redhat_8_0:: m4 version=0:1.4.1-11 cmp=eq pkgmgr=rpm elsedefine=needsm4 - @end smallexample + @noindent In this first example, we are looking for the m4 package at exactly version 0:1.4.1-11. The installed m4 package on a redhat_8_0 box has no epoch @@ -8917,6 +9034,7 @@ make version=0:4.5-2 cmp=ge define=hasmake elsedefine=needsmake @end smallexample + @noindent In the second example, we use the DefaultPkgMgr variable to set the default for the @code{pkgmgr} attribute to @code{rpm}. The actual version of make @@ -8924,6 +9042,32 @@ for greater than or equal to this version, the hasmake class will be defined. +@smallexample +control: + redhat:; + DefaultPkgMgr = ( rpm ) + RPMInstallCommand = ( "/usr/sbin/up2date %s" ) + +packages: + redhat_8_0:: + make define=hasmake elsedefine=needsmake action=install +@end smallexample + +@noindent +This example is much like the second example, except that if the package is +not installed, cfengine will attempt to install it using the command in +RPMInstallCommand, replacing the %s with the package name, @code{make}. +If there were multiple packages specified in this way, the package installation +would occur at the end of the package checks, and one command would be run, +with %s replaced with a list of all package names. In this example we chose +not to use a version spec, but it is allowed, and as always, is optional. + +@noindent +NOTE here that if make was not installed when the check is made, +@code{needsmake} is defined, regardless of whether or not the +install succeeds. If the install is successful, the next cfagent +run will define @code{hasmake}. + @page @c ------------------------------------------------------------------------------- diff -Naur cfengine-2.1.13.orig/src/cf.defs.h cfengine-2.1.13/src/cf.defs.h --- cfengine-2.1.13.orig/src/cf.defs.h 2005-01-17 17:24:51.000000000 -0500 +++ cfengine-2.1.13/src/cf.defs.h 2005-02-23 22:14:00.000000000 -0500 @@ -1163,6 +1163,15 @@ /*******************************************************************/ +enum pkgactions /* What to do with a package if it is found/not found */ + { + pkgaction_install, + pkgaction_remove, + pkgaction_none + }; + +/*******************************************************************/ + typedef char flag; enum socks @@ -1832,6 +1841,7 @@ char *ver; enum cmpsense cmp; enum pkgmgrs pkgmgr; + enum pkgactions action; struct Package *next; int ifelapsed; int expireafter; diff -Naur cfengine-2.1.13.orig/src/cf.extern.h cfengine-2.1.13/src/cf.extern.h --- cfengine-2.1.13.orig/src/cf.extern.h 2005-01-06 08:29:51.000000000 -0500 +++ cfengine-2.1.13/src/cf.extern.h 2005-02-23 22:14:00.000000000 -0500 @@ -463,6 +463,7 @@ extern char *VRESOURCES[]; extern char *CMPSENSETEXT[]; extern char *PKGMGRTEXT[]; +extern char *PKGACTIONTEXT[]; extern int VTIMEOUT; extern mode_t UMASK; @@ -486,6 +487,7 @@ extern enum cmpsense CMPSENSE; extern enum pkgmgrs PKGMGR; extern enum pkgmgrs DEFAULTPKGMGR; +extern enum pkgactions PKGACTION; extern unsigned short PORTNUMBER; diff -Naur cfengine-2.1.13.orig/src/do.c cfengine-2.1.13/src/do.c --- cfengine-2.1.13.orig/src/do.c 2005-01-23 03:10:49.000000000 -0500 +++ cfengine-2.1.13/src/do.c 2005-02-23 22:14:00.000000000 -0500 @@ -2663,6 +2663,10 @@ { struct Package *ptr; int match = 0; + int i; + /* pkgmgr_none will always be the highest number in the enum so set + the array size with that */ + char *package_install_list[pkgmgr_none] = { NULL }; for (ptr = VPKG; ptr != NULL; ptr=ptr->next) { @@ -2705,6 +2709,38 @@ CfLog(cferror,OUTPUT,""); break; } + + /* Handle install/remove logic now. */ + if (match) + { + if (ptr->action == pkgaction_remove) + { + match = match; + } + } + else + { + if (ptr->action == pkgaction_install) + { + /* Initial allocation of memory if we have not yet allocated any */ + if(package_install_list[ptr->pkgmgr] == NULL) + { + package_install_list[ptr->pkgmgr] = malloc(CF_BUFSIZE); + ((char **)package_install_list[ptr->pkgmgr])[0] = NULL; + } + + /* Make sure we don't overflow the buffer */ + if(strlen(ptr->name) > + (CF_BUFSIZE - strlen(package_install_list[ptr->pkgmgr]))) + { + Verbose("Package list exceeds CF_BUFSIZE. Skipping %s", ptr->name); + } + + /* Finally add the name to the list. */ + strcat(package_install_list[ptr->pkgmgr], ptr->name); + strcat(package_install_list[ptr->pkgmgr], " "); + } + } if (match) { @@ -2719,6 +2755,19 @@ ReleaseCurrentLock(); } +/* Run through the package managers, and execute the package install + * for each of them... */ +for(i=0; i < pkgmgr_none; i++) +{ + if(package_install_list[i] != NULL) + { + Verbose("Package install list for %s is: %s\n", PKGMGRTEXT[i], + package_install_list[i]); + InstallPackage(package_install_list[i], i); + free(package_install_list[i]); + } +} + } /*******************************************************************/ diff -Naur cfengine-2.1.13.orig/src/globals.c cfengine-2.1.13/src/globals.c --- cfengine-2.1.13.orig/src/globals.c 2005-01-06 08:29:12.000000000 -0500 +++ cfengine-2.1.13/src/globals.c 2005-02-23 22:14:00.000000000 -0500 @@ -649,6 +649,15 @@ NULL }; + /*********************************************************************/ + /* The names of the possible package-related actions */ + PRIVATE char *PKGACTIONTEXT[] = + { + "install", + "remove", + NULL + }; + /*******************************************************************/ /* */ /* parse object : variables belonging to the Parse object */ @@ -802,6 +811,7 @@ PRIVATE enum cmpsense CMPSENSE = cmpsense_eq; /* Comparison for packages: */ PRIVATE enum pkgmgrs PKGMGR = pkgmgr_none; /* Which package mgr to query */ PRIVATE enum pkgmgrs DEFAULTPKGMGR = pkgmgr_none; + PRIVATE enum pkgactions PKGACTION = pkgaction_none; PRIVATE flag ACTION_IS_LINK = false; PRIVATE flag ACTION_IS_LINKCHILDREN = false; diff -Naur cfengine-2.1.13.orig/src/install.c cfengine-2.1.13/src/install.c --- cfengine-2.1.13.orig/src/install.c 2004-12-09 14:47:13.000000000 -0500 +++ cfengine-2.1.13/src/install.c 2005-02-23 22:14:00.000000000 -0500 @@ -1344,7 +1344,9 @@ break; case cfexpaft: HandleIntSwitch("expireafter",value,&PEXPIREAFTER,0,999999); break; - + case cfaction: + PKGACTION = (enum pkgactions) GetPkgAction(value); + break; default: yyerror("Illegal packages attribute"); } } @@ -2644,7 +2646,7 @@ break; case packages: - InstallPackagesItem(CURRENTOBJECT,PKGVER,CMPSENSE,PKGMGR); + InstallPackagesItem(CURRENTOBJECT,PKGVER,CMPSENSE,PKGMGR,PKGACTION); break; } @@ -4646,7 +4648,7 @@ /*******************************************************************/ -void InstallPackagesItem(char *name,char *ver,enum cmpsense sense,enum pkgmgrs mgr) +void InstallPackagesItem(char *name,char *ver,enum cmpsense sense,enum pkgmgrs mgr,enum pkgactions action) { struct Package *ptr; char buffer[CF_EXPANDSIZE]; @@ -4667,8 +4669,8 @@ return; } -Debug1("InstallPackagesItem(%s,%s,%s,%s)\n", - name,ver,CMPSENSETEXT[sense],PKGMGRTEXT[mgr]); +Debug1("InstallPackagesItem(%s,%s,%s,%s,%s)\n", + name,ver,CMPSENSETEXT[sense],PKGMGRTEXT[mgr],PKGACTIONTEXT[action]); if ((ptr = (struct Package *)malloc(sizeof(struct Package))) == NULL) { @@ -4738,6 +4740,7 @@ ptr->inform = INFORMP; ptr->cmp = sense; ptr->pkgmgr = mgr; +ptr->action = action; ptr->done = 'n'; ptr->scope = strdup(CONTEXTID); @@ -4783,6 +4786,23 @@ /*******************************************************************/ +int GetPkgAction(char *pkgaction) + +{ int i; +for (i = 0; PKGACTIONTEXT[i] != '\0'; i++) + { + if (strcmp(pkgaction,PKGACTIONTEXT[i]) == 0) + { + return i; + } + } + +yyerror("Unknown package action"); +return (int) pkgaction_none; +} + +/*******************************************************************/ + void InstallImageItem(char *cf_findertype,char *path,mode_t plus,mode_t minus,char *destination,char *action,char *uidnames,char *gidnames,int size,char comp,int rec,char type,char lntype,char *server) { struct Image *ptr; diff -Naur cfengine-2.1.13.orig/src/package.c cfengine-2.1.13/src/package.c --- cfengine-2.1.13.orig/src/package.c 2004-10-10 03:42:51.000000000 -0400 +++ cfengine-2.1.13/src/package.c 2005-02-24 22:48:28.000000000 -0500 @@ -265,6 +265,105 @@ return 0; } +int InstallPackage(char *name, enum pkgmgrs pkgmgr) +{ + char rawinstcmd[CF_BUFSIZE]; + /* Make the instcmd twice the normal buffer size since the package list + limit is CF_BUFSIZE so this can obviously get larger! */ + char instcmd[CF_BUFSIZE*2]; + char line[CF_BUFSIZE]; + char *percent; + char *ptr; + FILE *pp; + + /* Determine the command to use for the install. */ + switch(pkgmgr) + { + /* RPM */ + case pkgmgr_rpm: + if (!GetMacroValue(CONTEXTID,"RPMInstallCommand")) + { + Verbose("RPMInstallCommand NOT Set. Package Installation Not Possible!\n"); + return 0; + } + strncpy(rawinstcmd, GetMacroValue(CONTEXTID,"RPMInstallCommand"), + CF_BUFSIZE); + break; + + /* Debian */ + case pkgmgr_dpkg: + if (!GetMacroValue(CONTEXTID,"DPKGInstallCommand")) + { + Verbose("DPKGInstallCommand NOT Set. Package Installation Not Possible!\n"); + return 0; + } + strncpy(rawinstcmd, GetMacroValue(CONTEXTID,"DPKGInstallCommand"), + CF_BUFSIZE); + break; + + /* Solaris */ + case pkgmgr_sun: + if (!GetMacroValue(CONTEXTID,"SUNInstallCommand")) + { + Verbose("SUNInstallCommand NOT Set. Package Installation Not Possible!\n"); + return 0; + } + strncpy(rawinstcmd, GetMacroValue(CONTEXTID,"SUNInstallCommand"), + CF_BUFSIZE); + break; + + /* Default */ + default: + Verbose("InstallPackage(): Unknown package manager %d\n", + pkgmgr); + break; + } + + /* Common to all pkg managers */ + + /* This could probably be a bit more complete, but I don't think + that anyone would want to expand the package name more than + once in a single command invocation anyhow. */ + if (percent = strstr(rawinstcmd, "%s")) + { + *percent = '\0'; + strncpy(instcmd, rawinstcmd, CF_BUFSIZE*2); + ptr = instcmd + strlen(rawinstcmd); + *percent = '%'; + strcat(ptr, name); + ptr += strlen(name); + percent += 2; + strncpy(ptr, percent, (CF_BUFSIZE*2 - (ptr-instcmd))); + } + else + { + sprintf(instcmd, "%s %s", rawinstcmd, name); + } + Verbose("Installing package(s) %s using %s\n", name, instcmd); + if ((pp = cfpopen(instcmd, "r")) == NULL) + { + Verbose("Could not execute package install command\n"); + /* Return that the package is still not installed */ + return 0; + } + while (!feof(pp)) + { + ReadLine(line,CF_BUFSIZE-1,pp); + printf("%s:package install: %s\n",VPREFIX,line); + } + if (cfpclose(pp) != 0) + { + Verbose("Package install command was not successful\n"); + return 0; + } + return 1; +} + +int RemovePackage(char *name, enum pkgmgrs pkgmgr) +{ + Verbose("Package removal not yet implemented"); + return 1; +} /*********************************************************************/ /* Debian */ diff -Naur cfengine-2.1.13.orig/src/parse.c cfengine-2.1.13/src/parse.c --- cfengine-2.1.13.orig/src/parse.c 2004-10-26 10:20:41.000000000 -0400 +++ cfengine-2.1.13/src/parse.c 2005-02-23 22:14:00.000000000 -0500 @@ -1248,6 +1248,7 @@ VTIMEOUT=0; PKGMGR = DEFAULTPKGMGR; /* pkgmgr_none */ + PKGACTION = pkgaction_none; CMPSENSE = cmpsense_eq; PKGVER[0] = '\0'; diff -Naur cfengine-2.1.13.orig/src/prototypes.h cfengine-2.1.13/src/prototypes.h --- cfengine-2.1.13.orig/src/prototypes.h 2005-01-06 06:46:50.000000000 -0500 +++ cfengine-2.1.13/src/prototypes.h 2005-02-24 15:52:12.000000000 -0500 @@ -498,9 +498,10 @@ void InstallImageItem ARGLIST((char *cf_findertype, char *path, mode_t plus, mode_t minus, char *destination, char *action, char *uidnames, char *gidnames, int size, char comp, int rec, char type, char lntype, char *server)); void InstallMethod ARGLIST((char *function, char *file)); void InstallAuthItem ARGLIST((char *path, char *attribute, struct Auth **list, struct Auth **listtop, char *classes)); -void InstallPackagesItem ARGLIST((char *name, char *ver, enum cmpsense sense, enum pkgmgrs mgr)); +void InstallPackagesItem ARGLIST((char *name, char *ver, enum cmpsense sense, enum pkgmgrs mgr, enum pkgactions action)); int GetCmpSense ARGLIST((char *sense)); int GetPkgMgr ARGLIST((char *mgr)); +int GetPkgAction ARGLIST((char *pkgaction)); int GetCommAttribute ARGLIST((char *s)); void HandleRecurse ARGLIST((char *value)); void HandleCopyType ARGLIST((char *value)); @@ -852,6 +853,8 @@ int RPMPackageCheck ARGLIST((char *package, char *version, enum cmpsense cmp)); int DPKGPackageCheck ARGLIST((char *package, char *version, enum cmpsense cmp)); int SUNPackageCheck ARGLIST((char *package, char *version, enum cmpsense cmp)); +int InstallPackage ARGLIST((char *name, enum pkgmgrs pkgmgr)); +int RemovePackage ARGLIST((char *name, enum pkgmgrs pkgmgr)); /* popen.c */