An example patch submission
The community I submitted a patch to was cfengine, a system-administratration tool. I showed the page that shows their mailing lists and developer page.
Patch overview
The patch I wrote adds Gentoo Portage package support to cfengine, so that I could use the native package management support in cfengine on Gentoo.
I showed how you can use the diff command compare two files or folders. Use diff -ru directory1 directory2 to generate a `unified' (with context), recursive diff for two folders. For two files, drop the `r'.
Patch diff
diff -ru cfengine-2.1.20.orig/src/cf.defs.h cfengine-2.1.20/src/cf.defs.h
--- cfengine-2.1.20.orig/src/cf.defs.h 2006-03-29 13:11:29.000000000 +0000
+++ cfengine-2.1.20/src/cf.defs.h 2006-08-31 22:38:55.000000000 +0000
@@ -1191,6 +1191,7 @@
pkgmgr_rpm,
pkgmgr_dpkg,
pkgmgr_sun,
+ pkgmgr_portage,
pkgmgr_none
};
diff -ru cfengine-2.1.20.orig/src/do.c cfengine-2.1.20/src/do.c
--- cfengine-2.1.20.orig/src/do.c 2006-03-02 07:33:45.000000000 +0000
+++ cfengine-2.1.20/src/do.c 2006-08-31 22:39:30.000000000 +0000
@@ -2721,6 +2721,9 @@
case pkgmgr_sun:
match = SUNPackageCheck(ptr->name, ptr->ver, ptr->cmp);
break;
+ case pkgmgr_portage:
+ match = PortagePackageCheck(ptr->name, ptr->ver, ptr->cmp);
+ break;
default:
/* UGH! This should *never* happen. GetPkgMgr() and
* InstallPackagesItem() should have caught this before it
diff -ru cfengine-2.1.20.orig/src/globals.c cfengine-2.1.20/src/globals.c
--- cfengine-2.1.20.orig/src/globals.c 2006-03-29 13:11:29.000000000 +0000
+++ cfengine-2.1.20/src/globals.c 2006-08-31 22:39:55.000000000 +0000
@@ -656,6 +656,7 @@
"rpm",
"dpkg", /* aptget ? */
"sun", /* pkginfo/pkgadd/pkgrm */
+ "portage",
NULL
};
diff -ru cfengine-2.1.20.orig/src/package.c cfengine-2.1.20/src/package.c
--- cfengine-2.1.20.orig/src/package.c 2006-03-29 13:11:29.000000000 +0000
+++ cfengine-2.1.20/src/package.c 2006-08-31 22:41:11.000000000 +0000
@@ -327,6 +327,18 @@
CF_BUFSIZE);
break;
+ /* Portage */
+ case pkgmgr_portage:
+
+ if (!GetMacroValue(CONTEXTID,"PortageInstallCommand"))
+ {
+ Verbose("PortageInstallCommand NOT Set. Package Installation Not Possible!\n");
+ return 0;
+ }
+ strncpy(rawinstcmd, GetMacroValue(CONTEXTID,"PortageInstallCommand"),
+ CF_BUFSIZE);
+ break;
+
/* Default */
default:
Verbose("InstallPackage(): Unknown package manager %d\n",pkgmgr);
@@ -816,6 +828,197 @@
free(tmpcpy);
}
+/*********************************************************************/
+/* Gentoo Portage */
+/*********************************************************************/
+
+int PortagePackageCheck(char *package,char *version,enum cmpsense cmp)
+
+{ FILE *pp;
+ struct Item *ebuildlist = NULL;
+ struct Item *ebuild;
+ enum cmpsense result;
+ int match = 0;
+
+ /* Yes, it's an ugly python one-liner that does something beautiful */
+ snprintf(VBUFF,CF_BUFSIZE,"/usr/bin/python -c 'import portage, re, sys; "
+ "[sys.stdout.write(re.sub(\"\\w*?\\-\\w*?\\/([a-zA-Z0-9_+]|\\-(?![0-9]))*\\-\", \"\", package, 1) + "
+ "\"\\n\") for package in portage.db[\"/\"][\"vartree\"].dbapi.match("
+ "\"%s\")]'", package);
+
+ if ((pp = cfpopen(VBUFF, "r")) == NULL)
+ {
+ Verbose("Could not execute the equery command. Assuming package not installed.\n");
+ return 0;
+ }
+
+ while(!feof(pp))
+ {
+ *VBUFF = '\0';
+
+ ReadLine(VBUFF,CF_BUFSIZE,pp);
+
+ if (*VBUFF != '\0')
+ {
+ AppendItem(&ebuildlist,VBUFF,"");
+ }
+ }
+
+ /* Non-zero exit status means that we could not find the package, so
+ * zero the list and bail. */
+
+ if (cfpclose(pp) != 0)
+ {
+ DeleteItemList(ebuildlist);
+ ebuildlist = NULL;
+ }
+
+ if (ebuildlist == NULL)
+ {
+ Verbose("Package %s not installed.\n", package);
+ return 0;
+ }
+
+
+ Verbose("PortageCheckPackage(): Requested %s %s %s\n", package, CMPSENSETEXT[cmp],(version[0] ? version : "ANY"));
+
+ /* If no version was specified, just return 1, because if we got this far
+ * some package by that name exists. */
+
+ if (!version[0])
+ {
+ DeleteItemList(ebuildlist);
+ return 1;
+ }
+
+ /* The rule here will be: if any package in the list matches, then the
+ * first one to match wins, and we bail out. */
+
+ char *comparehead = NULL;
+ char *comparetail = NULL;
+ char *installedhead = NULL;
+ char *installedtail = NULL;
+
+ for (ebuild = ebuildlist; ebuild != NULL; ebuild=ebuild->next)
+ {
+ char *ebuildver;
+ ebuildver = ebuild->name;
+
+ Verbose("PortageCheckPackage(): Trying installed version %s\n", ebuildver);
+
+ /* Start out assuming the comparison will be equal. */
+ result = cmpsense_eq;
+
+ comparehead = version;
+ comparetail = NULL;
+ installedhead = ebuildver;
+ installedtail = NULL;
+
+ /* Iterate over version portions delimited by `-' */
+ while (result == cmpsense_eq)
+ {
+ if (*comparehead == '\0' && *installedhead == '\0')
+ {
+ /* No substrings remain, break from while */
+ break;
+ }
+ else if (*comparehead == '\0')
+ {
+ /* Installed version has more version substrings than given */
+ result = cmpsense_gt;
+ }
+ else if (*installedhead == '\0')
+ {
+ /* Installed version has less version substrings than given */
+ result = cmpsense_lt;
+ }
+ else
+ {
+ /* New substring in both to test */
+ comparetail = strchr(comparehead, '-');
+ installedtail = strchr(installedhead, '-');
+
+ /* Throw a \0 over the `-' so just the substring is tested.
+ * If the tail is less than the head, we must be at last substring,
+ * as no `-'s were found (so the `\0' is already there) */
+ if (comparetail > comparehead) *comparetail = '\0';
+ if (installedtail > installedhead) *installedtail = '\0';
+
+ switch (rpmvercmp(installedhead, comparehead))
+ {
+ case 1:
+ result = cmpsense_gt;
+ break;
+ case -1:
+ result = cmpsense_lt;
+ break;
+ }
+
+ if (comparetail > comparehead)
+ {
+ /* Restore `-' at tail and move head just past it */
+ *comparetail = '-';
+ comparehead = comparetail + 1;
+ }
+ else
+ {
+ /* Move head to the end of the line (`\0') */
+ comparehead = comparehead + strlen(comparehead);
+ }
+ if (installedtail > installedhead)
+ {
+ /* Restore `-' at tail and move head just past it */
+ *installedtail = '-';
+ installedhead = installedtail + 1;
+ }
+ else
+ {
+ /* Move head to the end of the line (`\0') */
+ installedhead = installedhead + strlen(installedhead);
+ }
+ }
+ }
+
+ Verbose("Comparison result: %s\n",CMPSENSETEXT[result]);
+
+ switch(cmp)
+ {
+ case cmpsense_gt:
+ match = (result == cmpsense_gt);
+ break;
+ case cmpsense_ge:
+ match = (result == cmpsense_gt || result == cmpsense_eq);
+ break;
+ case cmpsense_lt:
+ match = (result == cmpsense_lt);
+ break;
+ case cmpsense_le:
+ match = (result == cmpsense_lt || result == cmpsense_eq);
+ break;
+ case cmpsense_eq:
+ match = (result == cmpsense_eq);
+ break;
+ case cmpsense_ne:
+ match = (result != cmpsense_eq);
+ break;
+ }
+
+ /* If we find a match, just return it now, and don't bother checking
+ * other ebuilds */
+
+ if (match)
+ {
+ DeleteItemList(ebuildlist);
+ return 1;
+ }
+ }
+
+ /* if we manage to make it out of the loop, we did not find a match. */
+
+ DeleteItemList(ebuildlist);
+ return 0;
+
+}
/*********************************************************************/
/* RPM Version string comparison logic
diff -ru cfengine-2.1.20.orig/src/prototypes.h cfengine-2.1.20/src/prototypes.h
--- cfengine-2.1.20.orig/src/prototypes.h 2006-03-11 13:53:03.000000000 +0000
+++ cfengine-2.1.20/src/prototypes.h 2006-08-31 22:41:37.000000000 +0000
@@ -847,6 +847,7 @@
int RPMPackageCheck (char *package, char *version, enum cmpsense cmp);
int DPKGPackageCheck (char *package, char *version, enum cmpsense cmp);
int SUNPackageCheck (char *package, char *version, enum cmpsense cmp);
+int PortagePackageCheck (char *package, char *version, enum cmpsense cmp);
int InstallPackage (char *name, enum pkgmgrs pkgmgr);
int RemovePackage (char *name, enum pkgmgrs pkgmgr);
Email submission
I sent the patch to the developers' mailing list:
To: bug-cfengine@cfengine.org From: Eric Searcy <emsearcy@osuosl.org> Subject: packages patch for Gentoo Portage support Date: Tue, 16 Jan 2007 19:47:48 -0800 Hello, I've written a patch (attached) for cfengine that adds support to the `packages:' action for the Gentoo Linux [1] Portage [2] package management system. I have been using cfengine 2.1.20 with this patch for the last several months on a network of several dozen production hosts. The same unified diff also correctly applies to the 2.2.21 release. (I would test against the latest SVN revision, but I can't seem to connect at the moment, and neither can viewcvs on the site. It is probably broken by the recently-added AIX support.) I based it very closely off the RPMPackageCheck code, and it reuses the rpmvercmp function. To use, the DefaultPkgMgr/pkgmgr should be given the value `portage', and the function expects (and checks for) the macro PortageInstallCommand, which should be set to ``/usr/bin/emerge -- nocolor'' or similar. Setting cmp and version, as well as setting neither (existence check only) is supported. Please comment on whether this patch could be incorporated into the trunk. If you would like any clarifications on the design of the PortagePackageCheck function or any other changes I made, please ask. One quick note is that it makes an exec call to a python one- liner to grab package information. Portage is a python-based system with a python API, and since python is required by portage, there shouldn't be any dependancy concerns. [1] http://www.gentoo.org/ [2] http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=1 -- Eric Searcy OSU Open Source Lab
I received the reply:
Date: Wed, 17 Jan 2007 10:06:29 +0100 From: Mark Burgess <Mark.Burgess@iu.hio.no> To: Eric Searcy <emsearcy@osuosl.org> CC: bug-cfengine@cfengine.org Subject: Re: packages patch for Gentoo Portage support THanks very much Eric. I would certainly like to include this, once I have looked over the code briefly. The svn server is very flakey and the database needs to be recovered at regular intervals...sigh. It should be working again now. Eric Searcy wrote:
<trim>
and:
Date: Tue, 23 Jan 2007 16:36:09 +0100 From: Mark Burgess <Mark.Burgess@iu.hio.no> To: Eric Searcy <emsearcy@osuosl.org> Subject: Re: packages patch for Gentoo Portage support Thank you -- patches applied.
<trim>
Overview
Submitting a patch is not a very difficult process--depending on the community. The packages portion of cfengine is all contributer-based; the developer Mark Burgess doesn't hold the same ownership, so it is easier to contribute to that portion of the code. The key is having the patch to submit (this can be anything from spelling corrections, to feature additions, to bug-fixes), and to know which channel the open-source community expects you to submit your patch in (developer mailing list or bug tracker ticket).